fuzzygoat
fuzzygoat

Reputation: 26223

Using the new @StateObject to pass model to multiple views?

If you wanted to pass a model between multiple views can you use @StateObject multiple times (see example below) so that each view can modify the object? I appreciate that this would be better done using @EnvironmentObject but was just trying to understand how this new property wrapper works in this situation.

// MARK: - MODEL
class Model: ObservableObject {
   @Published var temperature = 27.5
}

.

// MARK: - APP.SWIFT
@main
struct SwiftUI_DataFlow_005App: App {
    @ObservedObject var model = Model()
    var body: some Scene {
        WindowGroup {
            ContentView(model: model)
        }
    }
}

.

// MARK: - CONTENT VIEW
struct ContentView: View {
    @StateObject var model: Model
    var body: some View {
        VStack {
            Text("\(model.temperature)")
            AnotherView(model: model)
        }
    }
}

// MARK: - ANOTHER VIEW
struct AnotherView: View {
    @StateObject var model: Model
    var body: some View {
        Text("\(model.temperature)")
    }
}

Upvotes: 7

Views: 7690

Answers (2)

Asperi
Asperi

Reputation: 257573

@StateObject is source of truth, so it must be created in one place, so (schematically as you provided screenshot, but not a code to modify)

in App >
  @StateObject private var model = Model()

  ...

  ContentView().environmentObject(model)

  ...

in ContentView >

  @EnvironmentObject var model: Model

  ...

in AnotherView >

  @EnvironmentObject var model: Model

Upvotes: 9

fuzzygoat
fuzzygoat

Reputation: 26223

A small expansion based on the accepted answer so future readers can gain a little more insight.

1 - @ObservedObject

APPLE: Managing Model Data in Your App

It would seem from the Apple docs that one solution would be to use @ObservedObject on all three views.

// MARK: - CONTENT VIEW
struct ContentView: View {
    @ObservedObject var model: Model
    var body: some View {
        VStack {
            Text("\(model.temperature)")
            AnotherView(model: model)
        }
    }
}

.

// MARK: - ANOTHER VIEW
struct AnotherView: View {
    @ObservedObject var model: Model
    var body: some View {
        Text("\(model.temperature)")
    }
}

2 - @EnvironmentObject

Another solution @asperi and by far the simplest is to create the model in app.swift as @StateObject and then inject the object into the environment. This then means you don't have to pass the model through the hierarchy, but rather access it as needed using @EnviromentObject.

// MARK: - APP.SWIFT
@main
struct SwiftUI_DataFlow_005App: App {
    @StateObject var model = Model()
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(model)
        }
    }
}

.

// MARK: - CONTENT VIEW
struct ContentView: View {
    @EnvironmentObject var model: Model
    var body: some View {
        VStack {
            Text("\(model.temperature)")
            AnotherView()
        }
    }
}

.

// MARK: - ANOTHER VIEW
struct AnotherView: View {
    @EnvironmentObject var model: Model
    var body: some View {
        Text("\(model.temperature)")
    }
}

Upvotes: 7

Related Questions