I have a React Native app that needs to fetch data and play a sound every 15 minutes, regardless of whether the app is in the background or foreground. However, I am experiencing an issue: when the app goes into the background, it doesn’t play the sound after 15 minutes. The sound only plays when I reopen the app.
Here is the code I am using:
import React, {useState, useEffect, useCallback} from 'react';
import {
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
FlatList,
StatusBar,
ListRenderItem,
} from 'react-native';
import {Header, Colors} from 'react-native/Libraries/NewAppScreen';
import BackgroundFetch from 'react-native-background-fetch';
import {Button} from '@rneui/base';
import Sound from 'react-native-sound';
// Define the type for event
interface Event {
taskId: string;
timestamp: string;
}
const getTime = () => {
let currentDate = new Date();
// Get the current hours, minutes, and seconds
let hours = currentDate.getHours();
let minutes = currentDate.getMinutes();
let seconds = currentDate.getSeconds();
// Format the time as HH:MM:SS
let currentTime = `${hours.toString().padStart(2, '0')}:${minutes
.toString()
.padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
return currentTime;
};
const App: React.FC = () => {
const [events, setEvents] = useState<Event[]>([]);
const [data, setData] = useState<any>(getTime());
useEffect(() => {
initBackgroundFetch();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const addEvent = useCallback(async (taskId: string): Promise<void> => {
console.log('Hello from a background task', getTime());
setEvents(prevEvents => [
...prevEvents,
{
taskId: taskId,
timestamp: getTime(),
},
]);
}, []);
const initBackgroundFetch = async () => {
const onEvent = async (taskId: string) => {
console.log('started task time', getTime());
await addEvent(taskId);
BackgroundFetch.finish(taskId);
console.log('finished task time', getTime());
triggerBackgroundFetch();
};
const onTimeout = async (taskId: string) => {
console.warn('[BackgroundFetch] TIMEOUT task: ', taskId);
BackgroundFetch.finish(taskId);
};
let status = await BackgroundFetch.configure(
{
minimumFetchInterval: 15, // <-- minutes (15 is minimum allowed)
forceAlarmManager: false, // Set true to bypass JobScheduler.
stopOnTerminate: false,
startOnBoot: true,
},
onEvent,
onTimeout,
);
console.log('[BackgroundFetch] configure status: ', status);
};
const renderItem: ListRenderItem<Event> = ({item}) => (
<Text>
[{item.taskId}]: {item.timestamp}
</Text>
);
const triggerBackgroundFetch = async () => {
setData(Math.random() * 50);
var whoosh = new Sound('sound.mp3', Sound.MAIN_BUNDLE, error => {
if (error) {
console.log('failed to load the sound', error);
return;
}
// Play the sound with an onEnd callback
whoosh.play(success => {
if (success) {
console.log('successfully finished playing');
} else {
console.log('playback failed due to audio decoding errors');
}
});
});
};
return (
<>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={styles.scrollView}>
<Header />
<View style={styles.body}>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>BackgroundFetch Demo</Text>
<View>
<Text>random:{data}</Text>
</View>
</View>
</View>
<Button onPress={triggerBackgroundFetch}>Call API</Button>
</ScrollView>
<View style={styles.sectionContainer}>
<FlatList
data={events}
renderItem={renderItem}
keyExtractor={item => item.timestamp}
/>
</View>
</SafeAreaView>
</>
);
};
const styles = StyleSheet.create({
scrollView: {
backgroundColor: Colors.lighter,
},
body: {
backgroundColor: Colors.white,
},
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
color: Colors.black,
},
sectionDescription: {
marginTop: 8,
fontSize: 18,
fontWeight: '400',
color: Colors.dark,
},
});
export default App;