How Long Does Apple Permit a Background Task to Run

How long does Apple permit a background task to run?

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.

App restart after 180 sec of execution in background mode. By enable the background modes in capabilities

The limit for background tasks is 3 minutes (180 seconds) in later versions of iOS. You cannot extend this to 4 hours.

Apple Docs:

Note: Always provide an expiration handler when starting a task, but if you want to know how much time your app has left to run, get the value of the backgroundTimeRemaining property of UIApplication.

Good Stack Overflow Post on the topic:

How long does Apple permit a background task to run?

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.

If you need additional time to perform any final tasks, request additional execution time from the system by calling beginBackgroundTask(expirationHandler:). Call beginBackgroundTask(expirationHandler:) as early as possible. Because the system needs time to process your request, there’s a chance that the system might suspend your app before that task assertion is granted. For example, don’t call beginBackgroundTask(expirationHandler:) at the very end of your applicationDidEnterBackground(_:) method and expect your app to continue running.

If the long-running operation you describe above is on the main thread and it takes longer than 5 seconds to finish after your application heads to the background, your application will be killed. The main thread will be blocked and you won't have a chance to return from -applicationDidEnterBackground: in time.

If your task is running on a background thread (and it really should be, if it's taking long to execute), that thread appears to be paused if the application returns from -applicationDidEnterBackground: (according to the discussion in this answer). It will be resumed when the application is brought back to the foreground.

However, in the latter case you should still be prepared for your application to be terminated at any time while it's in the background by cleaning things up on your way to the background.

What is the Xcode Background Processing Background Mode?

There is no documentation yet. But in WWDC2019, they explain what it is and how to use it. Here is the link:
Apple WWDC 2019

Say like you wanted to clean up your database in background to delete old records. First, you have to enable background processing in your Background Modes Capabilities. Then in your Info.plist add the background task scheduler identifier:
Snapshot from an Info.plist file showing permitted background task scheduler identifiers.

Then in 'ApplicationDidFinishLaunchingWithOptions' method register your identifier with the task.

BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.example.apple-samplecode.ColorFeed.db_cleaning", using: nil) { task in
// Downcast the parameter to a processing task as this identifier is used for a processing request
self.handleDatabaseCleaning(task: task as! BGProcessingTask)
}

Do the work that you wanted to perform in the background and put it into the operation queue. In our case, the cleanup function will looks like:

// Delete feed entries older than one day...
func handleDatabaseCleaning(task: BGProcessingTask) {
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1

// Do work to setup the task
let context = PersistentContainer.shared.newBackgroundContext()
let predicate = NSPredicate(format: "timestamp < %@", NSDate(timeIntervalSinceNow: -24 * 60 * 60))
let cleanDatabaseOperation = DeleteFeedEntriesOperation(context: context, predicate: predicate)

task.expirationHandler = {
// After all operations are canceled, the completion block is called to complete the task
queue.cancelAllOperations()
}

cleanDatabaseOperation.completionBlock {
// Perform the task
}

// Add the task to the queue
queue.addOperation(cleanDatabaseOperation)
}

Now, when the app goes into the background we have to schedule the background task in BGTaskScheduler.

Note: BGTaskScheduler is a new feature to schedule multiple background tasks that will be performed into the background].

enter image description here

This background task will execute once a week to clean up my database. Check out the properties you can mention to define the task types.

Background upload of large amount of data

Unfortunately, Apple's APIs are terrible at this task because of bad design decisions. There are a couple of major obstacles that you face:

  • There are limits to how many simultaneous tasks you can create. I think performance starts to break down at somewhere on the order of 100 tasks.
  • There are limits to how often the OS will wake your app up. The more often it wakes your app, the longer it will wait before waking it again. At some point, this will result in not being able to schedule new tasks.
  • Uploads don't continue from where they left off in the event of a failure; they restart. This can result in huge bandwidth costs on a bad network.

I suspect that the best approach is to:

  • Chunk the requests into several large groups, each of which can be written to a single ZIP archive that is not so big that the user runs out of disk space, but not so small that it uploads too quickly.
  • Write the first set of those files into a single file (e.g. in ZIP format).
  • Use a custom script on the server side that lets you resume uploads from where it left off by adding extra CGI parameters.
  • On failure, ask the server how much data it got, then truncate the front of the file and reupload from the current spot.
  • On success, compute how quickly the first large file finished uploading, and if it is not O(minutes), combine the next few sets. Write the set/sets to a file, and start the next request.

With the caveat that all of this must be done fairly quickly. You may find it necessary to pre-combine the files into ZIP archives ahead of time to avoid getting killed. But do not be tempted to combine them into a single file, because then you'll take too long when truncating the head on retry. (Ostensibly, you could also provide any parameters as part of the URL, and make the POST body be raw data, and provide a file stream to read from the ZIP archive starting at an offset.)

If you're not banging your head against a wall already, you soon will be. :-)

iOS 13 Schedule iOS background tasks

The solution is to run on a device. I was running on a simulator. However, It was showing the Background App Refresh has been enabled in Settings while running on the simulator.

There may be some other reasons. Please visit
https://developer.apple.com/documentation/backgroundtasks/bgtaskschedulererrorcode/bgtaskschedulererrorcodeunavailable?language=objc



Related Topics



Leave a reply



Submit