Is it possible to retrieve optional @EnvironmentObject

I'm trying to develop an iOS app with SwiftUI supporting iOS15 and above and then I encounter with this issue.

I have a SwiftUI view and call this view from multiple different UI flows. So I want to provide different environment objects depending which flow it is in. I want them optional because depending which environment object I am able to retrieve, I want to modify properties of those environment objects to trigger UI updates. (expect to use "if let" check for optional binding)

Inside that view, I want to retrieve those environment objects as optionals but I can't use @EnvironmentObject because is expects conforming ObservableObject protocol. I also tried to use EnvironmentKey protocol to define custom optional types but this time Xcode gives error as:

"Failed to produce diagnostic for expression; please submit a bug report (https://swift.org/contributing/#reporting-bugs)"

I really wonder If I am able to reach an optional environment object for iOS15.

Note: I know that with Observation framework I may be able to get optional environment object but I have to update my supported iOS level beginning from iOS17 for the app which I can't do right now.

Answered by DTS Engineer in 795450022

I would suggest you make your environment objects non-optional when accessed through the environment, and instead put the optional state inside your object. Then you can mutate it from any view, and it should work exactly as you expect. For example:


@main
struct MainView: App {
    @StateObject private var photosManager: PhotosManager = PhotosManager()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(photosManager)
        }
    }
}

class PhotosManager: ObservableObject {
    @Published var video: Video?
}

struct Video {
    var id: UUID
    var title: String?
}

struct ContentView: View {
    @EnvironmentObject private var photosManager: PhotosManager

    var body: some View {
        Group {
            if let title = photosManager.video?.title {
                Text("Video: \(title)")
            } else {
                Button("Set video") {
                    photosManager.video = Video(id: UUID(),title: "Video 2")
                }
            }
        }
    }
}
Accepted Answer

I would suggest you make your environment objects non-optional when accessed through the environment, and instead put the optional state inside your object. Then you can mutate it from any view, and it should work exactly as you expect. For example:


@main
struct MainView: App {
    @StateObject private var photosManager: PhotosManager = PhotosManager()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(photosManager)
        }
    }
}

class PhotosManager: ObservableObject {
    @Published var video: Video?
}

struct Video {
    var id: UUID
    var title: String?
}

struct ContentView: View {
    @EnvironmentObject private var photosManager: PhotosManager

    var body: some View {
        Group {
            if let title = photosManager.video?.title {
                Text("Video: \(title)")
            } else {
                Button("Set video") {
                    photosManager.video = Video(id: UUID(),title: "Video 2")
                }
            }
        }
    }
}

Based on your answer I understand and accept that @EnvironmentObject property wrapper is not designed to accept optional values. Instead we can use that environment objects to store optional properties. Thanks.

Is it possible to retrieve optional @EnvironmentObject
 
 
Q