SwiftUI @Environment* accessibility in View init()

Hi,

I want to use @EnvironmentObject or @Environment to initialise a view model, see below:

struct ExampleView: View {
    @EnvironmentObject
    private var envData: EnvironmentData
    
    @ObservedObject
    var exampleViewModel: ExampleViewModel
    
    init() {
        exampleViewModel = ExampleViewModel(envData: envData)
    }
 
    var body: some View {
        Text(exampleViewModel.value)
    }
}

Currently, this is not possible. To work around this limitation i am using the following approach:

struct ExampleView: View {

    struct Content: View {
        @ObservedObject
        var exampleViewModel: ExampleViewModel
       
        init(envData: EnvironmentData) {
            exampleViewModel = ExampleViewModel(envData: envData)
        }
    
        var body: some View {
            Text(exampleViewModel.value)
        }
    }
    
    @EnvironmentObject
    private var envData: EnvironmentData
 
    var body: some View {
        Content(envData: envData)
    }
}

This adds a lot of boilerplate code - especially when a view has multiple parameters. Is there a better way?

It would be great if you could use syntax similar to the following, and inject environment values using init parameters with a parameter attribute.

struct ExampleView: View {
    @ObservedObject
    var exampleViewModel: ExampleViewModel

    init(@EnvironmentObject envData: EnvironmentData) {
        exampleViewModel = ExampleViewModel(envData: envData)
    }
    
    var body: some View {
        Text(exampleViewModel.value)
    }
}

At the very least, access to a read-only environment init parameter or prop on the view would solve this problem.

Given SwiftUI architecture fits naturally with MVVM, i'd think this would be a common usage pattern. If there isn't a better solution, maybe a pitch could be created to make the environment (and probably @ObservableObject) accessible from the view initialiser.

Interested in feedback, Adam.

Another alternative would be to send in envData from parent, and have parents holding envData.

PS

What does Published do in View? Should it be State or ObservedObject?

It should be @ObservedObject - typo.

Does exampleViewModel change at all if envData is not changing?

PS

You can edit the post, to fix the typo.

By "change" do you mean state changes? If so, then yes - the view model can @Publish state changes that do not originate from envData.

This is nit a limitation but correct by design. If you would push the environment into your model and pass down the model to some view down the view graph, you would be likely to get inconsistency bugs.

Terms of Service

Privacy Policy

Cookie Policy