1

Suppose I have views like this:

struct Parent: View {
  var data: Int

  var body: some View {
    Child(state: ChildState(data: data))
  }
}

struct Child: View {
  @StateObject var state: ChildState
  var body: {
    ...
  }
}

class ChildState: ObservableObject {
  @Published var property: Int
  ...

  init(data: Int) {
    // ... heavy lifting
  }
}

My understanding is that the init method for Child should not do any heavy computation, since it could be called over and over, by SwiftUI. So heavy lifting should be done in the ChildState class, which is marked with the @StateObject decorator, to ensure it persists through render cycles. The question is, if Parent.data changes, how do I propagate that down so that ChildState knows to update as well?

That is to say, I DO want a new instance of ChildState, or an update to ChildState if and only if Parent.data changes. Otherwise, ChildState should not change.

CaptainStiggz
  • 1,787
  • 6
  • 26
  • 50

1 Answers1

1

You need to make the parent state observable too. I'd do it like this:

class ParentState: ObservableObject {
    @Published var parentData = 0
}

class ChildState: ObservableObject {
    @Published var childData = 0
}

struct Parent: View {
    @StateObject var parentState = ParentState()
    @StateObject var childState = ChildState()

    var body: some View {
        ZStack {
            Color.black

            VStack {
                Text("Parent state \(parentState.parentData) -- click me")
                    .onTapGesture {
                        parentState.parentData += 1
                    }

                Child(parentState: parentState, childState: childState)
            }
        }
    }
}

struct Child: View {
    @ObservedObject var parentState: ParentState
    @ObservedObject var childState: ChildState

    var body: some View{
        Text("Child state \(childState.childData) parentState \(parentState.parentData) -- click me")
            .onTapGesture {
                childState.childData += 1
            }
    }
}

struct ContentView: View {
    var body: some View {
        Parent()
    }
}

Tested with Xcode 13.1

Of course, there's nothing wrong with passing the child state object to the initializer of the child view, the way you've shown it. It will work fine still, I just wanted to make the example as clear as possible.

SaganRitual
  • 3,143
  • 2
  • 24
  • 40
  • Ok, so if I changed line 2 in my example from `var data: Int` to `@State var data: Int`, would that have the desired effect? – CaptainStiggz Dec 15 '21 at 22:37
  • Heh, yeah, [that](https://gist.github.com/SaganRitual/c87debe6c6e3ee1661d99fff45a5569a) would work too. Maybe I made my first example too complex ‍♂️ – SaganRitual Dec 15 '21 at 22:44