Background Fetch & Task Manager - eyecuelab/walkertreker GitHub Wiki
Note: BackgroundFetch and TaskManager is working across all SDK versions, but at the time of writing this, the Pedometer API is non-functioning on Android phones. Within the
stuart-bgFetch
branch, Expo is run on SDK 33. This was done to allow for further development which was not stable enough to keep. Further discussion is below.
BackgroundFetch and TaskManager
TaskManager.defineTask()
These two modules are very closely linked in Expo, with the BackgroundFetch module running TaskManager Native API under the hood. The way the logic is structured is that a background task is defined with TaskManager.defineTask(taskName, () => {
try {
const receivedNewData = // do your background fetch here
return receivedNewData ? BackgroundFetch.Result.NewData : BackgroundFetch.Result.NoData;
} catch (error) {
return BackgroundFetch.Result.Failed;
}
});
This is the task that is called outside of the App in the background. As per the Expo documentation, "It must be called in the global scope of your JavaScript bundle. In particular, it cannot be called in any of React life-cycle methods like .componentDidMount(). This limitation is due to the fact that when the application is launched in the background, we need to spin up your JavaScript app, run your task and then shut down — no views are mounted in this scenario." It is run as a Headless JavaScript background process on the phone system itself and therefor does not have access to the whole application and must have complete and self-sufficient logic to complete the task.
BackgroundFetch.registerTaskAsync()
Once the task has been defined, it must be registered with the system to appropriately hand it off as a background process. This is done by calling BackgroundFetch.registerTaskAsync(taskName, {
minimumInterval: 60,
stopOnTerminate: false,
startOnBoot: true
}).then(() => BackgroundFetch.setMinimumIntervalAsync(60));
The object that is registered with the background task is used for Android settings. minimumInterval defaults to an average of every 10-15 minutes, stopOnTerminate defaults to true, and startOnBoot defaults to false so these must be changed to start and then keep the process continuously running in the background. After the task is registered, .setMinimumIntervalAsync() must be called or there will be problems with iOS systems.
Next Steps
Because the background task is run independently of the rest of the app, the preexisting logic is useful as a reference, but not usable in its current form. A background process to fetch and update a user's step count is integral to the smooth functionality of this app. Currently, step counts are only updated when the app is foreground on the user's phone at an interval of every 60 seconds. Random events were implemented to create user engagement with the application so that it would be opened at least once a day to update steps. All logic is contained in the BackgroundPedometer.js
and Sagas.js
and as it is now, even if someone were to walk 10 miles in a day, but never opened the Walkertreker app, they would receive no credit for the day.
-
Sagas were attempted to be called through
await store.dispatch({ type: GET_STEPS })
inside the background task and it would trigger the backgroundpedometer logic, but as it was a component based file, the step count by day logic would break on the first day and fail to loop through the rest of the date objects (n = # of days in the campaign). (The existing logic is based off a component structure with componentDidMount() etc. as well as definingconst { dispatch } = this.props
as there are no props accessible to independent functions outside of a component.) -
The Pedometer API can be called from Expo in the background task but will always return a value of 0. The one time it returned something other than 0, the async fetch was triggered while in the background but the app was foregrounded in the middle of the call and then it returned the accurate step count. (Is it a permissions issue?)
(async () => { let allKeys = await AsyncStorage.getAllKeys() console.log("allKeys: ", allKeys) })();
-
AsyncStorage is used to store a local state copy along with other keys. These can be viewed by uncommenting the above lines in the
stuart-bgFetch
branch or copy and pasting them into the top of the document inApp.js
. (Would these be able to be modified or overwritten and reinserted back into AsyncStorage? This could be used to trigger a reverse data flow to the server for the desired value updates. Alternatively, the lastState object key could be used as sudo-props and values parsed to keep the existingBackgroundPedometer.js
logic as similar as possible while tweaked to work as a headless task.)