GCD background task maximum TTL - ios

I need to be able to program background tasks. Little "crons" if you will, that execute some simple code. While not being an expert in GCD I was wondering:
What is the maximum time I expect for the background task to actually perform its duties in the background before apps quits completely
Can I "program" multiple tasks and expect them to complete in timely order
Are they only active as long as the app is launched? ( I bet they are, unlike local notifications that dont really care whether the app is running in the background or not, so just asking to be sure)
How to I keep track of them and cancel if needed?
For instance I able to do something like this and task is performed. I went as far as 1 minute here and it works.
let backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
DispatchQueue.main.asyncAfter(wallDeadline: DispatchWallTime.now() + 60) {
// Some action here
UIApplication.shared.endBackgroundTask(backgroundTaskIdentifier)
})

You can print the time remaining for a given session using backgroundTimeRemaining (docs here). Apple makes no guarantees about what this time will be, it varies with battery level, hardware, resources, etc., so probably no good for a long running persistent background task. You might want to consider the background fetch API, although this is similarly throttled by iOS and you don't have complete control over when it runs.

Related

Download multiple files with operation queue not stable in background mode

Currently what I want to achieve is download files from an array that download only one file at a time and it still performs download even the app goes to the background state.
I'm using Rob code as stated in here but he's using URLSessionConfiguration.default which I want to use URLSessionConfiguration.background(withIdentifier: "uniqueID") instead.
It did work in the first try but after It goes to background everything became chaos. operation starts to download more than one file at a time and not in order anymore.
Is there any solution to this or what should I use instead to achieve what I want. If in android we have service to handle that easily.
The whole idea of wrapping requests in operation is only applicable if the app is active/running. It’s great for things like constraining the degree of concurrency for foreground requests, managing dependencies, etc.
For background session that continues to proceed after the app has been suspended, though, none of that is relevant. You create your request, hand it to the background session to manage, and monitor the delegate methods called for your background session. No operations needed/desired. Remember, these requests will be handled by the background session daemon even if your app is suspended (or if it terminated in the course of its normal lifecycle, though not if you force quit it). So the whole idea of operations, operation queues, etc., just doesn’t make sense if the background URLSession daemon is handling the requests and your app isn’t active.
See https://stackoverflow.com/a/44140059/1271826 for example of background session.
By the way, true background sessions are really useful when download very large resources that might take a very long time. But it introduces all sorts of complexities (e.g., you often want to debug and diagnose when not connected to the Xcode debugger which changes your app lifecycle, so you have to resort to mechanisms like unified messaging; you need to figure out how to restore UI if the app was terminated between the time the requests were initiated and when they finished; etc.).
Because of this complexity, you might want to consider whether this is absolutely needed. Sometimes, if you only need less than 30 seconds to complete some requests, it’s easier to just ask the OS to keep your app running in the background for a little bit after the user leaves the app and just use standard URLSession. For more information, see Extending Your App's Background Execution Time. It’s a much easier solution, bypassing many background URLSession hassles. But it only works if you only need 30 seconds or less. For larger requests that might exceed this small window, a true background URLSession is needed.
Below, you asked:
There are some downside with [downloading multiple files in parallel] as I understanding.
No, it’s always better to allow downloads to progress asynchronously and in parallel. It’s much faster and is more efficient. The only time you want to do requests consecutively, one after another, is where you need the parse the response of one request in order to prepare the next request. But that is not the case here.
The exception here is with the default, foreground URLSession. In that case you have to worry about latter requests timing out waiting for earlier requests. In that scenario you might bump up the timeout interval. Or we might wrap our requests in Operation subclass, allowing us to constrain not only how many concurrent requests we will allow, but not start subsequent requests until earlier ones finish. But even in that case, we don’t usually do it serially, but rather use a maxConcurrentOperationCount of 4 or something like that.
But for background sessions, requests don’t time out just because the background daemon hasn’t gotten around to them yet. Just add your requests to the background URLSession and let the OS handle this for you. You definitely don’t want to download images one at a time, with the background daemon relaunching your app in the background when one download is done so you can initiate the next one. That would be very inefficient (both in terms of the user’s battery as well as speed).
You need to loop inside an array of files and then add to the session to make it download but It will be download asynchronously so it's hard to keeping track also since the files are a lot.
Sure, you can’t do a naive “add to the end of array” if the requests are running in parallel, because you’re not guaranteed the order that they will complete. But it’s not hard to capture these responses as they come in. Just use a dictionary for example, perhaps keyed by the URL of the original request. Then you can easily look up in that dictionary to find the response associated with a particular request URL.
It’s incredibly simple. And we now can perform requests in parallel, which is much faster and more efficient.
You go on to say:
[Downloading in parallel] could lead the battery to be high consumption with a lot of requests at the same time. that's why I tried to make it download each file one at a time.
No, you never need to perform downloads one at a time for the sake of power. If anything, downloading one at a time is slower, and will take more power.
Unrelated, if you’re downloading 800+ files, you might want to allow the user to not perform these requests when the user is in “low data mode”. In iOS 13, for example, you might set allowsExpensiveNetworkAccess and allowsConstrainedNetworkAccess.
Regardless (and especially if you are supporting older iOS versions), you might also want to consider the appropriate settings isDiscretionary and allowsCellularAccess.
Bottom line, you want to make sure that you are respectful of a user’s limited cellular data plan or if they’re on some expensive service (e.g. connecting on an airplane’s expensive data plan or tethered via some local hotspot).
For more information on these considerations, see WWDC 2019 Advances in Networking, Part 1.

Is there any way not to get CPU-throttled in the background?

I have a CPU task that needs to occur when the app is running in the background (either by way of fetch or silent notification). This task takes about 1s when running in the foreground but about 9s when running in the background. It's basically saving out ~100K textual entries to a database. Whether I use FileHandle operations or a Core Data sqlite solution, the performance profile is about the same (Core Data is a little slower surprisingly).
I don't really want to get into the specifics of the code. I've already profiled the hell out of it and in the foreground it's quite performant. But clearly when the app is running in the background it's being throttled by iOS to the tune of a 9x slowdown. This wouldn't be such a big issue except in response to a silent notification iOS only gives the app 30-40s to complete and this 9s task can put it over the limit. (The rest of it is waiting on subsystems that I have no control over.)
So the question:
Is there any way to tell iOS Hi, yes, I'm in the background but I really need this chunk of code to run quickly and avoid your throttling ? FWIW I'm already running in a .userInitiated qos dispatch queue:
DispatchQueue.global(qos: .userInitiated).async {
// code to run faster goes here
}
Thanks!
First, no. The throttling is on purpose, and you can't stop it. I'm curious if using a .userInitiated queue is actually improving performance much over a default queue when you're in the background. Even if that's true today, I wouldn't bet on that, and as a rule you shouldn't mark something user initiated that is clearly not user initiated. I wouldn't put it past Apple to run that queue slower when in the background.
Rather than asking to run more quickly, you should start by asking the OS for more time. You do that by calling beginBackgroundTask(expirationHandler:) when you start processing data, and then call endBackgroundTask(_:) when you're done. This tells the OS that you're doing something that would be very helpful if you could complete, and the OS may give you several minutes. When you run out of whatever time it gives you, then it'll call your expirationHandler, and you can save off where you were at that point to resume work later.
When you run out of time, you're only going to get a few seconds to complete your expiration handler, so you may not be able to write a lot of data to disk at that point. If the data is coming from the network, then you address this by downloading the data first (using a URLSessionDownloadTask). These are very energy efficient, and your app won't even be launched until the data is finished downloading. Then you start reading and processing, and if you run out of time, you squirrel away where you were in user defaults so you can pick it up again when you launch next. When you're done, you delete the file.

How to run a function at a specific time everyday using ios swift3 [duplicate]

I have read this answer and I do not believe it has what I am looking for but I a am beginner and I am happy to have someone point out the answer in this link : dispatch_after - GCD in swift?
My goal: set a function to run at 9 AM in the user's time zone (or system's time zone) every single day.
I've used GCD very briefly to delay a function as follows (it works perfectly fine):
var dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(10 * Double(NSEC_PER_SEC)))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
let alert = UIAlertView()
alert.title = "Foo Alert"
alert.message = "foo."
alert.addButtonWithTitle("OK")
alert.show()
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
});
So I wanted to fire it daily as I've mentioned above but the first thing I am stuck on is swapping out DISPATCH_TIME_NOW for a time zone relevant value? Do I even need to consider time zones or will simply replacing DISPATCH_TIME_NOW with military 09:00 be sufficient?
Also, any advice on the overall goal, scheduling to fire function same time every day would be much appreciated.
I'm also not married to using GCD for this goal but it was the one I ran into the most doing searches.
In short, you cannot generally execute some arbitrary function at some arbitrary time unless the app is still running. The dispatch_after presumes that the app is still running at the scheduled time, which is not generally assured. The dispatch_after is great for "do something in x seconds", not "do something tomorrow at 9am".
Yes, dispatch_after can perform some task on some background thread, but that's very different concept from having the app run in the background (i.e., when the app, itself, is no longer in foreground). Please refer to App Programming Guide for iOS: Background Execution, which enumerates all of the various background mechanisms.
The key technologies for performing something when the app is not currently active include:
Using background fetch, you can opportunistically check for new data on the server (but not per your schedule, but rather at the discretion of the OS).
If your app is serving several very specific tasks (e.g. music app, VOIP, navigation app, etc.) you can register for background operation.
You can schedule a local notification to fire at particular time (though it is incumbent on the user to see the notification and tap on it in order for the app to run). For more information see the Local and Remote Notification Programming Guide.
You can register for push notifications and then your server could push a notification to clients at some time of your choosing (but that requires server-side development).
There are other background tasks that can be configured (e.g. continue network requests in background, request a few minutes to finish some finite length task even if the app is no longer active, etc.), but those seem unrelated to your question.
If you could clarify what you want the app to do at the scheduled time, we might be able to advise what is possible and what the alternatives are. But, in broad brush strokes, those are the basic options you have.
Importantly, you should use dispatch_walltime instead of dispatch_time. The difference: If you set a dispatch_time for "1000 seconds from now" it will run 1000 seconds from now (if your app is running). But dispatch_walltime will calculate which time that is on the user's clock, and will run when the user's clock reaches that time.
So if you set up dispatch_time for 9am tomorrow morning, and I set the clock on my device forwards by five minutes, then dispatch_time will run when my clock displays 9:05am. dispatch_walltime will run at 9:00am. (You'll have to experiment what happens if I change the clock from 8:55am to 9:05am because then running when the clock shows 9:00am is obviously impossible).

On iOS, can you begin work on the background thread before applicationDidEnterBackground is called?

I'm updating some content in my app and I want that to finish up when the user switches out of the app. It seems like I have to stop my currently-running update and start another one in the applicationDidEnterBackground method. It would be much more convenient if I could mark some work as something I want to run in the background before that method is called.
Here's the scenario:
I'm trying to update content and start running a SQL update that takes a bit of time. (More than the five seconds you have to return from applicationDidEnterBackground.)
The user leaves the app. The current update is suspended, but I really want it to finish.
I can start a new update which picks up where the other left off, but if the user switches back into the app I have SQL-level concurrency issues.
Is the only option to break down the SQL queries to smaller batches so I can switch over cleanly in the applicationDidEnterBackground callback? It almost doubles the execution time. (I'm not worried about the OS killing my background task, resume is handled fine.)
Ideally I'd be able to have the existing work continue seamlessly in the background (at the pleasure of the OS), is that possible? Are there better options?
(I've read the Programming Guide's section on executing background tasks.)
You can continue to run your current threads. You don't have to stop any of them and start new one.
The only thing which you need to do, if to use beginBackgroundTaskWithExpirationHandler (as proxi mentioned) when you entering background and use endBackgroundTask when you are done. This method gives your application up to 10 minutes of execution. UI of your application won't be accessible (since a user switched to another app), but all threads of your app will continue to run. System will pause all threads when your will do endBackgroundTask or 10 minutes will expire.
I would organize it like this
Have you processing threads running
In applicationDidEnterBackground call beginBackgroundTaskWithExpirationHandler.
Save UIBackgroundTaskIdentifier somewhere accessbile.
At the end of your processing thread, check whether UIBackgroundTaskIdentifier isn't 0 and if it's not, call endBackgroundTask. Set UIBackgroundTaskIdentifier to zero.
If I understand right, you just have to wrap your long-running operation into beginBackgroundTaskWithExpirationHandler block. See the method's documentation for details on how to use it.

How to do lengthy operations without being killed by the watchdog? iphone

I have an important operation that is executed rarely. In some cases, it might take minutes to execute. My app is getting killed after a 50 second operation. How to avoid that?
Should I put it in a background thread? Could anyone please point me in the right direction here. I have not found any useful information about the so called watchdog. Is a background thread the way to go?
Yes, you need to move this task to a background thread. You should never jam up the main thread with any task that takes longer than a fraction of a second to perform. Ignoring the watchdog timer, which only kicks in under extreme conditions, your application is completely unresponsive to touch or other events during this lengthy operation, and you're unable to provide feedback to the user as to the progression of this operation.
The watchdog timer will kill an application that jams up the main thread for an extremely long period of time, making the application unresponsive to input (I believe this duration is currently 20 seconds on startup, but I'm not sure what it is for when the application is running). You should never let your application get to the point where the watchdog is killing it, because that's pointing to a real problem in the way your application is handling things.
Moving a long-running task to a background thread is a lengthy topic by itself, which is why I recommend reading Apple's Concurrency Programming Guide (updated) as well as watching some of their WWDC videos on the subject before starting.
However, in my opinion the most elegant way to deal with long-running tasks is to use Grand Central Dispatch, where something like
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do your long-running task here
dispatch_async(dispatch_get_main_queue(), ^{
// Do callbacks to any UI updates here, like for a status indicator
});
});
will fire off your task to be performed in a background thread on one of the global concurrent queues. The little section of code within the main block shows how you might update any UI elements, such as a progress bar, from within this background task. Generally, UI updates must be performed on the main thread (there are some exceptions as of iOS 4.0, but it's still a good practice in general).
I also highly recommend adding some kind of visual indication of the status of this long-running task as it proceeds. Your users will really appreciate this, and it will make your application appear faster, even though it may run for the same duration.
Can you occasionally hit the watchdog during your process? Watchdog timers are just there to detect whether something crashed. They aren't really concerned with the system being busy.
Is the phone still able to respond to the user doing stuff like pressing the home button during your process?
EDIT: This guys recommends using a background thread

Resources