No ObservableObject of type AuthViewModel found, but only on Mac

Hi.

I am very new to SwiftUI and still trying to learn.

I just encountered a very weird problem that I can't figure out. I have an iPad application that runs on Apple Silicon Macs as "Designed for iPad"; for some reason, this issue only comes up when running it on a Mac, even if the code is exactly the same.

This is the view that's causing issues:

struct ProfileEditView: View {

    @EnvironmentObject var mainModel: MainViewModel
    @EnvironmentObject var authModel: AuthViewModel

    [some other stuff]

    var body: some View {
        GeometryReader { geo in
            VStack(spacing: 20) {

                navigationBar()
                    .padding(.horizontal, 3)

                if authModel.user != nil {
                    VStack(alignment: .leading, spacing: 30) {

                        PhotosPicker(selection: self.$imageSelection,
                                     matching: .images,
                                     preferredItemEncoding: .compatible) {

                            ProfilePicView(editIconShown: true)
                        }
                                     .disabled(authModel.profilePicIsLoading)
                                     .padding(.horizontal, geo.size.width * 0.3)
                                     .padding(.bottom)

                        VStack(spacing: 10) {
                            if let error = self.error {
                                Text(error)
                                    .foregroundStyle(Color.red)
                                    .font(.footnote)
                                    .italic()
                            }

                            SettingsTextFieldView(String(localized: "Your name:"),
                                                  value: self.$name) {

                                if nameIsValid {
                                    authModel.updateName(self.name)
                                }
                            }
                        }

                        Spacer()

                        actions()
                    }
                    .padding(.horizontal, 5)
                    .padding(.top)
                    .onAppear {
                        self.name = authModel.user!.name
                    }
                }
            }
            .padding()
        }
    }
}

Obviously, I am injecting the ViewModels instances at the app entry point:

@main
struct MyApp: App {

    @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate

    @StateObject var authViewModel: AuthViewModel = AuthViewModel()
    @StateObject var mainViewModel: MainViewModel = MainViewModel()
    @StateObject var statsViewModel: StatsViewModel = StatsViewModel()
    @StateObject var purchaseViewModel: PurchaseViewModel = PurchaseViewModel()

    @State var revenueCatIsConfigured: Bool = false

    init() {

        // MARK: Firebase configuration
        FirebaseConfiguration.shared.setLoggerLevel(.min)
        FirebaseApp.configure()

        // MARK: RevenueCat configuration
        if let uid = AuthViewModel.getLoggedInUserID() {

            Purchases.logLevel = .error
            Purchases.configure(withAPIKey: "redacted", appUserID: uid)

            self.revenueCatIsConfigured = true
        }
    }

    var body: some Scene {
        WindowGroup {
            MainView()
                .environmentObject(authViewModel)
                .environmentObject(mainViewModel)
                .environmentObject(statsViewModel)
                .environmentObject(purchaseViewModel)
        }
    }
}

And lastly, ProfileEditView that is causing issues, is a subview of MainView:

MainView has a switch statement, based on the selected tab; one of the possible views is SettingsView, which can show ProfileEditView as a sheet modifier.

For some reason, only when running on mac, I get the error

Thread 1: Fatal error: No ObservableObject of type AuthViewModel found. A View.environmentObject(_:) for AuthViewModel may be missing as an ancestor of this view.

This will come up every time I reference authModel in this view alone.

Both the iPad and iPhone versions work just fine, and again, the code is exactly the same, since it's a "Designed for iPad".

I don't even know how to troubleshoot the issue.

Answered by Dave_0111 in 765909022

For some reason, passing again the view model instance from the parent view works:

struct SettingsView: View {

    @EnvironmentObject var mainModel: MainViewModel
    @EnvironmentObject var authModel: AuthViewModel
    @EnvironmentObject var purchaseModel: PurchaseViewModel

    [some stuff]

    var body: some View {
        GeometryReader { geo in
            [some stuff]
        }
        .sheet(isPresented: self.$isProfileEditPresented) {
            ProfileEditView(isPresented: self.$isProfileEditPresented)
                .environmentObject(authModel)
        }
    }
}

I don't see why this is necessary, since ProfileEditView is a child View to SettingsView, and therefore should already have access to the environment object, but passing it again solved the problem.

You need to define a class where you publish your data, similar to the below. Perhaps you have done this already as you say it works on the iPhone and iPad.

class AuthViewModel: ObservableObject {
    @Published var profilePicIsLoading = false
    etc...
}

Hi. Yes I have it. That view model is widely used across the app and works fine everywhere else (Mac included), but gives issues only on that single view, and only on Mac.

Accepted Answer

For some reason, passing again the view model instance from the parent view works:

struct SettingsView: View {

    @EnvironmentObject var mainModel: MainViewModel
    @EnvironmentObject var authModel: AuthViewModel
    @EnvironmentObject var purchaseModel: PurchaseViewModel

    [some stuff]

    var body: some View {
        GeometryReader { geo in
            [some stuff]
        }
        .sheet(isPresented: self.$isProfileEditPresented) {
            ProfileEditView(isPresented: self.$isProfileEditPresented)
                .environmentObject(authModel)
        }
    }
}

I don't see why this is necessary, since ProfileEditView is a child View to SettingsView, and therefore should already have access to the environment object, but passing it again solved the problem.

I had a very similar odd issue with my app failing only in macOS with compile time error in Xcode 15.4:

Thread 1: Fatal error: No Observable object of type MyClass found. A View.environmentObject(_:) for MyClass may be missing as an ancestor of this view.

It seems that like you, I had to reinject the object in the view hierarchy - apparently because the view in fullScreenCover would otherwise not inherit the view when running on macOS

    var body: some View {
        TabView {
            ...
        }
        .fullScreenCover(isPresented: $shouldShowMyView) {
            MyView()
            .environment(myClassInstance)
        }
    }

Your post helped me resolve the issue - thank you!

FB13851806 (.fullScreenCover closure content does not inherit environement)

I have the same issue with my app on the iPad version on a mac :-(

Very annoying .. but the additional

.environment(myClassInstance)

is a workaround

Thanks ;-)

No ObservableObject of type AuthViewModel found, but only on Mac
 
 
Q