ObservableObject 文本即使使用 objectWillChange 也不会更新 - 多个 类

ObservableObject text not updating even with objectWillChange - multiple classes

我目前正在编写一个实用程序,作为其中的一部分,接收到一个字符串(通过 WebSockets 和 Starscream 库),然后将字符串值显示在 SwiftUI 视图(名为 ReadingsView)中。

代码结构如下 - 有两个 classes,管理 WebSocket 连接的 WSManager class 和具有 ObservableObject [的 GetReadings class =36=],它管理和存储读数。

当使用 WSManager class 中的 didReceive 方法接收字符串时,WSManager class 中的 decodeText 方法对其进行解码,然后调用 GetReadings [=] 中的 parseReceivedStrings 方法31=].

class WSManager : WebSocketDelegate {

    func didReceive(event: WebSocketEvent, client: WebSocket) {
        case .text(let string):
            // Decode the text
            DispatchQueue.main.async {
                self.decodeText(recvText: string)
                print("Received text: \(string)")
            }
            recvString = string
}

    func decodeText(recvText: String) {
        // If the message is allowed, then pass it to getReadings
        print("Decoding")
        if recvText.hasPrefix("A=") {
            getReadings.parseReceivedStrings(recvText: recvText, readingType: .allreadings)
            print("All readings received")
        } else if recvText.hasPrefix("T = ") {
            getReadings.parseReceivedStrings(recvText: recvText, readingType: .temperature)
        } else if recvText.hasPrefix("P = ") {
            getReadings.parseReceivedStrings(recvText: recvText, readingType: .pressure)
        } else if recvText.hasPrefix("H = ") {
            getReadings.parseReceivedStrings(recvText: recvText, readingType: .humidity)
        } else {
            print("Unrecognised string.")
        }
    }
}
enum ReadingType {
    case allreadings
    case temperature
    case pressure
    case humidity
}

class GetReadings: ObservableObject {


    let objectWillChange = ObservableObjectPublisher()

    @Published var temp: Float = 0.0 {
        willSet {
            print("Temp new = " + String(temp))
            objectWillChange.send()
        }
    }
    
    @Published var pressure: Float = 0.0 {
        willSet {
            print("Pressure new = " + String(pressure))
            objectWillChange.send()
        }
    }
    
    @Published var humidity: Float = 0.0 {
        willSet {
            print("Humidity new = " + String(humidity))
            objectWillChange.send()
        }
    }

    func getAll() {
        //print(readings.count)
        //print(readings.count)
        wsManager.socket.write(string: "get_all")
    }

    func parseReceivedStrings (recvText: String, readingType: ReadingType) {
        if readingType == .allreadings {
            // Drop first two characters
            let tempText = recvText.dropFirst(2)
            // Split the string into components
            let recvTextArray = tempText.components(separatedBy: ",")
            // Deal with the temperature
            temp = (recvTextArray[0] as NSString).floatValue
            // Pressure
            pressure = (recvTextArray[1] as NSString).floatValue
            // Humidity
            humidity = (recvTextArray[2] as NSString).floatValue
        }
    }
}

解析值时,我希望 ReadingsView 中的值立即更新,因为我已将变量标记为 @Published,并使用 objectWillChange 属性 手动推送更改。 willSet 参数中的打印语句反映新值,但文本不会更新。在 ReadingsView 代码中,我通过在按下刷新按钮时手动调用 parseReceivedString 方法对此进行了补偿(这被用作发送请求的 WebSocket 协议的一部分),但这会导致读数落后于它们所在的位置应该。理想情况下,我希望读数在上一段中描述的方法中解析后立即更新。

struct ReadingsView: View {
    @ObservedObject var getReadings: GetReadings
    var body: some View {
        VStack {
            Text(String(self.getReadings.temp))
            Text(String(self.getReadings.pressure))
            Text(String(self.getReadings.humidity))
            Button(action: {
                print("Button clicked")
                self.getReadings.getAll()
                self.getReadings.parseReceivedStrings(recvText: wsManager.recvString, readingType: .allreadings)
            }) {
                Image(systemName: "arrow.clockwise.circle.fill")
                    .font(.system(size: 30))
            }
        .padding()
        }
    }
}

我想知道我是否使用了正确的声明,或者我正在尝试做的事情是否与使用多个 classes 不兼容 - 这是我第一次使用 SwiftUI,所以我可能会遗漏一些细微差别.提前感谢您的帮助。

已编辑 - 添加代码

ContentView

struct ContentView: View {
    @State private var selection = 0
 
    var body: some View {
        TabView(selection: $selection){
            ReadingsView(getReadings: GetReadings())
                .tabItem {
                    VStack {
                        Image(systemName: "thermometer")
                        Text("Readings")
                    }
                } .tag(0)
            SetupView()
                .tabItem {
                    VStack {
                        Image(systemName: "slider.horizontal.3")
                        Text("Setup")
                    }
                }
                .tag(1)
        }
    }
}

如果您正在使用 ObservableObject,则无需在 Published 属性的 willSet 中写入 objectWillChange.send()

这意味着您也可以删除:

let objectWillChange = ObservableObjectPublisher()

ObservableObject类.

默认提供

还要确保如果您要更新 @Published 属性,请在 main 队列 (DispatchQueue.main) 中进行。异步请求通常在后台队列中执行,您可能会尝试在后台更新您的属性,但这是行不通的。

您不需要将所有代码包装在 DispatchQueue.main 中 - 只需更新 @Published 属性:

的部分
DispatchQueue.main.async {
    self.humidity = ...
}

并确保您只创建 一个 GetReadings 实例并在您的视图中共享它。为此,您可以使用 @EnvironmentObject.

SceneDelegate 中创建 ContentView:

// create GetReadings only once here
let getReadings = GetReadings()

// pass it to WSManager
// ...

// pass it to your views
let contentView = ContentView().environmentObject(getReadings)

然后在您的 ReadingsView 中您可以这样访问它:

@EnvironmentObject var getReadings: GetReadings

请注意,您不再需要在 TabView 中创建它:

TabView(selection: $selection) {
    ReadingsView()
    ...
}