I have a question about Alamofire and its behavior with a SessionManager configured for background Tasks. I am using it to upload a video in the background.
Step I: Uploading Video:
This part is standard, however, when the upload completes:
Step II: Completing Upload:
I need to send a DELETE request to the server letting it know that the video upload is complete. If successful, the response will contain a location header for the newly uploaded video.
Step III Add Video MetaData:
With this location I need to PATCH request the video metadata: Title and Description.
So my question is about overriding the Session Manager delegate closures. I can override sessionDidFinishEventsForBackgroundURLSession and taskDidComplete and when I am completely finished with the background I need to call the system completionHandler that I am storing as a property on the SessionManager... So when/ where should I fire off the DELETE request and then when/where should I fire off the PATCH request?
Should I create 3 different background session configuration identifiers so I can identify them and make sure I chain them in right order? Because obviously I cannot say in the closure: the first time you are called fire off this request, but the second fire off this one? And which closure of the 2 is the right one to finish off the whole process and call the system completionHandler? I'm not sure if this is right because I will be out of sync with the Alamofire upload response handler.
Also I am wondering about the Alamofire response handlers. If the app was in the foreground the whole time? I would simply chain the alamofire requests together using the response handlers? but if the app terminated and is running in the background will these handlers still be around?
Any insight here would be greatly appreciated. I realize there is a lot going on here and Apple eve rate limits background tasks, I'm just wondering if this is possible and if so how to go about it?
Should I create 3 different background session configuration
identifiers so I can identify them and make sure I chain them in right
order?
I dont think you will need multiple background sessions just to identify for which request the completion block was called and to chain the next request. You can achieve it with
Asynchronous NSOperation :
You can make use of Asynchronous NSOperations to chain the multiple requests. Add the dependency among the operations and let the iOS handle scheduling and handling the dependencies. Please note : I mentioned Asynchronous NSOperation. NSOperations are Synchronous by nature.
Promise kit :
If Asynchronous NSOperations are way too complicated, you can always use Promise kit. Promise kit will take care of executing the request only after the specific request completes and the whole dependency chain will short circuit if one on the top fails.
Simply create a new data task in the completion block of upload task to upload the video. Rather than using the delegate pattern for tasks use completion blocks. That way you dont have to identify for which request the delegate was called, as each task will have its own completion block, you can easily chain them up while writing the code.
if the app terminated and is running in the background will these
handlers still be around?
Am not 100% sure though, but as far as I know, when you schedule the background task (background session), task will continue to execute even if the app is killed.After all, thats why we use background session. So I believe the completion handlers will be executed even if you kill the app.
Related
I need to execute synchronous requests on API using Swift. Requests must be queued. Meaning, if one is already in progress and it awaits response it must not be canceled or interrupted by the next synchronous request that enters queue or is already in queue.
Requests must be executed in order as they enter queue (FIFO). Next request must not start until previous is finished/completed in the queue.
Also, every single request in queue must be executed until queue is empty. Synchronous requests can enter queue at any time.
I meant to implement a Synchronous API Client as a singleton which contains its own Queue for queued requests. Requests must not stop/freeze UI. UI has to be responsive on user interaction all the time.
I know it can be done with semaphores but, unless you know what your are doing and you are completely sure how semaphores work, it is not the safest or maybe the best way do it. Otherwise, potential bugs and crashes could appear.
I'm expecting successful execution of every synchronous request that enters queue (by FIFO order, regardless if it returns success or an error as a response) and UI updates immediately after.
So, my question is what is the best way to approach and solve this problem?
Thanks for your help and time.
You can create your own DispatchQueue and put you operations on it as DispatchWorkItems. It is serial per default. Just remember to call your completions on DispatchQueue.main if you plan to update the UI.
John Sundell has a wonderful article about DispatchQueues here:
https://www.swiftbysundell.com/articles/a-deep-dive-into-grand-central-dispatch-in-swift/
I use Firebase for A/B testing. I noticed that i cannot get remote configuration synchronously, because
-(void)fetchWithCompletionHandler:(nullable FIRRemoteConfigFetchCompletion)completionHandler;
method of FIRRemoteConfig executes completion block on main thread. So i have no way to block main thread until it done.
P.S. I try to get remote configurations in didFinishLaunchingWithOptions of AppDelegate
Like most modern web APIs Firebase Remote Config fetches data from its servers asynchronously and has no option to read synchronously. There is no way to say how long the fetch may take, so it's a bad idea to block the main thread. Completion handlers are honestly the best way to deal with data that is loaded asynchronously, and that must be available before the user can continue using the app.
A few related questions:
Swift: Wait for Firebase to load before return a function
How to make app wait until Firebase request is finished
How do I wait for an asynchronously dispatched block to finish?
Force asynchrounous Firebase query to execute synchronously?
In general: making the user wait for updated configuration data, leads to a lesser user experience. For most apps you can just as easily start fetching updated remote config data when you start them, but instead of waiting the app immediately continue with the data it loaded last time. When the user restarts the app, it applies the previously loaded data, which then happens without any delay.
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.
An app I am working on requires creating a container object on a server and inserting items into that container. I don't want to create the container object until the first item needs to be inserted. However, creating the container object requires some initialization that may take a little time. While that container is still initializing the user can still to send insertion requests that aren't getting handled because the container isn't ready yet. I have two main questions:
Should this be dealt with on the client or server side?
What is the best practice for dealing with kind of this issue?
Essentially, I need to ensure my initial createContainer data task in complete before any insertItem requests are sent.
Addition Information
An insertItem request is sent by clicking on a corresponding tableViewCell. The first tableViewCell a user clicks on sends a createContainer request that creates a container holding the first item.
For a container holding n items, the request should be sent in the following order:
createContainer(Container(with: item1)
insertItem(item2)
...
insertItem(itemn)
After the first request completes, the remaining n – 1 requests may complete in any order.
My Thoughts
It sounds like I want the createContainer request to be handled synchronously while the insertItem request should be handled asynchronously. I'm not sure if that is the best approach or even how to perform that appropriately, so any guidance would be greatly appreciated.
You can use a NSOperationQueue and multiple NSOperations to implement your desired behavior. A NSOperation instance can be dependent on the completion of another NSOperation instance:
dependencies
An array of the operation objects that must finish
executing before the current object can begin executing.
For your example this would mean that the insertItem-Operations are dependent on the createContainer operation.
When you add all those operations to a NSOperationQueue your createContainer operation will run first. When it has finished, the other operations will start running as their dependencies are now satisfied. You can also control how many operations you want to run concurrently using maxConcurrentOperationCount on NSOperationQueue.
As you will be using asynchronous API in your NSOperations you will need to implement a ConcurrentOperation and handle the state changes yourself. The API Reference is explaining this in pretty good detail.
Check out the API Reference for NSOperation for further information.
There is also a nice NSHipster article on NSOperations.
Adding to the NSOperationQueue answer, it's sometimes difficult to manually manage all the state changes that an NSOperation requires to handle something asynchronous like a network call.
To simplify that, you can use a Swift Library called Overdrive. It's an amazing library in which you simply subclass a Task class and write your network code in the run() function. And when you're done, you simply call self.finish to finish the task. Here's an example: Just create a simple download task:
Then, just add it to the queue.
You can also add dependencies between tasks, which basically solves your use case.
Hope this helps.
Right now I have some older code I wrote years ago that allows an iOS app to queue up jobs (sending messages or submitting data to a back-end server, etc...) when the user is offline. When the user comes back online the tasks are run. If the app goes into the background or is terminated the queue is serialized and then loaded back when the app is launched again. I've subclassed NSOperationQueue and my jobs are subclasses of NSOperation. This gives me the flexibility of having a data structure provided for me that I can subclass directly (the operation queue) and by subclassing NSOperation I can easily requeue if my task fails (server is down, etc...).
I will very likely leave this as it is, because if it's not broke don't fix it, right? Also these are very lightweight operations and I don't expect in the current app I'm working on for there to be very many tasks queued at any given time. However I know there is some extra overhead with using NSOperation rather than using GCD directly.
I don't believe I could subclass a dispatch queue the way I can an NSOperationQueue, so there would be extra code overheard for me to maintain my own data structure and load this into & out of a dispatch queue each time the app is sent to the background, right? Also not sure how I'd handle requeueing the job if it fails. Right now if I get a HTTP 500 response from the server, for example, in my operation code I send a notification with a deep copy of the failed NSOperation object. My custom operation queue picks this notification up and adds the task to itself. Not sure how of if I'd be able to do something similar with GCD. I would also need an easy way to cancel all operations or suspend the queue when network connectivity is lost then reactivate when network access is regained.
Just hoping to get some thoughts, opinions and ideas from others who might have done something similar or are more familiar with GCD than I am.
Also worth noting I know there's some new background task support coming in iOS 7 but it will likely be a while before that will be my deployment target. I am also not sure yet if it would exactly do what I need, so at the moment just looking at the possibility of GCD.
Thanks.
If NSOperation vs submitting blocks to GCD ever shows up as measurable overhead, the problem isn't that you're using NSOperation, it's that your operations are far too granular. I would expect this overhead to be effectively unmeasurable in any real-world situation. (Sure, you could contrive a test harness to measure the overhead, but only by making operations that did effectively nothing.)
Use the highest level of abstraction that gets the job done. Move down only when hard data tells you that you should.