@Published property wrapper not working on subclass of ObservableObject

前端 未结 4 1908
攒了一身酷
攒了一身酷 2020-12-08 02:52

I have a class conforming to the @ObservableObject protocol and created a subclass from it with it\'s own variable with the @Published property wrapper to manage state.

相关标签:
4条回答
  • 2020-12-08 03:17

    This is because ObservableObject is a protocol, so your subclass must conform to the protocol, not your parent class

    Example:

    class MyTestObject {
        @Published var aString: String = ""
    
    }
    
    final class MyInheritedObject: MyTestObject, ObservableObject {
        @Published var anotherString: String = ""
    }
    

    Now, @Published properties for both class and subclass will trigger view events

    0 讨论(0)
  • 2020-12-08 03:22

    Finally figured out a solution/workaround to this issue. If you remove the property wrapper from the subclass, and call the baseclass objectWillChange.send() on the variable the state is updated properly.

    NOTE: Do not redeclare let objectWillChange = PassthroughSubject<Void, Never>() on the subclass as that will again cause the state not to update properly.

    I hope this is something that will be fixed in future releases as the objectWillChange.send() is a lot of boilerplate to maintain.

    Here is a fully working example:

        import SwiftUI
    
        class MyTestObject: ObservableObject {
            @Published var aString: String = ""
    
        }
    
        class MyInheritedObject: MyTestObject {
            // Using @Published doesn't work on a subclass
            // @Published var anotherString: String = ""
    
            // If you add the following to the subclass updating the state also doesn't work properly
            // let objectWillChange = PassthroughSubject<Void, Never>()
    
            // But if you update the value you want to maintain state 
            // of using the objectWillChange.send() method provided by the 
            // baseclass the state gets updated properly... Jaayy!
            var anotherString: String = "" {
                willSet { self.objectWillChange.send() }
            }
        }
    
        struct MyTestView: View {
            @ObservedObject var myTestObject = MyTestObject()
            @ObservedObject var myInheritedObject = MyInheritedObject()
    
            var body: some View {
                NavigationView {
                    VStack(alignment: .leading) {
                        TextField("Update aString", text: self.$myTestObject.aString)
                        Text("Value of aString is: \(self.myTestObject.aString)")
    
                        TextField("Update anotherString", text: self.$myInheritedObject.anotherString)
                        Text("Value of anotherString is: \(self.myInheritedObject.anotherString)")
                    }
                }
            }
        }
    
    0 讨论(0)
  • 2020-12-08 03:31

    From my experience, just chain the subclass objectWillChange with the base class's objectWillChange like this:

    class GenericViewModel: ObservableObject {
        
    }
    
    class ViewModel: GenericViewModel {
        @Published var ...
        private var cancellableSet = Set<AnyCancellable>()
        
        override init() {
            super.init()
            
            objectWillChange
                .sink { super.objectWillChange.send() }
                .store(in: &cancellableSet)
        }
    }
    
    0 讨论(0)
  • 2020-12-08 03:43

    This happens also when your class is not directly subclass ObservableObject:

    class YourModel: NSObject, ObservableObject {
    
        @Published var value = false {
            willSet {
                self.objectWillChange.send()
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题