SwiftUI 并发:仅在后台线程上运行活动

问题描述 投票:0回答:3

当我的内容视图加载并且用户第一次打开应用程序时,我会联系 API。

但是,我不希望这妨碍了主要内容。我收到的数据永远不会更新/影响 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+。

ios swiftui concurrency
3个回答
6
投票

使用 Swift Concurrency 系统时,您可以使用

Task.detached(...)
构造函数来生成非结构化分离任务。该任务将在后台同时运行。如果不需要高优先级执行,您还可以指定任务优先级
.background
(相当于DispatchQueue
qos
)。

当您尝试运行的异步函数更新触发视图重绘的属性时(

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
            }
        }
    }
}

6
投票

添加路易斯的回复(回答是因为我还不能发表评论),优先级不是后台运行的内容 - 而是

detached
部分。

如果您以优先级运行

.background
,它将不会被优先考虑,因此可能需要更长的时间才能执行。

您可以在后台运行某些内容,但优先级也很高:

Task.detached(priority: .userInitiated) {...}


0
投票

您可以将其标记为

nonisolated
或将其移出
View
,即
@MainActor
(即主线程),例如

struct ContentView: View {

    @State private var data: [Data] = []

    var body: some View {
        ...
    }
    .task {
        data = await loadData()
    }

    // nonisolated uses a background thread
    nonisolated func loadData() async -> data {
            // Call an api.
            // get some data using URLSession
            return await getData()
    }
}

// outside of View uses a background thread
func loadData() async -> [Data] {
            // Call an api.
            // get some data using URLSession
    return await getData()
}

服务异步函数的一个好地方是在 EnvironmentKey 中,就像 AuthorizationController 的工作方式一样。

© www.soinside.com 2019 - 2024. All rights reserved.