SwiftUI 并发:运行 activity 仅在后台线程上
SwiftUI Concurrency: Run activity ONLY on background thread
当我的内容视图加载并且它是用户第一次打开应用程序时,我联系 API。
但是,我不希望它挡住主要内容。我收到的数据永远不会update/affect UI。所以它应该完全 运行 在后台。
现在,运行是这样的:
struct ContentView: View {
@StateObject var settings = Settings()
var body: some View {
}
.task {
await loadData()
}
func loadData() async {
// Call an api.
// get some data using URLSession
settings.data = data
}
}
我收到以下错误:[SwiftUI] 不允许从后台线程发布更改;确保在模型更新时从主线程发布值(通过像 receive(on:) 这样的运算符)。
我想我明白了:SwiftUI 认为我希望任务更新 UI。
为了修复它,我试过:
.task {
DispatchQueue.global(qos: .background).async {
await loadData()
}
}
但是,我得到:无法将类型为“@Sendable () async -> ()”的函数传递给需要同步函数类型的参数
在这种情况下我将如何使用调度队列?我只针对 iOS 15+。
在使用 Swift 并发系统时,您可以使用 Task.detached(...)
构造函数来生成非结构化分离任务。如果你想在后台 运行 它,你可以在初始化参数中另外指定 .background
任务优先级。
由于您尝试 运行 的异步函数更新了一个 属性,它触发了视图重绘(settings
被声明为 ObservedObject
,我假设 data
是一个Published
属性),所以你必须从主角那里设置这个属性。
要实现这一点,您可以这样做:
struct ContentView: View {
@StateObject var settings = Settings()
var body: some View {
// Some view...
}
.task {
await loadData()
}
func loadData() async {
await Task.detached(priority: .background) {
// Call an api.
// Get some data using URLSession
await MainActor.run {
settings.data = data
}
}
}
}
当我的内容视图加载并且它是用户第一次打开应用程序时,我联系 API。
但是,我不希望它挡住主要内容。我收到的数据永远不会update/affect UI。所以它应该完全 运行 在后台。
现在,运行是这样的:
struct ContentView: View {
@StateObject var settings = Settings()
var body: some View {
}
.task {
await loadData()
}
func loadData() async {
// Call an api.
// get some data using URLSession
settings.data = data
}
}
我收到以下错误:[SwiftUI] 不允许从后台线程发布更改;确保在模型更新时从主线程发布值(通过像 receive(on:) 这样的运算符)。
我想我明白了:SwiftUI 认为我希望任务更新 UI。
为了修复它,我试过:
.task {
DispatchQueue.global(qos: .background).async {
await loadData()
}
}
但是,我得到:无法将类型为“@Sendable () async -> ()”的函数传递给需要同步函数类型的参数
在这种情况下我将如何使用调度队列?我只针对 iOS 15+。
在使用 Swift 并发系统时,您可以使用 Task.detached(...)
构造函数来生成非结构化分离任务。如果你想在后台 运行 它,你可以在初始化参数中另外指定 .background
任务优先级。
由于您尝试 运行 的异步函数更新了一个 属性,它触发了视图重绘(settings
被声明为 ObservedObject
,我假设 data
是一个Published
属性),所以你必须从主角那里设置这个属性。
要实现这一点,您可以这样做:
struct ContentView: View {
@StateObject var settings = Settings()
var body: some View {
// Some view...
}
.task {
await loadData()
}
func loadData() async {
await Task.detached(priority: .background) {
// Call an api.
// Get some data using URLSession
await MainActor.run {
settings.data = data
}
}
}
}