I have this code for authentication in a SwitUI application. SessionStore
is an observable object which is injected into the main entry point of the app. When the sign is success then navigate to the dashboard. The isLogin
variable changes but redirect does not happen. I don't understand what am not doing wrong.
// Main View
struct MyApp: App {
@StateObject var session = SessionStore()
init() {
FirebaseApp.configure()
}
var body: some Scene {
WindowGroup {
SplashScreenView()
.environmentObject(session)
}
}
}
// Splashscreen view which redirects to sign in or dashboard
struct SplashScreenView: View {
@StateObject var session = SessionStore()
var body: some View {
Group {
if session.isLogedIn {
DashboardScreen()
} else {
SignInScreen()
}
}
}
}
// Session Store
class SessionStore: ObservableObject {
@Published var session: User?
@Published var profile: UserProfile?
@Published var isLogedIn = false
private var profileRepository = UserProfileRepository()
func signUp(email: String, password: String, firstName: String, lastName: String, city: String, completion: @escaping (_ profile: UserProfile?, _ error: Error?) -> Void) {
Auth.auth().createUser(withEmail: email, password: password) { (result, error) in
if let error = error {
print("Error signing up: \(error)")
completion(nil, error)
return
}
guard let user = result?.user else { return }
print("User (user.uid) signed up.")
let userProfile = UserProfile(uid: user.uid, firstName: firstName, lastName: lastName, city: city)
self.profileRepository.createProfile(profile: userProfile) { (profile, error) in
if let error = error {
print("Error while fetching the user profile: \(error)")
completion(nil, error)
return
}
self.profile = profile
completion(profile, nil)
}
}
}
func signIn(email: String, password: String, completion: @escaping (_ profile: UserProfile?, _ error: Error?) -> Void) {
Auth.auth().signIn(withEmail: email, password: password) { [self] (result, error) in
if let error = error {
print("Error signing in: \(error)")
completion(nil, error)
return
}
guard let user = result?.user else { return }
print("User \(user.uid) signed in.")
self.isLogedIn = true
self.profileRepository.fetchProfile(userId: user.uid) { (profile, error) in
if let error = error {
print("Error while fetching the user profile: \(error)")
completion(nil, error)
return
}
self.profile = profile
completion(profile, nil)
}
}
}
func signOut() {
do {
try Auth.auth().signOut()
self.session = nil
self.profile = nil
} catch let signOutError as NSError {
print("Error signing out: \(signOutError)")
}
}
}
// In my view I call the method and update the log-in status or sign up. Which changes the state but does not redirect
struct SignInScreen: View {
@ObservedObject var sessionStore = SessionStore()
var body: some View {
Button(action:signIn, label: {
Text("Sign In")
})
}
func signIn() {
loading = true
error = false
sessionStore.signIn(email: signInViewModel.emailAddress, password: signInViewModel.password) { (profile, error) in
if let error = error {
print("Error when signing up: \(error)")
return
}
sessionStore.isLogedIn = true
}
}
}
CodePudding user response:
I see you are using firebase. If you need to check authentication status it would be easy to do it this way.
Your main entry view
struct MyApp: App {
@StateObject var session = SessionStore()
init() {
FirebaseApp.configure()
}
var body: some Scene {
WindowGroup {
SplashScreenView()
.environmentObject(session)
}
}
}
In your session store add this code to check if the current is signed in using FirebaseAuth
and not doing it manually.
class SessionStore: ObservableObject {
// Previous code
var isSignIn: Bool {
Auth.auth().currentUser?.uid != nil
}
}
In your splash screen check for isSignIn
.
struct SplashScreenView: View {
@EnvironmentObject var session: SessionStore
var body: some View {
Group {
if session.isSignIn {
DashboardScreen()
} else {
SignInScreen()
}
}
}
}
CodePudding user response:
You have to use @EnvironmentObject var session: SessionStore instead of @StateObject in SplashScreenView.
CodePudding user response:
The goal is to have only one instance of your SessionStore(). Here, you redefined it twice: @StateObject var session = SessionStore()
In your MyApp, do
@StateObject var session = SessionStore()
and in others views, do
@EnvironmentObject var session: SessionStore
and, you forgot to pass your environmentObject to your SignInScreen
.environmentObject(session)