后台任务和网络
dependencies: [
.package(url: "https://github.com/ChimeHQ/Background", branch: "main")
]
URLSession
的后台上传和下载功能入门相对简单。但是,管理它们却出奇的困难。核心挑战在于操作可能在你的进程甚至没有运行的情况下启动和/或完成。你不能仅仅等待一个完成处理程序或 await
调用。这通常意味着你必须涉及持久性存储,以便在进程启动之间协调状态。
你通常还需要使用系统提供的 API 将你的会话重新连接到启动之间发生的任何工作。这可以通过几种不同的方式完成,具体取决于你的项目类型以及你希望系统如何工作。
WidgetConfiguration.onBackgroundSessionEvents(matching:_:)
UIApplicationDelegate.application(_:handleEventsForBackgroundURLSession:completionHandler:)
因为涉及到持久状态,并且网络操作可能已经在进行中,所以 Uploader
和 Downloader
类型支持重启操作。你的工作是跟踪正在进行的工作。然后,你可以使用 Uploader
或 Downloader
类型重新启动启动时尚未完成的任何工作。它们将负责确定操作是需要实际启动还是仅重新连接到现有工作。
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
}
}
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 中使用 Downloader
,你必须在你的 WidgetConfiguration
中重新连接会话。 这是方法:
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)
}
}
}
这个包用于管理 Wells(一个诊断报告提交系统)的后台上传功能。你可以查看该项目,了解如何管理后台上传的更复杂示例。
我很乐意听到你的声音! Issues、Discussions 或 pull requests 都很好。 Discord server 也可用于实时帮助,但我强烈倾向于以文档的形式回答。
我更喜欢协作,并且很乐意找到合作方式,如果你有类似的项目。
我更喜欢用制表符进行缩进以提高可访问性。但是,我宁愿你使用你想要的系统并创建一个 PR,也不愿因为空格而犹豫。
通过参与本项目,你同意遵守 贡献者行为准则。