不允许从后台线程发布更改;确保从主线程发布值(通过像 receive(on:) 这样的运算符)

agm*_*der 17 async-await swift swiftui

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

func getCountries() async throws{
    
        countries = try await MainViewModel.countriesApi.fetchCountries() ?? []
}
Run Code Online (Sandbox Code Playgroud)

谁称这个为:

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

}
Run Code Online (Sandbox Code Playgroud)

我想知道是否有人知道如何修复它

Joa*_*son 18

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

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

也许偏离主题,但我会更改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)
}
Run Code Online (Sandbox Code Playgroud)


小智 14

有两种方法可以解决这个问题。一,您可以将@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() ?? []
}
Run Code Online (Sandbox Code Playgroud)

第二个选项:

func getCountries() async throws{
  DispatchQueue.main.async{
   countries = try await MainViewModel.countriesApi.fetchCountries() ?? []
  }
}
Run Code Online (Sandbox Code Playgroud)