What are folks using for uploading a file in the b...
# ios
j
What are folks using for uploading a file in the background on iOS (as part of KMP/CMP app)? I had started trying Alamofire but looks like background support is not fully working in v5 (https://github.com/Alamofire/Alamofire/discussions/3615).
d
I've not tried it in KMP, but in general, I'd say that a significant part of the iOS community has moved away from Alamofire and towards directly using URLSession APIs over the last couple of years. Might be worth a try in KMP? I think most APIs should be available from Objective-C and therefore Kotlin.
m
Are you talking about when your app gets from foreground to background (1) or when your app is closed and you want to do some work in background (2)? For first I basically implemented apple docs: https://developer.apple.com/documentation/foundation/url_loading_system/downloading_files_in_the_background/ Second one I did not try, but there are also docs available: https://developer.apple.com/documentation/backgroundtasks/choosing-background-strategies-for-your-app
j
I'm looking to do multipart form data based file upload which alamofire has nice support for
m
Yeah, but as you know it does not support background because Apple has some weird requirements, but here's the gist of it (swift but it shouldn't be too hard to translate to kotlin/objc): First, you need to store content you're gonna upload to file or it won't work. Then I have class that manages whole thing:
Copy code
class BackgroundTransfersManager: NSObject {
    private var backgroundSession: URLSession!

    private override init() {
        super.init()

        let config = URLSessionConfiguration.background(withIdentifier: "com.company.app.background")
        // see, if you need need these 3 parameters
        config.sessionSendsLaunchEvents = true
        config.isDiscretionary = false
        config.httpCookieStorage = nil

        backgroundSession = URLSession(configuration: config, delegate: self, delegateQueue: nil)
    }

    ...

    func upload()  {
                ...
                for fragment in fragments {
                    let url = URL(string: EndpointConstantsKt.FRAGMENT_CONTENT_STORE)!
                    var request = URLRequest(url: url)
                    request.httpMethod = "POST"
                    request.setValue("multipart/form-data; boundary=\(fragment.keyId)", forHTTPHeaderField: "Content-Type")

                    let task = self.backgroundSession.uploadTask(with: request, fromFile: tempDirectory.appendingPathComponent(fragment.keyId))
                    task.resume()
                }

...

    // if you used `config.sessionSendsLaunchEvents = true`
    // that means you needed for your app to be woken up from background to execute some logic after file was uploaded
    var backgroundCompletionHandler: (() -> Void)?
    func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        DispatchQueue.main.async {
            self.backgroundCompletionHandler?()
            self.backgroundCompletionHandler = nil
        }
    }
}

    // implement needed delegates
    // to know when upload has completed you'll need `extension BackgroundTransfersManager: URLSessionDataDelegate` and
    // `func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)`
    // for progress you can use
    // `func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64)`

    // if you used `config.sessionSendsLaunchEvents = true`
class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
BackgroundTransfersManager.shared.backgroundCompletionHandler = completionHandler
    }
}
j
thanks, I'll take a look at that