使用 onKeyPress 和 ObservableObject 时,如何避免有关“不允许从视图更新中发布更改”的 SwiftUI 警告?

Jam*_*mes 5 swiftui property-wrapper-published ios17 macos-sonoma

我一直在尝试使用onKeyPressSwiftUI 5 中的新功能。但是,更新到@Published在处理程序中更新可观察对象的属性会产生警告“不允许从视图更新中发布更改,这将导致未定义的行为。”

注意,在实践中我还没有看到任何实际错误,但 Xcode 在控制台中记录了大量红色警告和紫色运行时问题。

简单的复制;可以通过按钮修改,也可以在本地修改@State而不提示;但是,修改该@Published属性会onKeyPress产生警告。

import SwiftUI

@MainActor
class ExampleService: ObservableObject {
  @Published var text = ""
}

struct ContentView: View {
  @StateObject var service = ExampleService()
  
  @State var localText = ""
  
    var body: some View {
      VStack {

        Button {
          // This is fine
          service.text += "!"
        } label: {
          Text("Press Me")
        }
        
        Label("Example Focusable", systemImage: "arrow.right")
          .focusable()
          .onKeyPress { action in
            // XCode whines about "Publishing changes from within view updates is not allowed, this will cause undefined behavior."
            service.text += "."
            return .handled
          }
        
        Label("Example Local State", systemImage: "arrow.left")
          .focusable()
          .onKeyPress { action in
            // This is fine
            localText += "."
            return .handled
          }
        
        Text(service.text)
        Text(localText)
      }
    }
}

#Preview {
    ContentView()
}
Run Code Online (Sandbox Code Playgroud)

wor*_*dog 2

要使警告消失,请使用

DispatchQueue.main.async {  
    service.text += "." 
}
Run Code Online (Sandbox Code Playgroud)

这将确保按照 SwiftUI 的要求在主线程上执行 UI 更新。请注意,正如评论中提到的,您也可以使用Task{...}.

据我了解,@MainActor应该在主队列上执行,但(我猜)并不总是如此。请参阅此提案以将其从 Swift 6 中删除: https://github.com/apple/swift-evolution/blob/main/proposals/0401-remove-property-wrapper-isolation.md