Is it possible to get send a @Published variable value through a chain of classes

I'm trying to update the ContentView with the score as you press Increase Score. I want the num in the Score class to update score in UserSettings so then score can update settings in ContentView. So far it's not working and I think I've misunderstood how to use these property wrappers, could someone help with explaining what it takes to make this work if possible?

class Score: ObservableObject {   
    @Published var num = 0   
}

class UserSettings: ObservableObject {
    @Published var score = Score()
}

struct ContentView: View {
    @StateObject var settings = UserSettings()

    var body: some View {
        VStack {
            Text("Your score is \(settings.score.num)")
            Button(action: {
                self.settings.score.num += 1
            }) {
                Text("Increase Score")
            }
        }
    }
}

This question is about SwiftUI. I'd suggest that you ask over Apple Developer Forums instead. I'd better attract the people who are interested in the topic.

That said, there is a distinction between the mutating (those that you mark mutating), and signalling (the objectWillChange sends a message). Here's what they do use to trigger the updates.

  • View updates when
    • its StateObject wrapped values signal, or
    • its State wrapped values mutate,
  • ObservedObject signals when
    • its Published wrapped value* mutate, or
    • manually using objectWillChange publisher, or
    • overridden by implementing your own objectWillChange publisher

So, looking at your code (note the bold text):

  • ContentView updates when setting signals,
  • setting signals when state.score mutates,
  • state.score signals when state.score.num mutates.

So when you mutate state.score.num, the setting misses the mutation as state.score only signals the change, not mutating itself.

One way to resolve this is to make state.score a struct, so that when you mutate state.score.num, you also mutate state.score:

struct Score {
    var num = 0
}

* Technically, you can't access wrappedValue from the wrapper itself, only from the wrapped declaration.

PS

You should use triple tick marks instead of indentation for code block:

```
Like this
```

Thanks for your response @Lantua

I was able to get it working as you suggested. Both by turning the classes into structs and by manually sending an update after the Score.num is changed inside an additional function incrementNum in UserSettings. It worked as follows:

class Score {
    fileprivate(set) var num = 0
}

class UserSettings: ObservableObject {
    
    let score = Score()
    
    func incrementNum() {
        score.num += 1
        objectWillChange.send()
    }
}

struct ContentView: View {
    
    @StateObject var settings = UserSettings()

    var body: some View {
        VStack {
            Text("Your score is \(settings.score.num)")
            Button("Increase Score") {
                self.settings.incrementNum()
            }
        }
    }
}