Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct way to use backgroundCompletionHandler in Alamofire?

I'm not clear on how to use this properly but had seen other people doing this type of thing:

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

In our similar implementation, at this point completionHandler is partial apply forwarder for reabstraction thunk helper...

Where manager is (despite being a singleton) essentially:

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

However this causes the following warning to be printed in the console:

Warning: Application delegate received call to -application:handleEventsForBackgroundURLSession:completionHandler: but the completion handler was never called.

I set a breakpoint here and at this point the message is already visible in the console and backgroundCompletionHandler is nil.

We're building against the iOS 9 SDK with Xcode 7.0 and currently using Alamofire 2.0.2

I originally thought this was introduced when we merged our Swift 2.0 branch but I'm also seeing the message with an earlier commit using Xcode 6.4 against the iOS 8 SDK.


Update 1

To address @cnoon's suggestions:

  • The identifier matches - the configuration and manager are set inside a singleton so there's no way for it to be wrong.
  • When adding and printing inside of didSet on backgroundCompletionHandler in the Manager class, the message is logged before the warning.
  • When printing inside of the closure set to sessionDidFinishEventsForBackgroundURLSession on the delegate inside the Manager class, the message is printed after the warning.
  • When overriding sessionDidFinishEventsForBackgroundURLSession and printing inside of it before calling backgroundCompletionHandler, the message is printed after the warning.
  • As for verifying I have my Xcode project set up correctly for background sessions, I'm not sure how to do that and couldn't find any documentation on how to do so.

I should note that when trying to upload some screenshots from my phone I was initially unable to reproduce this issue in order to try these suggestions.

It was only after trying to share some photos that I was able to reproduce this again. I'm not sure or the correlation (if any) but it may be related to the photos taking longer to upload.


Update 2

The UIBackgroundModes are set exactly as @Nick described, and calling completionHandler() directly inside of application:handleEventsForBackgroundURLSession:completionHandler: does not display the warning.


Update 3

So, it appears I overlooked an small but important detail. There's a wrapper around Alamofire.Manager that doesn't expose it directly. The relevant part of its implementation looks like this:

private var manager: Manager

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

and setting the completion handler in the AppDelegate executes that code path.

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

I've confirmed that the following change to expose the instance of Alamofire.Manager and access it directly does not produce the warning:

public var manager: Manager

//    public var backgroundCompletionHandler: (() -> Void)? {
//        get {
//            return manager.backgroundCompletionHandler
//        }
//        set {
//            manager.backgroundCompletionHandler = backgroundCompletionHandler
//        }
//    }

and in the AppDelegate:

func application(application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: () -> Void) {
    myManager.sharedInstance.manager.backgroundCompletionHandler = completionHandler
}

Based on this it appears that using a computed property to proxy the completion handler is the cause of the issue.

I'd really prefer not to expose this property and would love to know of any workarounds or alternatives.

like image 866
Paul Young Avatar asked Sep 24 '15 16:09

Paul Young


1 Answers

It appears as though everything you are doing is correct. I have an example app that does exactly what you've described that works correctly and does not throw the warning you are seeing. I'm guessing you still have some small error somewhere. Here are a few ideas to try out:

  • Verify the identifier matches the identifier of your background session
  • Add a didSet log statement on the backgroundSessionHandler in the Manager class temporarily to verify it is getting set
  • Add a log statement into the sessionDidFinishEventsForBackgroundURLSession to verify it is getting called as expected
  • Override the sessionDidFinishEventsForBackgroundURLSession on the delegate and manually call the backgroundSessionHandler
  • Verify you have your Xcode project set up correctly for background sessions

Update 2

Your computed property is wrong. Instead it needs to set the backgroundCompletionHandler to newValue. Otherwise you are never setting it to the new value correctly. See this thread for more info.

like image 117
cnoon Avatar answered Sep 21 '22 14:09

cnoon