I tried the answer of this question
How can a connection started with NSURLConnection while in the foreground be continued in the background?
And works like a charm! but only for 3 minutes in background, then iOS sends the app to sleep, Im trying to upload n videos and 3 minutes is not enought to send all data, I also readed that I can change my NSURLConnection to NSURLSession but I have no idea how to start that or how much time it takes.
Is there a way to keep background task alive after 3 minutes and until upload ends?
Is there a way to keep background task alive after 3 minutes and until upload ends?
No, there isn't. On-going background execution is limited to very narrow purposes (e.g. playing music, VOIP apps, navigation apps, etc.). All of this is outlined in the Background Execution section of the App Programming Guide for iOS.
I also [read] that I can change my NSURLConnection to NSURLSession but I have no idea how to start that or how much time it takes.
Yes, this is correct. If you want network request to continue for more than a few minutes after the app enters background, you should use NSURLSession with a background NSURLSessionConfiguration. See Downloading Content in the Background in the App Programming Guide for iOS for more information. See WWDC 2013 What's New in Foundation Networking for introduction to NSURLSession, including a demonstration on how to do background session.
I'd suggest you tackle this in two steps: First, convert to NSURLSession, and second, enable background NSURLSession operation. I think you'll find this first step is pretty easy. All of the concepts are very familiar. The only trick is that NSURLSession has two types of interfaces, delegate-protocol pattern and completion handlers. If you plan on using this for background operation later, you'll want to stick with the delegate-protocol pattern because the simpler completion handler pattern is not compatible with background sessions. And you'll want to use the upload task method that uploads from a file (don't use the stream-based rendition).
This second step, enabling background operation, isn't hard, but it can be tricky, though, simply because it involves a few new little details that we haven't had to worry about in the past (e.g. the app delegate code to capture (and later call) the completion handler for background sessions). Also, when you start debugging background session code, it can be disorienting to have requests initiated by one debugging session to show up in the next debugging session (hey, they're background sessions that are supposed to keep running when your app is terminated, after all). It's not a problem, but can be a bit disorienting.
Related
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.
I've written a simple WiFi based App using TCP on Network.Framework that receives about 80 bytes every 10s from a remote service on specific port. I want to write this data to file while in the foreground and the background.
The question is which of the background modes if any/is permitted/best fits this requirement?
All wisdom gratefully accepted.
Without knowing anything else about your app (is it a music app?, location?, VoIP?, bluetooth?) it's hard to say what you might do. But there are two classes of background actions:
Actions that are ongoing when you go to the background that can be finished.
Permitted background actions for specific kinds of apps. See "Implementing Long-Running Tasks" in the link you have about background modes.
The first doesn't really apply to you since this is ongoing. Of the second items, I'd guess that "Background fetch" is the one that fits best.
Note that you won't necessarily be called as regularly as you might like. But since your time interval is quite long (10sec) then a few hundred milliseconds latency wouldn't be particularly damaging I'd guess.
But also note that "Enabling this mode is not a guarantee that the system will give your app any time to perform background fetches. The system must balance your app’s need to fetch content with the needs of other apps and the system itself."
AppDelegate.applicationWillTerminate is called when the application is about to terminate. In this function, I am issuing a network request via Alamofire, to notify the server that the app is terminating. Alamofire's response handler is never invoked. It looks to me like the termination completes before the completion handler is invoked.
Alamofire's completion handlers appear to run on the main thread. I found documentation saying that the app is responsible for draining the main queue: "Although you do not need to create the main dispatch queue, you do need to make sure your application drains it appropriately. For more information on how this queue is managed, see Performing Tasks on the Main Thread." (From https://developer.apple.com/library/content/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html) And this is where I am stuck.
How do I drain the main thread? I need to ensure that this last Alamofire request runs before the main thread exits.
Don't worry about “draining” the main thread. The problem is more simple than that. It's just a question of how to do something when your app is leaves the “foreground”/“active” state.
When a user leaves your app to go do something else, it is generally not terminated. It enters a “suspended” state where it remains in memory but does not execute any code. So when the app is suspended, it cannot process your request (but the app isn't yet terminated, either).
There are two approaches to solve this problem.
You could just request a little time to finish your request (see Extending Your App's Background Execution Time). By doing this, your app is not suspended, but temporarily enters a "background" state, where execution can continue for a short period of time.
The advantage of this approach is that it is fairly simple process. Just get background task id before starting the request and you tell it that the background task is done in the Alamofire completion handler.
The disadvantage of this approach is that you only have 30 seconds (previously 3 minutes) for the request to be processed. If you have a good connection, this is generally adequate. But if you don't have a good network connection in that period, the request might never get sent.
The second approach is a little more complicated: You could make your request using a background URLSession. In this scenario, you are effectively telling iOS to take over the handling of this request, and the OS will continue to do so, even if your app is suspends (or later terminated during its natural lifecycle).
But this is much more complicated than the first approach I outlined, and you lose much of the ease and elegance of Alamofire in the process. You can contort yourself to do it (see https://stackoverflow.com/a/26542755/1271826 for an example), but it is far from the obvious and intuitive interface that you're used to with Alamofire. For example, you cannot use the simple response/responseJSON completion handlers. You can only download/upload tasks (no data tasks). You have to write code to handle the OS restarting your app to tell you that the network request was sent (even if you're not doing anything meaningful with this response). Etc.
But the advantage of this more complicated approach is that it is more robust. There's no 3 minute limit to this process. The OS will still take care of sending the request on your behalf whenever connectivity is reestablished. Your app may may even be terminated by that point in time, and the OS will still send the request on your behalf.
Note, neither of these approaches can handle a "force-quit" (e.g. the user double taps on the home button and swipes up to terminate the app). It just handles the normal graceful leaving of the app to go do something else.
Hopefully these questions will seem helpful to people out there. I've been learning objective c, mostly from this book, which I found to be amazing and helpful even for a noob. My questions all have to deal with this:
What happens to the queue when the user changes the view? I can't seem to find a good explanation anywhere.
From my understanding, using the NSOperation and its queue, you can always cancel it using the "cancel"...but what if you don't want it to cancel? What if, say a user selects multiple images to upload to the server, and you create a queue with the order, and the user switches to a new view controller? This might be time consuming, especially on a slow mobile network. I remember reading somewhere that iOS gives around 20 seconds extra time for a method to finish its work, but I think that's only when the app enters to the background.
For the GCD, there is no cancel method...so what happens in the background if you use async? I guess if you don't have a response to the queue, I mean you don't update the UI in any way, shouldn't the queue finish since it's sent to another thread?
I'm sure there is much more that I don't understand as far as threading goes, but I hope I made my question clear. And please please don't tell me to use the AFNetworking stuff...I tried using all those keychain wrappers out there and it all failed. Thanks to the book, the straight Apple code from the book did everything easily. I would rather learn the basics first before using the easier way out.
I would really appreciate if someone took the time to talk about this. Thanks in advance!
Your concern about only having a set amount of time to finish tasks only applies to when you switch away from your app to another app. And in that case you can use the beginBackgroundTaskWithExpirationHandler method so your app can request time to finish those tasks if your app happens to go into background. See the Executing a Finite-Length Task in the Background section of the App States and Multitasking section of the iOS App Programming Guide for more information.
But if you're still within your app (whether you transitioned to another view controller or not), anything you've added to your operation queue will continue to run until you cancel those operations (or the app is suspended or terminated). Likewise, anything you've added to a GCD queues will continue to run until the app is suspended or terminated.
In both of these scenarios, the above beginBackgroundTaskWithExpirationHandler will give you a few minutes to finish your queued tasks/operations after your app goes into the background.
My app needs to encode a large amount of audio data to an M4A file. I am currently using AVAssetWriter, which works fine, except that it takes a few minutes to encode all the data.
Instead of asking the user to keep the app running until the process has finished, I would like to pause the encoding when the app terminates and continue on relaunch.
Unfortunately, AVAssetWriter doesn't seem to support this, as it always creates a new file when initializing.
Do you know any other APIs that I could use? Maybe a third-party library?
This is exactly what background processing is intended for. As long as you can complete within 10 minutes, you can use beginBackgroundTaskWithExpirationHandler: to ask the system to let you keep running after the user switches applications. See Completing a Finite-Length Task in the Background in the iOS Application Programming Guide. Not only is this the easiest to use, but it'll give the best user experience.
The only issue you'd face is if it's possible for an audio file to take longer than 10 minutes in a non-resumable way. If that's a real possibility, then you'll need another solution.