Imagine this scenario: you have finally turned your latest startup idea into an app and everything is working perfectly. Everything is working perfectly, except getting things like audio recording and image and data fetching to run in the background. At OK GROW! we use approaches like the ones below to overcome such hurdles in regards to running background tasks.

This post talks about apps in:

Background mode : The app is sent to the background by pressing the home button and the phone is potentially locked, but the screen is still on.

: The app is sent to the background by pressing the home button and the phone is potentially locked, but the screen is still on. Suspended mode or sleep mode: The app is in the background, the phone is locked, and the screen is turned off.

Depending on how often you have to run your background tasks, you can choose one of the following approaches.

Expo vs ExpoKit

Running tasks in the background is currently not possible in Expo without ejecting, but it’s already in progress and expected to be released in the upcoming releases from Expo. You can track the progress for background tasks and background location tracking, on their feature requests dashboard and chime in on the pull request on Github.

However, if you can’t wait for Expo’s future releases, do decide to eject your app, the following approaches are all compatible with an ejected Expo app with ExpoKit. Keep in mind that working with ExpoKit has an exciting roadmap that is going to make ejecting much easier to use.

Running background tasks every 15 minutes

If your job doesn’t need to run on a quick interval, you can follow the Easy OS Background Tasks in React Native tutorial, which has a minimum of 15 minutes interval between jobs. This would be ideal for none time-sensitive tasks. For example, this could be used in an Instagram-like app, to fetch images and videos in the background so that they open without loading when the user opens the app.

This tutorial makes use of the following packages:

Running tasks every few seconds — excluding iOS’s suspended mode

Normally, you could run setTimeout or setInterval to run a simple task every few seconds. The sad news is that setTimeout does not work in React Native’s background or suspended mode. This is true for both plain react native apps and with ExpoKit.

Thankfully, the React Native Background Timer package allows you to run setTimeout when the app is in the background. On Android, this works for both suspended mode and background mode, whereas for iOS it only works in the background, and not suspended mode. In other words, on iOS it works as long as the app is in the background, and the phone is locked, but not when the screen is turned off.

Running tasks every 60 seconds — including on iOS’s suspended mode

If you have to run background tasks on iOS’s suspended mode, there is a workaround from the same author as React Native Background Fetch. This solution makes use of React Native Background Geolocation and a combination of its onLocation and onHeartbeat functionality. This is a package designed for tracking the user’s GPS location and not specifically intended for running background tasks in the suspended mode. Keep in mind that this is a pretty big library that you’d be adding to your project, with dependencies that can potentially conflict with your existing stack. So be cautious and consider if the added complexity is worth the gain.

Heartbeat only works while the phone is stationary, therefore you’d have to use it in combination with onLocation to get it to work all the time. This is obviously a tricky workaround that would require some trial and error to get it to work perfectly for your app. The minimum interval between heartbeats is 60 seconds, and at the time of writing this blog post, I have not found any other way that can enable a quicker interval for running tasks for iOS.

Bonus: Getting React Native Audio to work in the suspended mode

Android is more flexible with doing things in the background and suspended mode, however, iOS only allows you to do certain things in those modes. You can see a full list of what’s possible in Xcode by visiting the capabilities tab under your project’s target like below:

Although tasks like recording and playing audio, are possible by iOS, that doesn’t necessarily mean that they’re solved in the React Native packages that you are using for those means. In fact, audio is one of the workarounds, that some people use to keep their apps alive in the background. I highly discourage such hacky solutions, especially because they may break Apple’s policies.

However, even if you use the most popular library for recording audio, React Native Audio, you may find that it does record audio, but the inProgress event does not get fired in suspended mode, while startRecording , onFinished , stopRecording and other APIs work fine.

One non-ideal solution in situations like this is to follow the workaround above and use a combination of onLocationand onHeartbeat , along with Mitt, a tiny pure javascript library that allows you to emit arbitrary events. Using mitt, you can force events like react-native-audio 's onProgress to fire as simple as below:

const emitter = mitt(); BackgroundGeolocation.on('heartbeat', async () => {

emitter.emit('onProgressWorkaround');

}); BackgroundGeolocation.on('location', async () => {

emitter.emit('onProgressWorkaround');

});

This will call your new event, replacing the normal onProgress every time that the location or heartbeat events get fired. Keep in mind that above implies that you'd have to calculate the onProgress payload yourself. But since you know the start time of your audio recording (as you trigger the start), you can easily calculate the time difference like so:

Before using mitt:

AudioRecorder.onProgress = (data) => {

const currentTime = data.currentTime;

};

After adding mitt:

cont startRecordingTimestamp = new Date();

await AudioRecorder.startRecording();

emitter.on('onProgressWorkaround', async () => {

const now = new Date();

const currentTime = (now - lastClipTimestamp) / 1000;

});

This concludes the approaches you can take to run tasks in the background. If you have other suggestions and solutions, please comment below and let us know!