Using @EnvironmentObject to handle Log In / Log Out condition in SwiftUI
So I started using SwiftUI this last week and thought I would write about something I have been able to make over the brief time I have used it for. While SwiftUI makes designing new applications extremely easy; I couldn’t help but notice how much Apple has simplified UI components. Sometimes a bit too much. So I have been able to make a simple on boarding flow and a log in page. I have not integrated the app with Firebase as yet, but I just wanted to see how to go about creating a flow where when user taps on a button, he / she is taken to the main app (tab bar View).
SwiftUI framework has numerous property wrappers and understanding each one and knowing which to use and when to use should be your main target.
In this article, I will be using @EnvironmentObject
. EnvironmentObject is combination of @ObjectBinding
(another wrapper) and Singleton
. In principle, it should be injected in the initial view of the view hierarchy. Something which is done in SceneDelegate
. Once done, this property will be usable throughout any view that your initial view presents or hosts.
EnvironmentObject
can also be used to share data - but it’s very easy to over use this everywhere. Be very careful when and where you use this property wrapper.
After you have imported Combine
, create a class that will conform to ObservableObject
protocol and create a boolean property called loggedIn
.
@Published var loggedIn : Bool = false
@Published
annotation means that this property stores both a value (false/true) in this case and a publisher which will send a message to any views that there has been a change to the value.
This is how Apple explains @Published
as:
Properties annotated with `@Published` contain both the stored value and a publisher which sends any new values after the property value has been sent. New subscribers will receive the current value of the property first.
So this is our class now:
class UserSettings: ObservableObject {
@Published var loggedIn : Bool = false
}
Let’s create an instance of this class and inject this to our first view that we will load up. In our case it will be called StartView()
So in SceneDelegate.swift
file, look for this method:
and add this in:
The StartView()
now contains a property called settings and it is annotated as @EnvironmentObject:
In StartView
we are checking the loggedIn
property and if it is false will show ContentView()
else it will show TabbarView()
. Since loggedIn
variable is @published,
it will grab the new value and then send the new value to any view that subscribes (reads) to this property and handles the condition for it.
UserSettings is now shared and can be used anywhere in the app, in as many views as you want. Here we will add UserSettings type property in LogInView()
, where we when the user taps on the log in button we will toggle the value of the isLoggedIn
and this will run the if block above in the StartView.
Let’s revise this:
We injected an instance of UserSettings in StartView(). This means UserSettings instance is available in any view that StartView hosts or presents, or is a descendent of StartView(). Then by using the @EnvironmentObject
property wrapper we are able to access that instance in any view.
We access the instance in StartView()
and LogInView()
. In LogInView, we write a value to isLoggedIn
bool = true. On doing that, the property (being @Published
) notifies any views to refresh itself - so in StartView()
we are reading the value of this isLoggedIn
property and showing the TabbarView()
.
We can access this instance in TabbarView()
as well and create a Log Out button. On pressing Log Out
, we set the isLoggedIn
bool to false. In doing that it again refreshes any view and goes to ContentView()
.
Here is a quick diagram to sum it all up:
Here is the app in action: