Background

main

Background Tasks and Networking
ChimeHQ/Background

Build Status Platforms Documentation Discord

Background

Background Tasks and Networking

Integration

dependencies: [
    .package(url: "https://github.com/ChimeHQ/Background", branch: "main")
]

Concept

URLSession's background upload and download facilities are relatively straightforward to get started with. But, they are surprisingly difficult to manage. The core challange is an operation could start and/or complete while your process isn't even running. You cannot just wait for a completion handler or await call. This usually means you have to involve peristent storage to juggle state across process launches.

You also typically need to make use of system-provided API to reconnect your session to any work that has happened between launches. This can be done a few different ways, depending on your type of project and how you'd like your system to work.

Because persistent state is involved and networking operations might already be in-progress, the Uploader and Downloader types support restarting operations. Your job is to track that in-flight work. You can then just restart any work that hasn't yet completed on launch using the Uploader or Downloader types. They will take care of determining if the operations need to be actually started or just reconnected to existing work.

Usage

Uploading

import Foundation
import Background

let config = URLSessionConfiguration.background(withIdentifier: "com.my.background-id")
let uploader = Uploader(sessionConfiguration: config)

let request = URLRequest(url: URL(string: "https://myurl")!)
let url = URL(fileURLWithPath: "path/to/file")
let identifier = "some-stable-id-appropriate-for-the-file"

Task<Void, Never> {
    // remember, this await may not return during the processes entire lifecycle!
    let response = await uploader.networkResponse(from: request, uploading: url, with: identifier)
    
    switch response {
    case .rejected:
        // the server did not like the request
        break
    case let .retry(urlResponse):
        let interval = urlResponse.retryAfterInterval ?? 60.0
        
        // transient failure, could retry with interval if that makes sense
        break
    case let .failed(error):
        // failed and a retry is unlikely to succeed
        break
    case let .success(urlResponse):
        // upload completed successfully
        break
    }
}

Downloading

import Foundation
import Background

let config = URLSessionConfiguration.background(withIdentifier: "com.my.background-id")
let downloader = Downloader(sessionConfiguration: config)

let request = URLRequest(url: URL(string: "https://myurl")!)
let identifier = "some-stable-id-appropriate-for-the-file"

Task<Void, Never> {
    // remember, this await may not return during the processes entire lifecycle!
    let response = await downloader.networkResponse(from: request, with: identifier)

    switch response {
    case .rejected:
        // the server did not like the request
        break
    case let .retry(urlResponse):
        let interval = urlResponse.retryAfterInterval ?? 60.0

        // transient failure, could retry with interval if that makes sense
        break
    case let .failed(error):
        // failed and a retry is unlikely to succeed
        break
    case let .success(url, urlResponse):
        // download completed successfully at url
        break
    }
}

Widget Support

If you are making use of Downloader in a widget, you must reconnect the session as part of your WidgetConfiguration. Here's how:

struct YourWidget: Widget {
    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: provider) { entry in
           YourWidgetView()
        }
        .onBackgroundURLSessionEvents { identifier, completion in
            // find/create your downloader instance using the system-supplied
            // identifier
            let downloader = lookupDownloader(with: identifier)
            
            // and allow it to handle the events, possibly resulting in
            // callbacks and/or async functions completing
            downloader.handleBackgroundSessionEvents(completion)
        }
    }
}

More Complex Usage

This package is used to manage the background uploading facilities of Wells, a diagnostics report submission system. You can check out that project for a much more complex example of how to manage background uploads.

Contributing and Collaboration

I would love to hear from you! Issues, Discussions, or pull requests work great. A Discord server is also available for live help, but I have a strong bias towards answering in the form of documentation.

I prefer collaboration, and would love to find ways to work together if you have a similar project.

I prefer indentation with tabs for improved accessibility. But, I'd rather you use the system you want and make a PR than hesitate because of whitespace.

By participating in this project you agree to abide by the Contributor Code of Conduct.

Description

  • Swift Tools 5.8.0
View More Packages from this Author

Dependencies

  • None
Last updated: Sat Apr 13 2024 11:31:54 GMT-0900 (Hawaii-Aleutian Daylight Time)