Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Async download inside didReceiveRemoteNotification

My app is configured to support silent push (content-available), and also supports background fetch. Want I need to achieve is upon receiving a silent push, I need to send an ajax request to the server, get back the data and save it (persist it with CoreData).

Of course all this happens without the user ever opening the app. When he do opens up the app, a fresh data will be waiting. This is the silent push call back:

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {

    // Use Alamofire to make an ajax call:
    //...
    let mutableURLRequest = NSMutableURLRequest(URL: URL)
    let requestBodyData : NSMutableData = NSMutableData()

    mutableURLRequest.HTTPBody = body
    mutableURLRequest.HTTPMethod = "POST"
    mutableURLRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization")
    mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")

    request(mutableURLRequest)
        .responseJSON { (req, res, data, error) in

            //We do not reach this code block !

            // Save the incoming data to CoreData
            completionHandler(UIBackgroundFetchResult.NewData)
    }
}

Now, the problem is that when a notification arrives and the delegate is called, the ajax code executes, make the call to the server, and then exits. The code inside the ajax callback will not run. But, when I open the app and brings it to the foreground, suddenly this code section wakes up and continues to run.

This is not the desirable behaviour because when I open the app I still need to wait 1-2 seconds for the those operations to run (updating the UI etc)

What am I doing wrong here? Should I open a new background thread for this operation?

UPDATE:
I moved completionHandler(UIBackgroundFetchResult.NewData) into the ajax callback, but still this dose not fix the original problem, which is that this ajax callback code block won't execute.

UPDATE 2:
It seems like it's an issue with Alamofire, and that I will have to use NSURLSession to make this ajax call. Trying to put some code together.

like image 853
Yaron Levi Avatar asked Aug 16 '15 02:08

Yaron Levi


1 Answers

To add to this answer. When I do this, I have gotten spotty support when starting that background fetch after didReceiveRemoteNotification. I can start an Alamofire connection, but it may not work, it will die almost immediately.

Scouring the internet and looking at silent notification tutorials, no one seems to mention the use of:

  • beginBackgroundTaskWithExpirationHandler:
  • beginBackgroundTaskWithName(_:expirationHandler:)
  • endBackgroundTask(_:)

Everyone says:

ya, you get about 30 seconds per the docs to get your work done or you need to use the NSURLSession Background Session

It appears in my, own testing and hair pulling, that in order to buy yourself that 30 seconds you likely need to invoke the beginBackgroundTask* + endBackgroundTask methods on the UIApplicationDelegate

References:

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/#//apple_ref/occ/instm/UIApplication/beginBackgroundTaskWithName:expirationHandler:

https://forums.developer.apple.com/message/96731#96731

Specifically, the apple dev forum answer is done by a member of Apple Developer Relations, Developer Technical Support, Core OS/Hardware

He specifically says:

Huh? You’re issuing the request in the NSURLSession shared session. Such requests will only run as long as the app remains running. If the app is suspended again, which is typically what happens in this case, the request will stop halfway through. You have two options here:

  • Continue using the shared session and use a UIApplication background task to prevent your app from suspended. IMPORTANT For this to work the request must completely quickly, because the UIApplication background task will typically only give you about 30 seconds of execution time.
  • Run the request in an NSURLSession background session, which will organise to resume your app when the request is complete.

Note the first option Continue using the shared session and use a UIApplication background task to prevent your app from suspended.

like image 198
AJ Venturella Avatar answered Sep 23 '22 07:09

AJ Venturella