I am trying to get my head around sharing data with multiple views using @Obervable protocol but having trouble making it work.
I have created a simple app to demonstrate the issue I am having, which you will see. It comprises of the:
The trouble I am having is passing the data through to the ‘SecondView’ All works fine within the ContentView, but outside that, no good.
Can someone have a look at this simple code and tell me where I have got it all wrong.
Pete
DemoApp.swift
import SwiftUI
@main
struct DemoApp: App {
@State private var data = ModelData()
var body: some Scene {
WindowGroup {
ContentView()
.environment(data)
}
}
}
ContentView.swift
import SwiftUI
struct ContentView: View {
@Bindable var data = ModelData()
var body: some View {
NavigationStack {
VStack {
TextField("Value", text: $data.value)
Text("\(data.value)")
NavigationLink {
SecondView()
.onAppear() {
data.Compute()
}
} label: {
Text("Button")
}
}
}
}
}
#Preview {
ContentView()
}
Model.swift
import Foundation
import Observation
@Observable
class ModelData {
var value = ""
func Compute() {
print(self.value)
}
}
SecondView.swift
import SwiftUI
struct SecondView: View {
var data = ModelData()
var body: some View {
Text("You Typed: \(data.value)")
}
}
#Preview {
SecondView()
}
RESULTS AFTER CHANGE
I made the suggested changes and they resolved the issue, but now, as soon as I entered: @Environment (ModelData.self) var data
in the 'ContentView', I am now unable to preview the View.
I end up receiving this fatal error:
Any ideas ?
Currently you have multiple ModelData()
that have no relations to each other. You need to pass a single source of truth, the ModelData()
in your DemoApp
to the other views.
So try this approach:
import Foundation
import SwiftUI
struct ContentView: View {
@Environment(ModelData.self) var data // <--- here
var body: some View {
@Bindable var data = data // <--- here
NavigationStack {
VStack {
TextField("Value", text: $data.value)
Text("\(data.value)")
NavigationLink {
SecondView()
.onAppear() {
data.Compute()
}
} label: {
Text("Button")
}
}
}
}
}
@Observable
class ModelData {
var value = ""
func Compute() {
print(self.value)
}
}
struct SecondView: View {
@Environment(ModelData.self) var data // <--- here
var body: some View {
Text("You Typed: \(data.value)")
}
}
See this link Managing model data in your app
EDIT-1:
Alternatively if you want to edit the data, you could use this setup for SecondView
struct SecondView: View {
@Bindable var data: ModelData
var body: some View {
Text("You Typed: \(data.value)")
TextField("", text: $data.value).border(.red)
}
}
And in ContentView
call SecondView(data: data)