Authentication flow in SwiftUI using a PassthroughSubject

In real apps, authentication flow is very common and handling it may have different approaches. Specially it was pretty tricky in UIKit apps. When it comes to SwiftUI apps, with the use of Combine framework, we can handle authentication flow in few lines of codes and with less effort. Basic idea is listening to a Publisher which can emit a value based on the login status of the user. Normally developers use a token and store it in UserDefaults in the login time and remove it when logout. Anyhow this mechanism is also used in this method. But additionally we will use an emitter to check when to change the View based on login and logout.

You can get the code here. I urge you to refer the code because all of the code is not mentioned below.

https://s27389.pcdn.co/wp-content/uploads/2021/04/could-social-media-networks-pave-the-way-stronger-authentication-1024x440.jpeg

First create a SwiftUI project and create a file called AppManager.swift. It will hold the variables and functions that we will need to emit whether logged in or logged out and to check whether a token is available in UserDefaults. It helps to check whether user is already logged in when opening app after a force quit. Here is the code that needs to be in the AppManager.

import Foundation
import Combine
struct AppManager {
static let Authenticated = PassthroughSubject<Bool, Never>()
static func IsAuthenticated() -> Bool {
return UserDefaults.standard.string(forKey: "token") != nil
}
}

Next we will create files as follows. A small description is given along because all the code is not shown here. Refer the files given in the Github repo mentioned at the begin of the article.

  1. LoginView.swift
    It has two TextFields to get inputs (email/passowrd) and binded to Publishers in its ViewModel. On the Button click, it will run the loginUser() method in the VM.
  2. LoginViewModel.swift
    This has the logic to check whether login credentials are correct in loginUser() method. For the demo purpose, credentials are hardcoded. If the login is successful, AppManager.Authenticated.send(true) is used. This sends a True value to the PassthroughSubject declared in the AppManager. So any listener is notified. (ContentView Group is listening for changes on this). Also in this method, a UserDefaults value is created with the key token. It is same as the accessToken we use in real apps.
  3. HomeView.swift
    This is the view shown when user is authenticated. This has a Logout button and triggers logoutUser() method in its VM.
  4. HomeViewModel.swift
    nce user press on Logout button, logoutUser() is called. It removed the token from the UserDefaults and emits a false value to AppManagers Authenticated PassthroughSubject.

Next we’ll look how to use the emitter in the ContentView where the app’s entry view.

struct ContentView: View {
@State var isAuthenticated = AppManager.IsAuthenticated()
var body: some View {
Group {
isAuthenticated ?
AnyView(HomeView()) :
AnyView(LoginView())
}
.onReceive(AppManager.Authenticated, perform {
isAuthenticated = $0
})
}
}

Here all Views are embedded in a Group. A group can listen to Publishers such as a PassthroughSubject in the onReceive performer and do actions. Here it listens to the AppManager.Authenticated PassthroughSubject of type Bool. When a values is received, it performs the block inside. Here it takes the argument as $0 and stores it in isAuthenticated. Because it is a State variable, UI refreshes when the state value is changed. Initially isAuthenticated value is gained from the AppManagers IsAuthenticated() method according to the UserDefault value(token).

Now Run the project and try to login and logout :)

Auth flow

I am an iOS developer and thought of writing day today Swift things for the community :)