Updating a @Published variable based on changes in an observed variable

ⅰ亾dé卋堺 提交于 2020-01-05 06:59:45

问题


I have an AppState that can be observed:

class AppState: ObservableObject {

    private init() {}
    static let shared = AppState()

    @Published fileprivate(set) var isLoggedIn = false

}

A View Model should decide which view to show based on the state (isLoggedIn):

class HostViewModel: ObservableObject, Identifiable {

    enum DisplayableContent {
        case welcome
        case navigationWrapper
    }

    @Published var containedView: DisplayableContent = AppState.shared.isLoggedIn ? .navigationWrapper : .welcome

}

In the end a HostView observes the containedView property and displays the correct view based on it.

My problem is that isLoggedIn is not being observed with the code above and I can't seem to figure out a way to do it. I'm quite sure that there is a simple way, but after 4 hours of trial & error I hope the community here can help me out.


回答1:


When you add an ObservedObject to a View, SwiftUI adds a receiver for the objectWillChange publisher and you need to do the same. As objectWillChange is sent before isLoggedIn changes it might be an idea to add a publisher that sends in its didSet. As you are interested in the initial value as well as changes a CurrentValueSubject<Bool, Never> is probably best. In your HostViewModel you then need to subscribe to AppState's new publisher and update containedView using the published value. Using assign can cause reference cycles so sink with a weak reference to self is best.

No code but it is very straight forward. The last trap to look out for is to save the returned value from sink to an AnyCancellable? otherwise your subscriber will disappear.




回答2:


DISCLAIMER:

It is not full solution to the problem, it won't trigger objectWillChange, so it's useless for ObservableObject. But it may be useful for some related problems.

Main idea is to create propertyWrapper that will update property value on change in linked Publisher:

@propertyWrapper
class Subscribed<Value, P: Publisher>: ObservableObject where P.Output == Value, P.Failure == Never {
    private var watcher: AnyCancellable?

    init(wrappedValue value: Value, _ publisher: P) {
        self.wrappedValue = value
        watcher = publisher.assign(to: \.wrappedValue, on: self)
    }

    @Published
    private(set) var wrappedValue: Value {
        willSet {
            objectWillChange.send()
        }
    }

    private(set) lazy var projectedValue = self.$wrappedValue
}

Usage:

class HostViewModel: ObservableObject, Identifiable {

    enum DisplayableContent {
        case welcome
        case navigationWrapper
    }

    @Subscribed(AppState.shared.$isLoggedIn.map({ $0 ? DisplayableContent.navigationWrapper : .welcome }))
    var contained: DisplayableContent = .welcome

    // each time `AppState.shared.isLoggedIn` changes, `contained` will change it's value
    // and there's no other way to change the value of `contained`
}



回答3:


Working solution:

After two weeks of working with Combine I have now reworked my previous solution again (see edit history) and this is the best I could come up with now. It's still not exactly what I had in mind, because contained is not subscriber and publisher at the same time, but I think the AnyCancellable is always needed. If anyone knows a way to achieve my vision, please still let me know.

class HostViewModel: ObservableObject, Identifiable {

    @Published var contained: DisplayableContent
    private var containedUpdater: AnyCancellable?

    init() {
        self.contained = .welcome
        setupPipelines()
    }

    private func setupPipelines() {
        self.containedUpdater = AppState.shared.$isLoggedIn
            .map { $0 ? DisplayableContent.mainContent : .welcome }
            .assign(to: \.contained, on: self)
    }

}

extension HostViewModel {

    enum DisplayableContent {
        case welcome
        case mainContent
    }

}


来源:https://stackoverflow.com/questions/58187541/updating-a-published-variable-based-on-changes-in-an-observed-variable

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!