Open robertherber opened 1 year ago
How does one use enableBackgroundDelivery today?, because i am seeing that in apples docs, they have something called completion handler, is that needed to do it ?
@mafen It's available in this library already (and seems to work allright) - but according to the Apple docs it's recommended to call it earlier in the lifecycle (in didFinishLaunchingWithOptions, i.e. before any JS code is initialized.
Any examples on the part that is in already? Like i want to use enableBackgroundDelivery in it's current from, and i have tried
HealthKit.enableBackgroundDelivery(HKQuantityTypeIdentifier.activeEnergyBurned, HKUpdateFrequency.immediate)
But where do I get the result of this, what calls my method?
I would love an example of how to use this function
@mafen The completion handler is solely indicating success or failure in enabling background delivery - not for getting the data itself (and in react-native-healthkit it's returned as a promise instead of a callback - to improve the developer experience).
I think the methods you use to fetch/subscribe to data are the same as without background delivery.
I have gotten it working with subscribe to data but only when press the simulate background fetch in XCode and it is very flaky on a real ios device, could only get it work well in the simulator but not after terminating the app.
@mafen In the Apple Docs about Background Delivery it states that most types of data can only be subscribed on to an hourly basis at most, I guess that might be what you’re experiencing?
I am subscribeing to active energy burned and that is supposed to be on an immediate frequently
@mafen Here it's not listed as one of the immediates.
@mafen Here it's not listed as one of the immediates.
I am maybe wrong, but I thought that was only for watch OS
@ha3 I started looking at implementing a config plugin for this. However when reading the docs again I realised it's not actually recommending to call the enableBackgroundDelivery
in didFinishLaunchingWithOptions
, but rather to set up all observer queries there:
As soon as your app launches, HealthKit calls the update handler for any observer queries that match the newly saved data. If you plan on supporting background delivery, set up all your observer queries in your app delegate’s application(_:didFinishLaunchingWithOptions:) method. By setting up the queries in application(_:didFinishLaunchingWithOptions:), you ensure that you’ve instantiated your queries, and they’re ready to use before HealthKit delivers the updates.
I guess this makes more sense, and is also more difficult to achieve. What is your experience with this - do you miss out on background updates? Are you using observer queries or maybe some other method to fetch the data?
@robertherber I understand that this means changing useSubscribeToChanges
implementation in general, and adding an additional native step for the installation. I agree that it can be more difficult to implement; but I'm not sure about the possible consequences of not following official advice on this.
We haven't ship HealthKit observers to production yet; since there is no way to retrieve changed data between observer calls for now (https://github.com/Kingstinct/react-native-healthkit/issues/50). So, we can't be sure if there is any miss or not.
Basically this is how we use the observers:
let isBackgroundObserversSetup = false;
const setupBackgroundObservers = () => {
if (isBackgroundObserversSetup) {
return;
}
isBackgroundObserversSetup = true;
([
HKQuantityTypeIdentifier.activeEnergyBurned,
...
] as const).forEach(permission => {
HealthKit.enableBackgroundDelivery(permission, HKUpdateFrequency.immediate);
});
};
const HealthKitProvider = ({ children }: { children: React.ReactNode }) => {
setupBackgroundObservers();
HealthKit.useSubscribeToChanges(
HKQuantityTypeIdentifier.activeEnergyBurned,
async () => {
const data = await HealthKit.getMostRecentQuantitySample(HKQuantityTypeIdentifier.activeEnergyBurned);
if (!data) {
return;
}
// do sth with data
}
);
return <>{children}</>;
};
const App = () => {
return (
<HealthKitProvider>
...
</HealthKitProvider>
);
}
@ha3 thank you for the example you posted, helped a lot to understand how to set it up
The one thing that is still not clear to me is the background delivery when the app is in the background or closed, will the useSubscribeToChanges
still fire?
For example, I'm trying to subscribe to the latest workout, I would like to send the user a notification once they completed a workout, I've created a provider like your example above but it only fires when the app is in the foreground, if the app is in a closed state I don't receive the data and therefore can't show the notification, any idea how to achieve that?
I'm trying to get new workouts, like @matteo-hertel but not having any luck. I have a provider, similar to what was referenced above but new workouts are not being picked up by the subscription. The function in the useSubscribeToChanges
is not called. The logs are stating true when enableBackgroundDelivery is called and on first load, I see SUBSCRIBE: change found
, but even with the app in the foreground, I log an activity with connected watch and nothing is triggered. Any help or pointers would be greatly appreciated.
export const AppleHealthContext = createContext(null)
let isBackgroundObserversSetup = false
const setupBackgroundObservers = async () => {
if (isBackgroundObserversSetup) return
isBackgroundObserversSetup = true
const status = await HealthKit.enableBackgroundDelivery(HKWorkoutTypeIdentifier, HKUpdateFrequency.immediate)
console.log('Enable Background Status', status)
}
export const AppleHealthProvider = ({ children }) => {
setupBackgroundObservers()
HealthKit.useSubscribeToChanges(
HKWorkoutTypeIdentifier,
async () => {
console.log('SUBSCRIBE: change found')
await update() // posts new workouts to endpoint
}
)
return (<AppleHealthContext.Provider>
{children}
</AppleHealthContext.Provider>)
}
@justingshreve I'm running into the same issue - were you able to find a solution?
I was able to get this working, but it is still a bit mysterious. I had a few providers and moved the HealthKit code to the top level provider. I also added fetch and processing UIBackgroundModes: ['remote-notification', 'fetch', 'processing'],
. Even with that, when I install new development build, testing it is spotty. Works, then does not. I did notice that if the build is installed for about a day, it does seem to become more reliable, so I'm assuming iOS is doing something similar to what is done with background tasks, and it takes some time for background queries to be engaged. It'd be great if this was more straightforward though.
I'm not sure I understand how enableBackgroundDelivery is actually working. If I subscribeToChanges
and in the callback do a console.log
, will I see that console.log even if my app isn't open? Or does this just mean that when my app opens, all those ObserverQueries will get run?
The part that is confusing comes from this part of the Apple documentation
As soon as your app launches, HealthKit calls the update handler for any observer queries that match the newly saved data.
Is that referring to the app launching in the background or when it is foregrounded again?
To comply with Apple recommendations and to prevent any race conditions that might arise from initialising it later, as is the case now.
There are two parts to this: