CocoaUser
CocoaUser

Reputation: 1429

Confusing about @EnvironmentObject in SwiftUI

I'm trying practice @EnvironmentObject tool in SwiftUI.
the share object User init in ContentView.
When I switch from ContentView to NameView work very well.
But, from NameView to AgeView is failed. (take a look error message and Code session, please)

Error message

Fatal error: No ObservableObject of type User found.

Could someone give me advice? Do I miss something? thx

Code

init "User" type in contentView
NavigationView

+-----------+                   +--------+                   +-------+
|ContentView+--NavigationLink-->|NameView+--NavigationLink-->|AgeView|
+-----------+                   +--------+                   +-------+
final class User: ObservableObject {
    @Published var name: String
    @Published var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

struct ContentView: View {
    var user: User = User(name: "SomeName", age: 44)
    var body: some View {
        NavigationView {
            VStack {
                Text("name: \(self.user.name) age: \(self.user.age)")
                NavigationLink(destination: NameView().environmentObject(self.user)) {
                    Text("Show Name")
                }
            }
        }
    }
}

struct NameView: View {
    @EnvironmentObject var user: User
    var body: some View {
        VStack {
            Text("name: \(self.user.name)")
            NavigationLink(destination: AgeView()) {
                Text("Show Age")
            }
        }
    }
}

struct AgeView: View {
    @EnvironmentObject var user: User
    var body: some View {
        VStack {
            Text("age: \(self.user.age)")
        }
    }
}

Upvotes: 3

Views: 3482

Answers (2)

Tobias Hesselink
Tobias Hesselink

Reputation: 1657

Hope this helps you with the following example:

We have our model which is Observable and has all it's variables Published:

class A: ObservableObject {
    @Published var id: String = ""
    @Published var value: String = ""
}

When using it as an EnvironmentObject make sure you'd set this in the SceneDelegate.swift as follows:

let example = Example()

    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: example.environmentObject(A()))
        self.window = window
        window.makeKeyAndVisible()
    }

Then we can use this EnvironmentObject in all Views:

struct Example: View {

    @EnvironmentObject var obj: A

    var body: some View {
        Button(action: {
            self.obj.value = "Hi im changed"
        }) {
            Text("Change me")
        }
    }
}

Sometimes when you want to update data in this object, xCode may give you an error about 'No EnvironmentObject set for ... classname etc.', in this case you need to pass an EnvironmentObject to the destination view:

DestinationView().environmentObject(self.A)

Hope this helps, goodluck!

Upvotes: 4

KevinP
KevinP

Reputation: 2738

Your are assigning the EnvironmentObject in your SceneDelegate to ContentView, so all subviews of ContentView automatically can access your User.

Passing it again is therefore unnecessary like here: NameView().environmentObject(self.user)

You only need to pass the environmentObject again if you show a View inside a .sheet modifier because its basically a new independent view.

final class User: ObservableObject {
    @Published var name: String
    @Published var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

struct ContentView: View {
    @EnvironmentObject var user: User
    var body: some View {
        NavigationView {
            VStack {
                Text("name: \(self.user.name) age: \(self.user.age)")
                NavigationLink(destination: NameView()) {
                    Text("Show Name")
                }
            }
        }
    }
}

struct NameView: View {
    @EnvironmentObject var user: User
    var body: some View {
        VStack {
            Text("name: \(self.user.name)")
            NavigationLink(destination: AgeView()) {
                Text("Show Age")
            }
        }
    }
}

struct AgeView: View {
    @EnvironmentObject var user: User
    var body: some View {
        VStack {
            Text("age: \(self.user.age)")
        }
    }
}

Upvotes: 0

Related Questions