在主线程上使用@Published 值?

TAL*_*ALE 9 swift swiftui combine

有没有办法指定 count 应该只在主线程上发布?我看过一些讨论使用 设置发布者的文档receive(on:),但在这种情况下,@Publisher包装器隐藏了该逻辑。

import SwiftUI
import Combine

class MyCounter: ObservableObject {
  @Published var count = 0

  public static let shared = MyCounter()
  
  private init() { }
}

struct ContentView: View {
    @ObservedObject var state = MyCounter.shared
    var body: some View {
        return VStack {
            Text("Current count: \(state.count)")
            Button(action: increment) {
                HStack(alignment: .center) {
                    Text("Increment")
                        .foregroundColor(Color.white)
                        .bold()
                }
            }
        }
    }
    
    private func increment() {
        NetworkUtils.count()
    }
}

public class NetworkUtils {

    public static func count() {
        guard let url = URL.parse("https://www.example.com/counter") else {
            return
        }
        
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            if let response = response as? HTTPURLResponse {
                let statusCode = response.statusCode
                if statusCode >= 200 && statusCode < 300 {
                    do {
                        guard let responseData = data else {
                            return
                        }
                        
                        guard let json = try JSONSerialization.jsonObject(with: responseData, options: []) as? [String: Any] else {
                            return
                        }
                        
                        if let newCount = json["new_count"] as? Int{
                            MyCounter.shared.count = newCount
                        }
                    } catch  {
                        print("Caught error")
                    }
                }
            } 
        }
        task.resume()
    }   
}
Run Code Online (Sandbox Code Playgroud)

正如您从我更新的示例中看到的,这是一个简单的 SwiftUI 视图,其中有一个按钮,单击该按钮会进行网络调用。网络调用是异步的。当网络调用返回时,ObservableObject MyCounter会在后台线程上更新。我想知道是否有办法ObservableObject在主线程上发布更改。我现在知道要完成此操作的唯一方法是将更新逻辑包装在网络调用闭包中,如下所示:

DispatchQueue.main.async {
    MyCounter.shared.count = newCount
}
Run Code Online (Sandbox Code Playgroud)

Gil*_*man 7

URLSession.shared.dataTask(with: request)您可以使用URLSession.shared.dataTaskPublisher(for: request)( docs ) 而不是使用,这将允许您创建一个组合管道。然后,您可以将receive(on:)操作符链接为管道的一部分。

URLSession.shared.dataTaskPublisher(for: request)
  .map { response in ... }
  ...
  .receive(on: RunLoop.main)
  ...
Run Code Online (Sandbox Code Playgroud)

另请查看heckj 的示例,我发现它们非常有用。


LuL*_*aGa 5

如果您尝试设置@Published从后台线程标记的值,您将看到此错误:

不允许从后台线程发布更改;确保从主线程发布值(通过像接收这样的操作符

因此,您必须确保在主线程上设置的值的任何位置,这些值将始终发布在主线程上。

  • 正确的。这就是我看到的错误,我想知道是否有一种方法可以配置当您使用 @Published 包装器自动将更新移动到主线程时使用的发布者。现在,我只是通过使用 DispatchQueue.main.async {} 切换到主线程来更新我使用的那些字段。 (4认同)