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.

1
  • Have it as a @StateObject property in Parent and then pass it to the Child view where it is an @ObservedObject property Commented Dec 15, 2021 at 21:41

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

2 Comments

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?
Heh, yeah, that would work too. Maybe I made my first example too complex 🤦‍♂️

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.