Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

In SwiftUI #Preview is crashing when using @Environment property wrapper (Observable Macro)

I am using the Observable macro and when I use @Environment property wrapper to instance my model the preview stop working. Sample code below

import SwiftUI
import Observation

@Observable class Library {
    // ...
}

Now in my main app I created an instance of Library and add that instance to the environment

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


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

Now if I want to retrieve the Library instance from any view using the @Environment property wrapper the preview stop working completely (Assume the BoolView actually exist)

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

struct LibraryView: View {
    @Environment(Library.self) private var library
    
    var body: some View {
        List(library.books) { book in
            BookView(book: book)
        }
    }
}

#Preview {
    LibraryView()
}

If I go back and don’t use the Observable macro and just use the ObservableObject with @StateObject, .environemntObject and @EnvironmentObject the #Preview work fine.

This Observable macro is "related" new.
Any idea why this is happening? Is there any workaround? I am working with Xcode Version 15.2. Thanks in advance for any kind of help!

>Solution :

I’ve found out why. When you use the new Observation framework you need to declare this struct along with its extension (usually calling it ClassNameKey) this way:

private struct LibraryKey: EnvironmentKey {
    static var defaultValue: Library = Library()
}

extension EnvironmentValues {
    var library: Library {
        get { self[LibraryKey.self] }
        set { self[LibraryKey.self] = newValue }
    }
}

Then in your App struct you inject it using the path to it like this:

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


    var body: some Scene {
        WindowGroup {
            LibraryView()
                .environment(\.library, library) // <-- Do it this way, using the path
        }
    }
}

And in any view you want to use your dependency you call it the same way, using the path: @Environment(\.library) private var library.
As for the previews, the dependency need to be injected this way:

#Preview {
    ContentView()
        .environment(\.library, Library())
}

If you want to know more go here: Observation Framework
Tell me if that worked for you!

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading