I am working on a SwiftUI app that utilizes AWS Amplify/Cognito for its authentication. I have created a session object that keeps track of whether a user is authenticated. This session object is an ObservableObject that is loaded into environmentObject and accessed by different views. It has a @Published property called isLoggedIn. Within this session object, a listener has been created to capture changes in authentication state which update the value of isLoggedIn. The code compiles and runs as expected but the following run time warning is generated when trying to update the isLoggedIn property when a user logs in:
Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.
My question is what is the appropriate way to capture the authentication state and set the value so that is it published via the environmentObject mechanism of SwiftUI? Could I move my listener to AppDelegate and update the Session contained in the environmentObject from there? If so, how do you access environmentObjects outside of views? Is there another cleaner way of capturing the value and introducing it into SwiftUI's environmentObjects? I know I can make an API call to Cognito/Amplify to determine the authentication state of the user but that doesn't fit into the reactive model of SwiftUI or at least I don't see how to fit it in :).
Shown below is the code involved in this process. The first code snippet is for the Session object. The second one shows the session object being put into an enviromentObject within the SceneDelegate. The last snippet shows a view where the object if accessed to make a rendering decision.
Session.swift
class Swift:ObservableObject {
@Published var firstName: String = ""
@Published var lastName: String = ""
@Published var isLoggedIn: Bool = false
init(){
AWSMobileClient.default().addUserStateListener(self) { (userState, info) in
switch (userState) {
case .guest:
self.isLoggedIn = false
case .signedOut:
self.isLoggedIn = false
case .signedIn:
self.isLoggedIn = true
case .signedOutUserPoolsTokenInvalid:
self.isLoggedIn = false
case .signedOutFederatedTokensInvalid:
self.isLoggedIn = false
default:
self.isLoggedIn = false
}
}
}
SceneDelegate.swift
...
let currentSession = Session()
let mainTabView = MainTabView().environmentObject(currentSession)
...
View
struct MyView: View {
@EnvironmentObject var currentSession: Session
var body: some View {
VStack{
if (self.currentSession.isLoggedIn) {
Spacer()
Text("Logged In Content")
Spacer()
}
else{
LoginJoinView()
}
}
}
}