Search code examples
iosswiftinheritanceswiftuiios13

@Published property wrapper not working on subclass of ObservableObject


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.

It seems that the @published property wrapper is ignored when using a subclass. Does anyone know if this is expected behaviour and if there is a workaround?

I'm running iOS 13 Beta 8 and xCode Beta 6.

Here is an example of what I'm seeing. When updating the TextField on MyTestObject the Text view is properly updated with the aString value. If I update the MyInheritedObjectTextField the anotherString value isn't updated in the Text view.

import SwiftUI

class MyTestObject: ObservableObject {
    @Published var aString: String = ""

}

class MyInheritedObject: MyTestObject {
    @Published var anotherString: String = ""
}

struct TestObserverWithSheet: View {
    @ObservedObject var myTestObject = MyInheritedObject()
    @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)")
            }
        }
    }
}

Solution

  • 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)")
                    }
                }
            }
        }