通过另一个ObObject传递ObservableObject模型?

时间:2020-01-10 21:47:10

标签: ios swift swiftui combine observableobject

我觉得我可以理解为什么我的工作无法正常工作,但我仍在努力将自己的头围在Combine和SwiftUI上,因此这里的任何帮助都将受到欢迎。

考虑以下示例:

单视图应用程序,它将一些字符串存储在UserDefaults中,并使用这些字符串显示一些“文本”标签。有三个按钮,一个用于更新标题,一个用于将两个UserDefaults存储的字符串更新为随机字符串。

该视图是一个哑渲染器视图,标题字符串直接存储在ObservableObject视图模型中。视图模型具有一个已发布的属性,该属性持有对UserSettings类的引用,该类实现了将用户定义的字符串存储到UserDefaults的属性包装器。

观察:

•轻按“设置新标题”可正确更新视图以显示新值

•轻按两个“设置用户值”按钮不会在内部更改值,但是视图不会刷新。 如果在其中一个按钮之后点击“设置新标题”,则在为标题更改而重建视图主体时显示新值。

查看:

import SwiftUI

struct ContentView: View {
    @ObservedObject var model = ViewModel()

    var body: some View {
        VStack {
            Text(model.title).font(.largeTitle)
            Form {
                Section {
                    Text(model.settings.UserValue1)
                    Text(model.settings.UserValue2)
                }

                Section {
                    Button(action: {
                        self.model.title = "Updated Title"
                    }) { Text("Set A New Title") }
                    Button(action: {
                        self.model.settings.UserValue1 = "\(Int.random(in: 1...100))"
                    }) { Text("Set User Value 1 to Random Integer") }
                    Button(action: {
                        self.model.settings.UserValue2 = "\(Int.random(in: 1...100))"
                    }) { Text("Set User Value 2 to Random Integer") }
                }

                Section {
                    Button(action: {
                        self.model.settings.UserValue1 = "Initial Value One"
                        self.model.settings.UserValue2 = "Initial Value Two"
                        self.model.title = "Initial Title"
                    }) { Text("Reset All") }
                }

            }
        }
    }
}

ViewModel:

import Combine

class ViewModel: ObservableObject {

    @Published var title = "Initial Title"

    @Published var settings = UserSettings()

}

UserSettings模型:

import Foundation
import Combine

@propertyWrapper struct DefaultsWritable<T> {
    let key: String
    let value: T

    init(key: String, initialValue: T) {
        self.key = key
        self.value = initialValue
    }

    var wrappedValue: T {
        get { return UserDefaults.standard.object(forKey: key) as? T ?? value }
        set { return UserDefaults.standard.set(newValue, forKey: key) }
    }
}



final class UserSettings: NSObject, ObservableObject {
    let objectWillChange = PassthroughSubject<Void, Never>()

    @DefaultsWritable(key: "UserValue", initialValue: "Initial Value One") var UserValue1: String {
        willSet {
            objectWillChange.send()
        }
    }
    @DefaultsWritable(key: "UserBeacon2", initialValue: "Initial Value Two") var UserValue2: String {
        willSet {
            objectWillChange.send()
        }
    }

}

当我在UserSettings的willSet { objectWillChange.send() }上设置一个断点时,我看到objectWillChange消息正按预期发送给发布者,这告诉我问题可能是视图或视图模型是没有正确订阅。我知道,如果我在视图上将UserSettings作为@ObservedObject可以使用,但是我觉得应该在带有Combine的视图模型中完成。

我在这里想念什么?我敢肯定这很明显...

1 个答案:

答案 0 :(得分:12)

ObsesrvedObject侦听@Published属性中的更改,但不侦听更深的内部发布者,因此下面的想法是将PassthroughSubject的内部发布者与@Published var settings一起加入,表示后者已更新。

通过Xcode 11.2 / iOS 13.2测试

唯一需要的更改是在ViewModel ...

class ViewModel: ObservableObject {

    @Published var title = "Initial Title"
    @Published var settings = UserSettings()

    private var cancellables = Set<AnyCancellable>()
    init() {
        self.settings.objectWillChange
            .sink { _ in
                self.objectWillChange.send()
            }
            .store(in: &cancellables)
    }
}