45

I am using Alamofire within a new app (A Download Manager Sample based on Alamofire) I need some clarifications about downloading files using the background session. I need to override SessionDelegate to get it works? Or just backgroundCompletionHandler?

Typically what are the steps to handle downloads in background using Alamofire? And how can I handle the case where my app is relauch, with downloads in flux.

Caleb Kleveter
  • 11,170
  • 8
  • 62
  • 92
LastMove
  • 2,482
  • 1
  • 15
  • 25
  • 1
    https://github.com/Alamofire/Alamofire, go to section **Downloading a File**, and Alamofire requests are asynchronous – saurabh May 13 '15 at 11:07

3 Answers3

33

Update

Based on this amazing tutorial, I have put together an example project available on GitHub. It has an example for background session management.

According to Apple's URL Loading System Programming Guide:

In both iOS and OS X, when the user relaunches your app, your app should immediately create background configuration objects with the same identifiers as any sessions that had outstanding tasks when your app was last running, then create a session for each of those configuration objects. These new sessions are similarly automatically reassociated with ongoing background activity.

So apparently by using the appropriate background session configuration instances, your downloads will never be "in flux".

I have also found this answer really helpful.

Original answer

From Alamofire's GitHub page:

Applications can create managers for background and ephemeral sessions, as well as new managers that customize the default session configuration, such as for default headers (HTTPAdditionalHeaders) or timeout interval (timeoutIntervalForRequest).

By default, top level methods use a shared Manager instance with default session configuration. You can however create a manager with background session configuration like so:

let configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("com.example.app.background")
let manager = Alamofire.Manager(configuration: configuration)

You can then make requests using this Manager instance.

manager.startRequestsImmediately = true
let request = NSURLRequest(URL: NSURL(string: "your.url.here")!)
manager.request(request)

By looking at its implementation, it also has a property called backgroundCompletionHandler, so you can add a completion block:

manager.backgroundCompletionHandler = {
        // do something when the request has finished
    }
Community
  • 1
  • 1
József Vesza
  • 4,775
  • 3
  • 27
  • 42
  • Thank you for your response. I had already read Alamofire's documentation. My main question is "how can I handle the case where my app is relauch, with downloads in flux." – LastMove May 16 '15 at 17:48
  • @LastMove I understand. I've done some research, check the updated answer, it might be useful. – József Vesza May 16 '15 at 17:57
  • Thank you very much, it not exactly what I was expecting but it's by far the best answer – LastMove May 18 '15 at 09:21
  • @LastMove sorry I couldn't be more helpful. I have one more thing for you: I have an example project in [this GitHub Repo](https://github.com/jozsef-vesza/swift-lessons) with a working background session implementation. Check the "Networking" project. You can also find the resources I used in the description. In my experience, you don't have to worry about app restarts, background sessions work "automatically". Hope that helps! :) – József Vesza May 18 '15 at 09:48
  • FIY startRequestsImmediately is true by default, thanks for the answer mate – thibaut noah Jun 08 '17 at 08:55
  • Could you add a section near the top explaining why it's easier to do this directly than with AF? It took me quite a bit of googling to figure out that AF's closure-based callback system simply can't work with background sessions, at least not directly, and that this is one use case that's better handled directly (if painfully). – RonLugge Oct 20 '17 at 05:49
  • @JózsefVesza Can you please update your example to at least Swift 3 ? – Sneha Nov 03 '17 at 06:54
  • My destinationURL is nil if I fire a download request with the above URL. and the file downloaded doesnt get downloaded using above manager. @JózsefVesza – Mukul More Jan 08 '18 at 05:53
31

EDIT

With Alamofire 5 this is no longer possible, see the release notes:

Using a URLSessionConfiguration with a background identifier is not possible any more. We're explicitly ensuring Alamofire isn't used with background sessions, in order to prevent ongoing issues around support and surprise on the part of the user.

Old answer, still valid if you use Alamofire 4

It's actually very easy with Alamofire:

1) your Alamofire.Manager should be configured with a background session identifier:

class NetworkManager {
    ...
    private lazy var backgroundManager: Alamofire.SessionManager = {
        let bundleIdentifier = ...
        return Alamofire.SessionManager(configuration: URLSessionConfiguration.background(withIdentifier: bundleIdentifier + ".background"))
    }()
    ...
}

2) in the App Delegate implement application(_:handleEventsForBackgroundURLSession:completionHandler: and pass the completion handler to Alamofire.SessionManager.backgroundCompletionHandler.

In my case the app delegate method looks like

func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
    NetworkManager.default.backgroundCompletionHandler = completionHandler
}

and my network manager has a computed property like this to set the Manager property:

var backgroundCompletionHandler: (() -> Void)? {
    get {
        return backgroundManager.backgroundCompletionHandler
    }
    set {
        backgroundManager.backgroundCompletionHandler = newValue
    }
}
Luca Torella
  • 7,974
  • 4
  • 38
  • 48
9

I was searching for the solution quite long. Until read the article mentioned above. The issue for me was - I had to enable External accessory communication

enter image description here

Everything else was done as described above. AppDelegate:

func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
        BackendAPIManager.sharedInstance.backgroundCompletionHandler = completionHandler
    }

Singleton:

import Alamofire

class BackendAPIManager: NSObject {
    static let sharedInstance = BackendAPIManager()

    var alamoFireManager : Alamofire.SessionManager!

    var backgroundCompletionHandler: (() -> Void)? {
        get {
            return alamoFireManager?.backgroundCompletionHandler
        }
        set {
            alamoFireManager?.backgroundCompletionHandler = newValue
        }
    }

    fileprivate override init()
    {
        let configuration = URLSessionConfiguration.background(withIdentifier: "com.url.background")
        configuration.timeoutIntervalForRequest = 200 // seconds
        configuration.timeoutIntervalForResource = 200
        self.alamoFireManager = Alamofire.SessionManager(configuration: configuration)
    }
}

And the calls are done in the following way:

BackendAPIManager.sharedInstance.alamoFireManager.upload(multipartFormData: { (multipartFormData) in ...
Naloiko Eugene
  • 2,453
  • 1
  • 28
  • 18