In this article we will talk about @EnvironmentObject in SwiftUI and how to use it to handle certain state management situations within our iOS applications.

SwiftUI is all about handling states in order to change or manipulate views. We get three ways to store states in our application.

  1. @State
  2. @ObservedObject
  3. @EnvironmentObject

EnvironmentObject is a way to handle state throughout multiple views in your application. Think of this as a property that you want to keep in sync, no matter on which screen the user is. One good example of this could be the “Play” icon in a music app. You would want to keep its state in sync throughout the entire app.

EnvironmentObject is injected inside the first view or the primary view that hosts other views. In order to create an environmentobject, first create a class and conform it to ObservableObject protocol. Define the properties inside the class and make sure to use @Published keyword before the properties so that these properties can store and publish their changes to the subscribers. 

class MusicPlayerState: ObservableObject {
    @Published var isPlaying: Bool = false
}

Once the class has been formed, we will create an instance of it and inject it inside the primary View. This can be done inside SceneDelegate:

let musicState = MusicPlayerState()
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(musicState))

Now that we have made the class and have injected it’s instance – now we can access this property anywhere inside our app using the @EnvironmentObject notation.

@EnvironmentObject var musicState : MusicPlayerState

Next, let’s quickly create two views – the first screen will contain song cover art, song name and a button (play/pause).

struct ContentView: View {
    @EnvironmentObject var musicState : MusicPlayerState
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: PlayerView()) {
                    Image("cover").renderingMode(.original)
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                        .frame(width: 150, height: 150)
                }
                    
                HStack {
                    Text("I Lost You - Havana")
                    Button(action: {
                        self.musicState.isPlaying.toggle()
                    }) {
                        self.musicState.isPlaying ? Text("Pause") : Text("Play")
                        
                    }
                }
            }.navigationBarTitle("Music Player")
        }
    }
}

The next view will be the PlayerView, and that will contain the name of the song and the same button (play/pause).

struct PlayerView: View {
    @EnvironmentObject var musicState : MusicPlayerState
    var body: some View {
       
        HStack {
            Text("I Lost You - Havana")
            Button(action: {
                self.musicState.isPlaying.toggle()
            }) {
                self.musicState.isPlaying ? Text("Pause") : Text("Play")
            }
        }.navigationBarTitle("Now Playing")
        
    }
}

In both views I am accessing the MusicPlayerState just by simply creating an @EnvironmentObject property. Now in the first View, whenever the user taps on the Play/Pause button, I am accessing the musicState’s isPlaying bool property and toggling it.

self.musicState.isPlaying.toggle()

Now since isPlaying variable is @Published, it will send the new value to its subscriber (which in this case is the button text).

self.musicState.isPlaying ? Text("Pause") : Text("Play")

So the button text switches to “Pause” or “Play” depending on the bool value. The same thing is applied in the second view’s button as well. Now our button’s state is being stored inside the @EnvironmentObject and we can access this state anywhere in our application. Now if we Pause or Play the song from any view, the rest of the View’s are kept in sync.

 

This is one of three ways you can handle states inside your SwiftUI application. Again it’s the state that dictates the UI. @EnvironmentObject is just like a combination of singleton and @ObjectBinding. It’s very easy to use this everywhere inside your app. Make sure you do not over use @EnvironmentObject and use it smartly.

 

Categories: SwiftUI

Leave a Reply

Your email address will not be published. Required fields are marked *