I'm developing a react-native app, and we noticed something strange. When the app comes back from suspended state (it doesn't happen from normal background state I think), often, the fetch freezes afterwards.
To trigger it, put on battery saver (I think it will make the app go to suspended state a lot quicker), then put the app in the background, do some other stuff on your phone for a minute or 5-10, open the app again and use it.
In our case we navigated to an other screen which tried to fetch three lists of objects at the same time from the backend. In the backend, we noticed only one call coming through, and the Promise.all(...) on the three calls never gets resolved or catched. So there is no error either. Afterwards all calls work again and the problem seems to be gone. So it's a one time issue.
This never happens when not coming from suspended state.
This is extremely hard to debug, since when the app goes to suspended state, the debugger is disconnected.
We haven't tested this yet on Android, it could be that the problem exists there as well.
My gut feeling tells me, it has something to so with the internal networking of IOS or the fetch library of RN. (e.g. when reconnecting to wifi, coming from 4G or something)
Has someone experienced the same problem already or has more insights on why this is happening or how to solve this? (e.g. use a time-out and retry mechanism, which I would like to prevent, force a api call on state change to active - although react-native can't detect the suspended state)
Thanks in advance!
EDIT:
Forgot to add: we're using react-native 54, but are in the process of updating to 57. I'll report back if the issue is still happening on 57...
EDIT 2:
Atm, we solved it by doing to the calls after each other instead of the at the same time (so fetch().then(fetch().then(fetch())) which seems to resolve the issue. So yet again, doing the calls at the same time, never fails normally, only after a suspended state of the app.
I can't seem to find the right documentation, tutorial or SO post to point me towards the right implementation so far... even though there are many posts about this topic floating around.
Problem
I have an app that connects (pairs) to a BT device I built. What I want, is when the user presses the home button or backgrounds the app I want to send an update through a BT service layer to a BT device on a regular interval. I am doing this now (only fires once) in the applicationWillResignActive and applicationDidEnterBackground methods and it works perfectly. The problem is that I can only perform this once it seems. Any loops, timers, background type services that I start up in these methods, die very soon there after.
What I need
Is for the application to keep looking for my BT device in the background and every 10 seconds or so and send an update to the device. I first need to check to make sure the app is still backgrounded, but once I know its in the background I wanna send the update. When the app comes back in focus I can stop the updates to the BT device but its crucial that it sends them when the app is not in focus.
Research
I have found these SO posts (long-running tasks, background task execution handler, using this in an NSTimer loop... but it dies) but they have not helped with implementation at all.
When I wrote this post I was far too new to OBJ-C and didn't quite understand the concept of delegate methods with the proper access level to fire in the background (i.e. info.plist access under bluetooth-central).
The end result a month later was to build a peripheral device that could wake up the application in a timed loop via subscribed characteristic updates. Once the app wakes up, you have 10 seconds to handle the event that has just occurred and since I am only saving off a copy of the data that the device posted, all is well.
After rebooting my phone, my app stops getting CoreLocation delegate callbacks, is it an expected behavior? I think I remember reading that you could still get callbacks. If so, is there anywhere in the documentation saying if its possible or not?
This is not normal. An app is supposed to get callbacks to CoreLocation's methods after your phone reboots, and I have built this feature into multiple apps and verified it works.
Three things make this hard to test, so be sure you are taking these into account:
After an iOS device reboots, it does not perform bluetooth scans to look for iBeacons right away. I do not know the exact timing, but it may take a minute or more.
Even after the above time passes, scans for iBeacons are not happening continually when your app is not ranging for iBeacons in the foreground. Make sure you wait up to 15 minutes (16 minutes after reboot) before you declare that you aren't getting background notifications.
In a typical setup, you won't see your logging statements after a reboot. So it is quite possible you are getting callbacks but there is nothing visible happening as a result. Make sure you do something in your callback for testing, like an unconditional local notification, so you know for sure whether you are getting the callbacks or not.
1) My plist configuration to provide backgroundmode:
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
</array>
2) In didFinishLaunchingWithOptions I have:
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:1.0];
3) I declared the protocol UIApplicationDelegate in the delegate.
4) I implemented the following method, but it never gets fired. (It only works if I simulate the fetch with "XCode->Debug->Simulate Background Fetch".)
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
Why? Is this a DP5 beta error? Should I radar this?
Running your app in the iOS Simulator, in Xcode Debug mode, you can force a background fetch from the Xcode menu:
Debug > Simulate Background Fetch
May work for a tethered device, I haven't tried it recently.
I'm afraid this is hard to debug on a device because you're not guaranteed it is called in the amount of time you specify.
setMinimumBackgroundFetchInterval means that it is not called in an interval which is smaller than the value you specified. But there's no setMaximumBackgroundFetchInterval.
So if iOS decides to call your app just once a day or even just once a week than it won't be called more often regardless your minimumBackgroundFetchInterval. AFAIK iOS decides when to call performFetchWithCompletionHandler measured by the pattern when and how often the users start the app.
There are many considerations:
Make sure the background fetch capability was set in the plist.
Make sure the background fetch capability hasn't been disabled for this particular app, or in general, in the device's Settings app.
Make sure to set the minimum fetch interval.
Make sure you gracefully leave the app (e.g. just hit the home button and fire up another app and/or just lock the device). But if you kill the app (by “force quitting” by double tapping on the home button and swiping up or, for those devices without home button, swiping up from the bottom to pull up the task manager and then swiping up on the app in question) that will prevent the OS from offering your app a chance to fire off subsequent background fetch requests (at least until the user runs the app again).
Make sure you are testing this on physical device and not running the app via the Xcode debugger. Being attached to the debugger changes the behavior of background operations.
Make sure the app is actually doing some network requests. If you have app that performs no network requests at all, it won't participate in background fetch. If you do, for example, a little test app with "background fetch" and don't issue any network requests, you won't participate in background fetch.
Likewise, if the OS starts up your app in background mode so it can perform a background fetch, if you don't actually perform a network request, the OS may stop offering your app the ability to perform background fetches in the future.
Make sure to call the completion handler, and do so within the allotted time, or your app may not participate in background fetch in the future.
The timing of when the OS performs background fetch is dictated by poorly documented rules that may change in the future. But the relevant factors include:
Whether the device is connected to power and/or is sufficiently charged;
Whether connected to WiFi or not;
How often the user actually fires up the app;
Whether the device is doing other network related tasks (e.g. whether background fetch can be coalesced with other network operations);
How frequently past background fetch requests resulted in there being data available.
In my experience, after the app is run the first time, if connected to wifi and power, if you wake the device about 5 minutes later, the app will perform background fetch. This isn't a hard and fast rule, but just what we've experienced in the past.
But many new developers post on Stack Overflow with questions like “how can I have app request data ever x minutes (or hours)”, “how can I request data every day at 2 am time”, etc. The short answer is that you can't. The OS decides the timing of background at its own discretion. You cannot control this (other than the minimum request interval; but you cannot control the maximum interval, as the OS controls that).
This may seem obvious to many, but make sure you've got a reliable way of knowing whether background fetch process is running correctly or not. User Notifications framework can be used to present some alert so you know if the background request resulted in something. Alternatively, os_log or Logger “Unified Logging” (see WWDC 2016 Unified Logging and Activity Tracing or 2020’s Explore logging in Swift) can be used to post messages on device that can be monitored on macOS Console app. But more than once, I've seen users do something like waiting for message to show up in Xcode or waiting for UIAlertController. You need some mechanism that works when not connected to Xcode and when the app never enters foreground.
Using your device you can fire application:performFetchWithCompletionHandler with the following steps:
Put your app in the Background state
Lock your device and wait 5 minutes.
Unlock your device, this will fire the method
(It only works if I simulate the fetch with "Xcode->Debug->Simulate
Background Fetch".)
It's because you're in Debug mode. Please try launch app without Xcode.
Another thing to check is your plist file. Make sure the UIApplicationExitsOnSuspend key is not present.
Many people here on Stack Overflow have recommended using that setting as a way to force your app to start fresh each time it's launched. That does work, but the side effect is that it prevents the new iOS 7 background fetch feature from being triggered.
If application: performFetchWithCompletionHandler: never gets fired (unless you simulate it using Xcode), check also if "Background App Refresh" preference is "On" for your app. (Settings app -> General -> Background App Refresh)
Also, background fetch is disabled if the iPhone is in Low Power Mode.
Apple provides an algorithm which defines how often the background fetch should trigger, based on your own usage of the app. If you use it a lot, then it will fetch as often as possible, but if you use like at 4pm every day, the background fetch should trigger just before, so your data is updated when you launch it.
I am working around for a while with GameKit. Almost everything works fine. I have a major Problem with the session:peer:didChangeState: Method not beeing called after resuming.
In the Apple example GKRocket the session is destroyed if the app suspends(willTerminate) and setup when it comes back(willResume). For my app it would be nice to keep the session up.
In the Logs i can see, that session:peer:didChangeState: with state GKPeerStateUnavailable is called when the app suspends, but after resuming the call with GKPeerStateAvailable doesnt show up. The session is available. But even new clients cant find the device.
I use the SessionModes Server/Client.
Thanks in advance for your help!
I was playing with sessions some time ago, but have some suggestions...
-after resuming, what happens if you try to send packed from device which was sleeping to device which was awake? does it go through ?
-do you need GKPeerStateUnavailable / available ? From my experience all you need to use are applicationDidBecomeActive and applicationWillResignActive methods: when going to sleep you send GAME_PAUSED to other device, when awake - GAME_UNPAUSED. If user puts app to sleep and then closes it - well, bad luck, the other player will sit there forever (so you can give him an option to quit to main menu). This approach works with apple (in terms of approval) - tested on two apps.
Regards!