How to use NSURLSession to upload data in background mode in a SDK?

I am creating SDK, which needs to upload data in a background mode.


Cuttenly, I am able to upload the files from SDK, but the call back is not getting called.



extension BackgroundUpload: URLSessionDelegate
{
    @nonobjc func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        DispatchQueue.main.async {
    
// Call back is not getting called.
    
        }
    }
}

This BackgroundUpload is in a SDK pod.

Replies

If you’re building library code that uses

URLSession
background sessions you must provide an entry point that the app can call when it gets the
application(_:handleEventsForBackgroundURLSession:completionHandler:)
app delegate callback. Beyond that I don’t think there’s any other restrictions here.

With regards the code snippet you posted, here’s a version with line numbers:

extension BackgroundUpload: URLSessionDelegate
{
    @nonobjc func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        DispatchQueue.main.async {

// Call back is not getting called.

        }
    }
}

You’ve indicated that you never get to line 6. What about to line 4? If you get to 4 but not 6, there’s clearly a problem with getting to the main thread.

Also, what’s up with the

@nonobjc
attribute on line 3? This method is expected to be called from Objective-C, because that’s what
NSURLSession
is implemented using, so marking it as
@nonobjc
is incorrect.

Note You can mark code snippets as code using the

<>
icon, and doing so makes it easier to discuss the details.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks Eskimo for the replay and detail explanation.


Line 6 and 4 are not getting called, I have updated the fuction to use Objective-C, removed @nonobjc.


I am uploading data from SDK to server and I dont want to update the app regarding this, so I tired using the below method which getting fired.



extension BackgroundUpload: URLSessionTaskDelegate {
   
    public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {

        if let urlResponse = task.response as? HTTPURLResponse {
            let status = urlResponse.statusCode
           
            print(status)
        }
    }
}



Onces my server give me the responce as 200 I need to remove data from my Core data.

If you put the same code into an app, does it work? I suspect that the problem you’re seeing is not related to the SDK-ness, but rather is a problem with the code itself. One way to confirm that suspicion is to put the same code in the app itself. If it fails there as well you know that the SDK has nothing to do with it.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Yes! it is working fine in sample App.


Please find the code, in my AppDelegate file

var backgroundSessionCompletionHandler: (() -> Void)?
func application(_ application: UIApplication, handleEventsForBackgroundURLSession
        identifier: String, completionHandler: @escaping () -> Void) {
  
        backgroundSessionCompletionHandler = completionHandler
      
    }


BackgroundUpload class contains


publiclazyvar bgSession: URLSession = {
        let config = URLSessionConfiguration.background(withIdentifier: Constant.sessionID.rawValue)
        config.sessionSendsLaunchEvents = true
        return URLSession(configuration: config, delegate: self, delegateQueue: nil)
    }()



publicfunc startUpload()  {
       
        guard let url = URL(string: "http://192.***.XX.XX:8081") else {
            return
        }
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
        let urlPath = getDataFromPath(pathSting:(UserDefaults.standard.string(forKey: "ImageData") ?? ""))
        // Background Uploader URL session
        let task = self.bgSession.uploadTask(with: request, fromFile: URL(fileURLWithPath: urlPath))
        task.resume()

    }



extension BackgroundUpload: URLSessionDelegate, URLSessionTaskDelegate
{
    public func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
    }
   
    public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { 
    }
   
    public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
     print(error.debugDescription)

       "This method is getting called, onces upload is done."
    }
}


Suggest me, what different thing I need to do?

Before you work on your library code you should start by getting your application code working reliably. Right now you’re missing a critical part of that, namely, the connection between

application(_:handleEventsForBackgroundURLSession:completionHandler:)
and
urlSessionDidFinishEvents(forBackgroundURLSession:)
. You should take a look at Downloading Files in the Background, which explains how that works.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks for you help Eskimo, its working fine.