如何异步更新ViewModel中UI相关的@Published变量?

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

一方面

var coins
应该在
MainThread
上更新(作为 UI 相关变量)。另一方面,
cryptoService.fetchCoins()
需要在后台线程上完成。我很难连接这两个世界。
在下面的代码中,我尽了最大努力,但仍然有一些我不明白的编译警告。 “do”块的两行上有两个警告:

@Sendable
闭包中捕获不可发送类型“MainPageViewModel”的“self”

将不可发送类型“MainPageViewModel”的参数传递到主要参与者隔离的上下文中可能会引入数据竞争

class MainPageViewModel: ObservableObject {
    @Published var coins: [Coin] = []
    private let cryptoService = CryptoService()
    
    init() {
        fetchCoins()
    }
    
    private func fetchCoins() {
        Task {
            do {
                let coins = try await cryptoService.fetchCoins()
                await updateUI(with: coins)
            } catch {
                print("Error fetching coins: \(error)")
            }
        }
    }
    
    @MainActor
    private func updateUI(with coins: [Coin]) {
        self.coins = coins
    }
}

我想做的是一些非常基本的事情。需要有一种方法可以正确地做到这一点。而且它不应该太复杂。

附注
请不要推荐组合。还必须有一种无需合并即可完成的方法。

更新。
代码库的一些 UI 部分:

@StateObject var viewModel = MainPageViewModel()
...
VStack(spacing: 12) {
    ForEach(viewModel.coins) { coin in
        CryptoCoinView(coin: coin)
    }
}
...
asynchronous swiftui viewmodel
1个回答
0
投票

当您升级到异步/等待时,您可以简单地删除组合

ObservableObject
/
@Published
并仅使用
.task
,例如

struct MainPage: View {
    @Environment(\.cryptoService) var cryptoService
    @State var coins: [Coin] = []

    var body: some View {
        ForEach(coins) { coin in
            CryptoCoinView(coin: coin)
        }
        .task {
            if coins.count == 0 { return }
            do {
                coins = try await cryptoService.fetchCoins()
            } catch {
                print("Error fetching coins: \(error)")
            }
        }
    }
}

只要您的

struct CryptoService
未标记为
@MainActor
,那么
fetchCoins
就会在后台线程上运行。

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.