How often is background fetch executed in iOS? - ios

In iOS 7, a background fetch mode is supported for apps to fetch data when the app is not frontmost:
When it is convenient to do so, the system launches or resumes the app in the background and gives it a small amount of time to download any new content.
My question is: how often is the background fetch code executed?
If I set the minimum interval:
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:10];
Does it get execute every 10 seconds, or maybe once a day? What kind of interval should I expect, generally?

There is no way for you to know how often, it is up to things like the users usage pattern, device battery and whatever else Apple has in their algorithms...
The minimumBackgroundFetchInterval can be used to specify that your app doesn't need to run fetch so often, it does not make the fetch happen more often. You also have the minimum possible value in UIApplicationBackgroundFetchIntervalMinimum, which is what you can use if you want the background fetch to run as often as possible (but still no guarantee on how often it will actually run).

Related

Background Fetch Enumerations

In iOS the outcome of a background fetch could be one of the following:
UIBackgroundFetchResultNewData
UIBackgroundFetchResultNoData
UIBackgroundFetchResultFailed
In what way does iOS care about the outcome?
I understand that a fetch that lasts too long (I believe 30 secs or more) is penalized by giving less fetch opportunities to the app.
Does any of the above, specifically NoData and Failed have repercussions as well?
Or is this just for internal processing?
Why not just return UIBackgroundFetchResultNewData every time?
The precise algorithm used by Apple is not described, but in the iOS Application Programming Guide Apple states,
Apps that download small amounts of content quickly, and accurately reflect when they had content available to download, are more likely to receive execution time in the future than apps that take a long time to download their content or that claim content was available but then do not download anything.
It seems that iOS observes your app's behaviour. If it claimed that new content was available (returned UIBackgroundFetchResultNewData) but it did not actually perform a network operation it may receive less frequent background fetch opportunities. It pays to be honest.
I also seem to remember reading somewhere (but can't find a reference now) that iOS can use the completion value to determine the times of day when your server may have new content (For example, if the fetch around 1am consistently returns new data and the fetch at 6pm consistently doesn't, iOS may be more likely to perform a background fetch for your app at 1am).
You should aim to complete the fetch as quickly as possible, but do not call the completion handler until the fetch is complete or your app will be suspended without completing the download. You can also use beginBackgroundTaskWithExpirationHandler to get more time, but your app has a limit of 180 seconds, total, of background execution per 'backgrounding' (ie. If the user brings your app back to the foreground and then suspends it again the 180 seconds is reset).

Where and When to get data for Watch Complication

After working with complications for a few days, I feel confident saying the following about the update process for updates that happen at a prescribed interval:
The system calls requestedUpdateDidBegin()
This is where you can determine if your data has changed. If it hasn't, your app doesn't have to do anything. If your data has changed, you need to call either:
reloadTimelineForComplication if all your data needs to be reset.
extendTimelineForComplication if you only need to add new items to the end of the complication timeline.
Note: the system may actually call requestedUpdateBudgetExhausted() instead of requestedUpdateDidBegin() if you've spent too much of your complication's time budget for the day. This is the reason for this question.
If you called reloadTimelineForComplication, the system will call getCurrentTimelineEntryForComplication (along with the future and past variants that get arrays, depending on your time travel settings)
This is conjecture as I haven't tested it yet, but I believe if you called extendTimelineForComplication that only the getTimelineEntriesForComplication(... afterDate date: NSDate ...) would be called.
The system will then call getNextRequestedUpdateDateWithHandler so you can specify how long until your complication requires a new update.
Apple's documentation is quite clear that you should not ask for updates too often, or conduct too much processing in the complication code or you will exhaust your time budget and your complication will stop updating. So, my question is: where and when do you do the update?
For context, my scenario is a URL with return data that changes up to two times per hour.
The most obvious place in which to put the URL fetch code is func requestedUpdateDidBegin() Fetch the data, store it, and if there's no change, just return. If there was a change then extend or reload the timeline.
However, a URL fetch can be costly. Alternatives:
Put the code on the phone app and send it over with a WCSession, but if the user closes that app then the updates will no longer happen.
Use push updates, but this isn't a web app, so I have no place to send them from.
Obviously I will update all the data when the user interacts with the watch app, but that now means it only gets updated when the user uses the app, which negates the need for a complication.
Is there anywhere else? Can I have a periodic function in the watch app that isn't part of the complication? Where is the right place to fetch the data for a complication update?
For watchOS 3, Apple recommends that you switch from using the complication datasource getNextRequestedUpdateDate scheduled update to update your complication.
The old way for watchOS 2
requestedUpdateDidBegin() is really only designed to update the complication. Keeping your complication (and watch app) up to date usually involves far more than reloading the timeline (and asynchronously retrieving data never fit in well with the old approach).
The new way for watchOS 3
The new and better approach is to use background refresh app tasks. You can use a series of background tasks to schedule and handle your app extension being woken in the background to:
Fetch new data
using WKWatchConnectivityRefreshBackgroundTask to obtain data from the phone, or
using WKURLSessionRefreshBackgroundTask to download data from a server
update your model once the data arrives,
update your complication from the model (by reloading or extending the timeline), and finally
update your app's dock snapshot to show the data on the dock
Call each tasks’s setTaskCompleted method as soon as the task is complete.
Other benefits of using app tasks
One of the key features about this design is that the watch extension can now handle a variety of foreground and background scenarios which cover:
initially loading data when your app/complication starts,
updating data in the background, when the extension is woken by a background task, and
updating data in the foreground, when the user resumes your app from the dock.
Apple recommends that you use each opportunity you are given regardless of whether your app is in the foreground or background to keep your complication, app, and dock snapshot up to date.
Are there any limitations?
The number of total available tasks per day is divided among the number of apps in the dock. The fewer apps in the dock, the more tasks your app could utilize. The more apps in the dock, the fewer you can utilize.
If your complication is active, your app can be woken up at least four times an hour.
If your complication is not active, your app is guaranteed to be woken at least once an hour.
Since your app is now running in the background, you're expected to efficiently and quickly complete your background tasks.
Background tasks are limited by the amount of CPU time and CPU usage allowed them. If you exceed the CPU time (or use more than 10% of the CPU while in the background), the system will terminate your app (resulting in a crash).
For more information
A good introduction explaining when and why to update your watch app is covered in Designing Great Apple Watch Experiences.
For specifics, the Keeping Your Watch App Up to Date session covers everything you need to know to keep your complication, app, and dock snapshot up to date.
WatchBackgroundRefresh sample code demonstrates how to use WKRefreshBackgroundTask to update WatchKit apps in the background.
Edit: El Tea (op) has posted a good answer at https://stackoverflow.com/a/32994055/630614
This is an interesting question/problem, and I've been wondering about a lot of the same!
For the most part, it seems that when I'm working on a new complication I need to step back and see when I really want to update it. A "countdown" complication could set all future timeline entries at one time, when the "end date" is set. An app that shows the current status of a web service could have relevant data stored in NSUserDefaults when an APNS comes through.
If you don't have access to APNS, don't want to run your iOS app in a background mode, and don't want to make HTTP requests from Apple Watch, I can think of 2 other options.
1) Schedule local notifications. The good part is that your Apple Watch should run didReceiveLocalNotification, but the bad part is that the user will get a notification when you're simply trying to check the status without a disruption.
2) Send a message to iOS via sendMessage(_:replyHandler:errorHandler:) in your reloadTimelineForComplication method, setting nil for the replyHandler to make it as quick as possible:
Calling this method from your WatchKit extension while it is active and running wakes up the corresponding iOS app in the background and makes it reachable.
Your iOS app could perform whatever network requests are needed and then store the information or push it to Apple Watch. Unfortunately, I don't think the watch extension will have it's session.didReceive... called until you run it, but you could access the data on the next call to requestedUpdateDidBegin.
As I said, I'm very interested in this same thing, so post some thoughts back and maybe we can extrapolate on some best practices here.

How long does Apple permit a background task to run?

I have to upload an array of image files to database, therefore, I stumbled upon Apple's background execution guide to make sure the app still uploads the data when user suspends or terminates my app.
But in the desciption, it says giving it a little extra time to finish its work if we call beginBackgroundTaskWithName:expirationHandler: or beginBackgroundTaskWithExpirationHandler: to start a background task.
How long is little extra time precisely?
Correct me if I am wrong, but I have stumbled upon a perfect article from Xamarin that discusses iOS backgrounding feature.
I will simply break down to two parts, ios pre 7 and ios 7+:
iOS version pre 7
The answer is simply 600 seconds (10 minutes), reason is provided by
the article above.
iOS version 7+
The answer is that the time system allocates you is opportunistic. You
will have to use #Gary Riches's suggestion
NSLog(#"Time Remaining: %f", [[UIApplication sharedApplication] backgroundTimeRemaining]);
to find out. The reason for it being opportunistic is the way iOS 7+
handles background tasks is completely different, certainly optimised. To
be exact, It has an intermittent behaviour, and therefore, if you need
background tasks such as downloading a big chuck of data, it will be
much more effective if you use `NSURLSession` instead.
However, in my special case, I am uploading one single object that contains one file to be exact. I do not have to consider NSURLSession for uploading a small amount of data. And besides, it's uploading task, it can take as much time as it wants. :-)
For these TL;DR visitors, the answer above should be sufficient. For more details, please refer to the article above.
The amount of time will differ based on many different variables, but the value can be checked by referencing the backgroundTimeRemaining property on UIApplication:
NSLog(#"Time Remaining: %f", [[UIApplication sharedApplication] backgroundTimeRemaining]);
If you want to upload your files when app is in background, you should use Apple's background service. iOS will give your app time of approx. 3 minutes (based on some experience) for completing your task and then it will kill your app.
Apple allows longer run of the app in special cases. For that you will need to use UIBackgroundModes in your info.plist file. For more info on these special cases see table 3-1 on this link.
Here is a nice article that describes background task run time and how to achieve long running background task in iOS.
Theorically, you have 2/3 minutes to close the tasks you want to do in background, if you don't do it, your app can be killed.
After that, you can call 'beginBackgroundTaskWithExpirationHandler 'and you have to be prepared just in case the 'little extra time' that Apple gives is not enough for the tasks you need to finish.
EDIT:
When an iOS application goes to the background, are lengthy tasks paused?:
From the documentation:
Return from applicationDidEnterBackground(_:) as quickly as possible. Your implementation of this method has approximately five seconds to perform any tasks and return. If the method doesn’t return before time runs out, your app is terminated and purged from memory.
From Raywenderlich:
'Again, there are no guarantees and the API documentation doesn’t even give a ballpark number – so don’t rely on this number. You might get 5 minutes or 5 seconds, so your app needs to be prepared for anything!':
http://www.raywenderlich.com/29948/backgrounding-for-ios
How much time you get after your app gets backgrounded is determined by iOS. There are no guarantees on the time you’re granted, but you can always check the backgroundTimeRemaining property of UIApplication. This will tell you how much time you have left.
The general, observation-based consensus is that usually, you get 10 minutes. Again, there are no guarantees and the API documentation doesn’t even give a ballpark number – so don’t rely on this number. You might get 5 minutes or 5 seconds, so your app needs to be prepared for anything!
It is not fixed: in Xcode 10 and Swift4.1
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
print(UIApplication.shared.backgroundTimeRemaining)
}
OP1: 166.13057912699878 mean approx 2.7 min,
OP2: 177.4997792619979 mean approx 2.95 min

swift/ios refreshing app data when in background

I'm writing a iOS/Swift application which reads data from a REST service each X minutes and updates the UI accordingly.
Now I would like that when the app is put in the background, a task keeps being invoked at X minutes intervals reading from the REST service and, in case the data just read satisfies a given condition, show a notification prompting the user to bring the app back to the foreground.
In my searches I've read that during applicationDidEnterBackground event, I should start a task with beginBackgroundTaskWithExpirationHandler.
The problem is that, if I've understood correctly, this allows a maximum of 10/15 minutes after which the app is terminated if the task is not stopped with endBackgroundUpdateTask, while I want the task to keep polling the service indefinitely (at least until the user disable it from the app's settings)
My question is:
How is this kind of functionality performed normally? Do some common solutions or best practices exist for the solution of such a problem?
Use iOS Background Fetch feature where you can specify minimum background fetch interval. But actual interval between successive invocation of your code will be determined by iOS framework. For details checkout this link: http://code.tutsplus.com/tutorials/ios-7-sdk-working-with-background-fetch--mobile-20520
I use this approach in my app and I think it is a preferred way of doing.
You can use a local notification that can be presented from the background in case your condition is met.
Correct, iOS will eventually shut down the background process, you can't enforce continuous background activity. Use the backgroundTimeRemaining property to check how much time your application has left and try to handle it as gracefully as possible by calling endBackgroundTask so that iOS does not force kill your app.
As a solution, you could think about using remote notifications with with content-available : YES, which runs the didReceiveRemoteNotification
Have a look at the Parse.com Their local datastore is an abstraction for what you are trying to acheive.
By the way, is it really necessary to refresh in the background. If call is relatively quick, there is no need to refresh until the user open's the app. Background processes like that, using the net can be quite battery consuming when the user are not on a Wifi. So consider the use case carefully!

Consistent background fetch in iOS7?

I was just wondering if it was possible to consistently update the data for my app every 60 seconds.
I have the following code right now:
NSTimeInterval testTime = 60.0;
[application setMinimumBackgroundFetchInterval:testTime];
However when testing on the simulator, the app initially grabs the data upon install, and then doesn't do anything else. My web searches tell me that iOS determines when to actually trigger background fetch.
That being said, is there anyway to consistently have background fetches occur for the user? Like say, for every 1 minute? My app's big selling point depends on the latest up to date information. I think it's possible, as apps like Gmail,twitter, SnapChat are always checking for new data...
Thanks
There is no way to achieve this using background fetch. You can only force this behavior using push (silent or otherwise), sending a push message to each device once every period of time.
Allow me to say, this is a terrible design. Polling is a terrible design for mobile apps. You should implement proper push notifications, notifying the user of new content, and loading it in the background as the OS deems possible.

Resources