在后台线程中发布更改是不允许的;请确保通过主线程(例如使用receive(on:)运算符)发布值。

11

我正在使用Swift和SwiftUI构建一个应用程序。在MainViewModel中,我有一个函数调用Api以从url获取JSON并进行反序列化。这是在async/await协议下完成的。 问题是,我从xcode收到了以下评论:“不允许从后台线程发布更改;确保通过像receive(on:)这样的运算符在模型更新时从主线程发布值。”在代码的这一部分:

func getCountries() async throws{
    
        countries = try await MainViewModel.countriesApi.fetchCountries() ?? []
}

谁调用了这个函数:

func fetchCountries() async throws -> [Country]? {

    guard let url = URL(string: CountryUrl.countriesJSON.rawValue ) else {
        print("Invalid URL")
        return nil
    }
    let urlRequest = URLRequest(url: url)
    do {
        let (json, _) = try await URLSession.shared.data(for: urlRequest)

        if let decodedResponse = try? JSONDecoder().decode([Country].self, from: json) {
            debugPrint("return decodeResponse")
            return decodedResponse
        }
    } catch {
            debugPrint("error data")
        
    }
    return nil

}

我想知道是否有人知道如何修复它。
3个回答

11
有两种方法可以解决这个问题。第一种是将@MainActor属性添加到您的函数中-这将确保它们在主线程上运行。文档: https://developer.apple.com/documentation/swift/mainactor。然而,这可能会导致延迟和冻结,因为整个块将在主线程上运行。您也可以使用DispatchQueue.main.async{}来设置变量-请参见Hacking With Swift的这篇文章。以下是示例:
@MainActor func getCountries() async throws{
   ///Set above - this will prevent the error
   ///This can also cause a lag
   countries = try await MainViewModel.countriesApi.fetchCountries() ?? []
}

第二个选项:
func getCountries() async throws{
  DispatchQueue.main.async{
   countries = try await MainViewModel.countriesApi.fetchCountries() ?? []
  }
}

谢谢你,兄弟。你救了我的一天。 - Erkam KUCET

10

首先异步获取数据,然后在主线程上将结果赋值给属性。

func getCountries() async throws{    
    let fetchedData = try await MainViewModel.countriesApi.fetchCountries()
    await MainActor.run {
        countries = fetchedData ?? []
    }
}

也许与主题不相关,但是我会将fetchCountries()更改为在出现错误时返回一个空数组而不是nil,或者更好的是实际上抛出错误,因为它被声明为可以抛出异常。

像这样:

func fetchCountries() async throws -> [Country] {
    guard let url = URL(string: CountryUrl.countriesJSON.rawValue ) else {
        return [] // or throw custom error
    }
    let urlRequest = URLRequest(url: url)
    let (json, _) = try await URLSession.shared.data(for: urlRequest)

    return try JSONDecoder().decode([Country].self, from: json)
}

0
@MainActor
func getCountries() async throws {
        countries = try await MainViewModel.countriesApi.fetchCountries() ?? [] }

这应该解决这个问题。
只需要在函数开始之前添加MainActor即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接