4

Is it possible to use EnvironmentObject in the app file for a new SwiftUI 2.0 project?
I'm using the following code and I get the error:

Fatal error: No ObservableObject of type Authentication found. A View.environmentObject(_:) for Authentication may be missing as an ancestor of this view.: file SwiftUI, line 0

I'm assuming this error is because I haven't used the .environmentObject modifier in any view yet and so the UserAuth has not yet been placed in the environment.

But I want this loginStatus to be available anywhere in my app and to be able to use the function getLoginStatus also anywhere in my app on one common instance of the UserAuth Class.

I was hoping to put this in my rootView and have it apply to all views but not sure how to do this with the structure I have below in my app file.

enum LoginStatus {
    case signedIn
    case signedOut
}

import SwiftUI

@main
struct ExampleApp: App {

    @EnvironmentObject var userAuth: UserAuth

    init() {
        userAuth.getLoginStatus()
    }
    
    var body: some Scene {
        WindowGroup {
            switch userAuth.loginStatus {
            case .signedIn:
                Text("Signed In")
            case .signedOut:
                Text("Signed Out")
            }
        }
    }
}

import SwiftUI

class UserAuth: ObservableObject {
    
    @Published var loginStatus: LoginStatus = .undetermined
    
    func getLoginStatus() {
        // asynchrounously set loginStatus
    }
}
1
  • For that why don't you make Singleton class to access your data from anywhere in App to whole lifecycle. Commented Mar 25, 2021 at 7:55

2 Answers 2

6

You can use it as regular property at that level and pass as environment down into view hierarchy if/when needed, like

struct ExampleApp: App {

    let userAuth = UserAuth()     // << here !!
    
    var body: some Scene {
        WindowGroup {
            switch userAuth.loginStatus {
            case .signedIn:
                ContentView()
                   .environmentObject(userAuth)    // << here !!
            case .signedOut:
                Text("Signed Out")
            }
        }
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

the problem with this approach is that I would need to pass it around to all the other views to make it global with I really don't want to do if I can avoid that. I want there to be only one instance of this class or else the loginStatus variable wouldn't make sense.
1) The instance is the only one because it is, as you see, at App level. 2) Injecting it via environmentObject(userAuth) make that instance available for all sub-view hierarchy, if you have many views inside WindowGroup just move its content into separated view and inject environment object to that one view (which is most usually is a root ContentView).
I see. that makes sense and I'm very anxious to get it working. my only problem with this now is that app does not observe changes to the loginStatus var in userAuth. Is there anyway to make that happen?
can I change let userAuth = UserAuth() to @ObservedObject var userAuth = UserAuth()
it seems that works. I've changed it to ObservedObject in the app file and everything works very well. thanks very much for the explanation
|
1

You need to inject UserAuth from root view, and access your object in View with @EnvironmentObject property.

import SwiftUI

@main
struct ExampleApp: App {

    var body: some Scene {
        WindowGroup {
            MainView().environmentObject(UserAuth())
        }
    }
}


class UserAuth: ObservableObject {
    
    @Published var loginStatus: LoginStatus = .undetermined
    
    func getLoginStatus() {
        // asynchrounously set loginStatus
    }
    
    enum LoginStatus {
        case undetermined
    }
}

struct MainView:View{
    var body: some View{
        Text("")
    }
}

struct MainView1:View{
    
    @EnvironmentObject var userAuth: UserAuth
    
    var body: some View{
        Text("")
    }
}

1 Comment

I'm familiar with this approach and mentioned it in the question. I can add the environmentObject to any other view. I just want to know if there is a way to add it to the WindowGroup or somehow make it available to all views in the app. In the above structure there are basically two main views. One for the signed in fork and one for the not signed in fork. Using your approach the main view is one fork.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.