Apr 27, 2024 iOS

SwiftUI – How to pass EnvironmentObject into View Model

Certainly! When working with SwiftUI, you can pass an EnvironmentObject into a view model to share data across your app. Here are the steps to achieve this:

  1. Create an Environment Object:
    • First, create an environment object that holds the data you want to share. You can do this by defining a class or struct that conforms to the ObservableObject protocol.
    • An example of an environment object might be a UserSettings object that tracks session data like whether a user is logged in or an access token.
  2. Inject the Environment Object into Your View:
    • In your SwiftUI view, use the @EnvironmentObject property wrapper to access the environment object.
    • For instance, if you have a UserSettings environment object, you can inject it into your view like this
struct YourView: View {
    @EnvironmentObject var settings: UserSettings
    @ObservedObject var viewModel = YourViewModel()

    var body: some View {
        VStack {
            Text("Hello")
            // Add other views here
        }
        .onAppear {
            self.viewModel.setup(self.settings)
        }
    }
}

Initialize Your View Model with the Environment Object:

  • In your view model (e.g., YourViewModel), create a property to hold the environment object (e.g., settings).
  • Implement a method (e.g., setup(_:)) that takes the environment object as a parameter and assigns it to the view model’s property.
class YourViewModel: ObservableObject {
    var settings: UserSettings?

    func setup(_ settings: UserSettings) {
        self.settings = settings
    }
}
  1. Considerations:
    • Be aware that using this approach, you’ll end up with optionals (since the environment object might not be available immediately).
    • Updates to the environment object won’t automatically trigger view updates unless you explicitly use @ObservedObject within your view model.

In SwiftUI, you can pass an EnvironmentObject into a view model by either injecting it directly into the view model’s initializer or by using property injection. Here’s how you can achieve both approaches:

Direct Injection:

In this approach, you inject the EnvironmentObject directly into the view model’s initializer.

import SwiftUI

class MyViewModel: ObservableObject {
    @EnvironmentObject var userData: UserData

    // Your view model logic here...
}

struct MyView: View {
    @EnvironmentObject var userData: UserData
    @StateObject var viewModel = MyViewModel()

    var body: some View {
        // Use viewModel here
        Text("Hello, \(viewModel.userData.username)")
    }
}

Property Injection:

In this approach, you pass the EnvironmentObject to the view model through a property setter.

import SwiftUI

class MyViewModel: ObservableObject {
    var userData: UserData?

    // Inject EnvironmentObject through a setter
    func inject(userData: UserData) {
        self.userData = userData
    }

    // Your view model logic here...
}

struct MyView: View {
    @EnvironmentObject var userData: UserData
    @StateObject var viewModel = MyViewModel()

    var body: some View {
        // Inject EnvironmentObject into viewModel
        Text("Hello, \(userData.username)")
            .onAppear {
                viewModel.inject(userData: userData)
            }
    }
}

Both approaches have their pros and cons:

  • Direct Injection: This approach is simpler and more straightforward. However, it ties the view model directly to the EnvironmentObject, which may make it less reusable.
  • Property Injection: This approach allows for more flexibility and decoupling between the view model and the EnvironmentObject. However, it requires additional setup to inject the object into the view model.
Index