1

Xcode version: 14.2

I am trying to display a list of sports clubs. Since my modelData.clubs is still an empty array when just viewing the preview (modelData.clubs gets its actual value in my contentView, when the app is loaded), I would like it to display an array of default clubs if modelData.clubs.isEmpty. However, when I try to run my code, it gives me the following error in the marked line: Thread 1: Fatal error: No ObservableObject of type ModelData found. A View.environmentObject(_:) for ModelData may be missing as an ancestor of this view.

struct ClubList: View {
    @EnvironmentObject var modelData: ModelData
    @State private var sortByValue = false
    var clubs: [Club] = [Club.default, Club.default, Club.default, Club.default, Club.default]
    
    init() {
        if (!modelData.clubs.isEmpty) { // ERROR IN THIS LINE
            clubs = modelData.clubs
        }
        
        if sortByValue {
            clubs.sort {
                $0.value < $1.value
            }
        } else {
            clubs.sort {
                $0.name < $1.name
            }
        }
    }
        
    var body: some View {
        NavigationView {
            List {
                ForEach(clubs) { club in
                    NavigationLink {
                        ClubDetail(club: club)
                    } label: {
                        ClubRow(club: club)
                    }
                }
            }
        }
    }
}

struct ClubList_Previews: PreviewProvider {
    static var previews: some View {
        ClubList()
            .environmentObject(ModelData())
    }
}

ClubList() gets called in my ContentView:

struct ContentView: View {
    @EnvironmentObject var modelData: ModelData
    // ... //
    
    var body: some View {
        if clubsLoaded {
            ClubList()
                .environmentObject(ModelData())
        } // ... //
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .environmentObject(ModelData())
    }
}

And ContentView gets called in my App:

@main
struct clubsApp: App {
    @StateObject private var modelData = ModelData()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(modelData)
        }
    }
}

I am pretty new to Xcode, but I think I put the environmentObject(ModelData()) everywhere, where it would be required, and I am pretty lost now on what to do.

Any help would be greatly appreciated!

Katharina
  • 21
  • 4
  • I don’t know if this is the cause of the issue but I never declare my @StateObject properties as private, specially not on the app root level (@main). – Joakim Danielson Feb 16 '23 at 11:30
  • 1
    `.environmentObject(ModelData())`, this on the other hand is more likely the issue. You shouldn’t re-declare an EnvironmentObject so remove it from everywhere in your ordinary code except from the first one at the root level. – Joakim Danielson Feb 16 '23 at 11:34
  • 1
    As @JoakimDanielson states, `ClubList().environmentObject(ModelData())` in your `ContentView` is creating a new `ModelData` and passing that into `ClubList`. You probably don't need this as the `environmentObject` is passed down through the view hierarchy, but if you do, It should be `ClubList().environmentObject(modelData)` – Ashley Mills Feb 16 '23 at 12:07

1 Answers1

0

I think I figured it out!

This post here made me realise that my problem was that I was trying to access my modelData in init(), where it is not possible yet. Someone from the link said:

The environment is passed down when the body is called, so it doesn't yet exist during the initialization phase of the View struct.

Thus, I solved my problem by moving

if (!modelData.clubs.isEmpty) { clubs = modelData.clubs }

from init() into its own function which is then called in onAppear of my NavigationView.

Katharina
  • 21
  • 4