How to debug missing environment values

I have a custom environment key defined:

struct MyCustomKey: EnvironmentKey {
    static let defaultValue = MyCustom.dummy
}

extension EnvironmentValues {
    var myCustom: MyCustom {
        get { self[MyCustomKey.self] }
        set { self[MyCustomKey.self] = newValue }
    }
}

and pass it on the top level:

let myCustomInstance = MyCustom()

@main
struct ThygeApp: App {
    var body: some Scene {
        WindowGroup {
            MainWindow()
                .environment(\.myCustom, myCustomInstance)
        }
    }
}

I really do not want it to use the default instance ever, I want it to be specified explicitly.

Is there a way to have it fail on use if it is not specified in the environment? I have made it so that the .dummy instance does a fatalError() and that works so far as it halt the app. But there are no hints in the stack track as to where the offending code is - where do I use the environment value without passing it like I do here

  .environment(\.myCustom, myCustomInstance)

The only solution to find the offending use is to add an assert() to all func defined in MyCustom. That seems cumbersome and error prone.

Is there another way?

Answered by DTS Engineer in 797169022

@nicolai One way is to use an Environment to get an observable object from a view’s environment, Get an observable object article has literature about this.

Basically with this approach, you must set the object in the environment using the the object itself or a key path.

@Observable
class Library {
    var books: [Book] = [Book(), Book(), Book()]


    var availableBooksCount: Int {
        books.filter(\.isAvailable).count
    }
}


@main
struct BookReaderApp: App {
    @State private var library = Library()


    var body: some Scene {
        WindowGroup {
            LibraryView()
                .environment(library)
        }
    }
}

struct LibraryView: View {
    @Environment(Library.self) private var library


    var body: some View {
        // ...
    }
}

@nicolai One way is to use an Environment to get an observable object from a view’s environment, Get an observable object article has literature about this.

Basically with this approach, you must set the object in the environment using the the object itself or a key path.

@Observable
class Library {
    var books: [Book] = [Book(), Book(), Book()]


    var availableBooksCount: Int {
        books.filter(\.isAvailable).count
    }
}


@main
struct BookReaderApp: App {
    @State private var library = Library()


    var body: some Scene {
        WindowGroup {
            LibraryView()
                .environment(library)
        }
    }
}

struct LibraryView: View {
    @Environment(Library.self) private var library


    var body: some View {
        // ...
    }
}

Is there a way to have it fail on use if it is not specified in the environment?

In the above example, if Library isn't provided into the environment, it would throw a runtime exception.

However using EnvironmentValues you could provided an optional value which you could nil check at runtime or the compiler throws a runtime exception if you assume the value was provided.

private struct MyEnvironmentKey: EnvironmentKey {
    static let defaultValue: String? = nil
}


extension EnvironmentValues {
    var myCustomValue: String? {
        get { self[MyEnvironmentKey.self]! }
        set { self[MyEnvironmentKey.self] = newValue }
    }
}

struct ContentView: View {
    @Environment(\.myCustomValue) private var text
    
    var body: some View {
        ....
    }
}
How to debug missing environment values
 
 
Q