Maybe this approach may help you:
Three views (just as an example):
- ContentView: It shows either Page1 or Page2, depending on the var
isShowingNewAccountView.
- Page1: It is the page you use to create a new user, it will be shown when
isShowingNewAccountView is false. It calls the function signUp from the AuthenticationViewModel.
- Page2: It is the page that will be opened when a new User has been created and
isShowingNewAccountView is true
One ViewModel (also just as an example)
The authentication is handled in the AuthenticationViewModel. It is handling the functions to signUp, signIn and signOut. It also holds isShowingNewAccountView.
Views
import SwiftUI
import FirebaseFirestore
import Firebase
import FirebaseFirestoreSwift
struct Page1: View {
@EnvironmentObject var authenticationViewModel: AuthenticationViewModel
@State private var buttonPressed = false
@State private var email: String = "sebastian@hello.me"
@State private var password: String = "password"
var body: some View {
VStack(){
Text("Register")
.font(Font.system(size: 24, weight: .bold))
.padding()
.padding(.vertical, 50)
TextField("E-Mail", text: $email)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
SecureField("Password", text: $password)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
Button(action: {
buttonPressed.toggle()
authenticationViewModel.signUp(email: email, password: password){ success, uid in
if success {
print("UID: \(uid)")
} else {
print("Uh-oh")
}
buttonPressed = false
}
}) {
Text(buttonPressed ? "In Progress" : "Sign Up - Create User")
.foregroundColor(buttonPressed ? Color.pink : Color.cyan)
}
}
}
}
struct Page2: View {
@EnvironmentObject var authenticationViewModel: AuthenticationViewModel
var body: some View {
Button(action: {
authenticationViewModel.isShowingNewAccountView.toggle()
}) {
Text("Back to other page")
}
}
}
struct ContentView: View {
@EnvironmentObject var authenticationViewModel: AuthenticationViewModel
var body: some View {
if authenticationViewModel.isShowingNewAccountView {
Page2()
} else {
Page1()
}
}
}
ViewModel for Authentication
import Foundation
import FirebaseFirestore
import Firebase
import FirebaseFirestoreSwift
class AuthenticationViewModel: ObservableObject {
let db = Firestore.firestore()
@Published var isShowingNewAccountView: Bool = false
// Sign Up
func signUp(email: String, password: String, completion: @escaping (Bool, String)->Void) {
Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
// ERROR AND SUCCESS HANDLING
if error != nil {
// ERROR HANDLING
print(error?.localizedDescription as Any)
completion(false, "ERROR")
} else {
// SUCCESS HANDLING
self.isShowingNewAccountView = true
completion(true, authResult?.user.uid ?? "")
}
}
}
// Sign In
func signIn(email: String, password: String, completion: @escaping (Bool, String)->Void) {
Auth.auth().signIn(withEmail: email, password: password) { (authResult, error) in
// ERROR AND SUCCESS HANDLING
if error != nil {
// ERROR HANDLING
print(error?.localizedDescription as Any)
completion(false, "ERROR")
}
completion(true, authResult?.user.uid ?? "")
}
}
// Sign Out
func signOut(_ completion: @escaping (Bool) ->Void) {
try! Auth.auth().signOut()
completion(true)
}
// Create new user
func createNewUser(name: String, id: String) {
do {
let newUser = User(name: name, id: id)
try db.collection("users").document(newUser.id!).setData(from: newUser) { _ in
print("User \(name) created")
}
} catch let error {
print("Error writing user to Firestore: \(error)")
}
}
}
Alternative with a NavigationLink
In your approach you are using a NavigationLink. That would work as well. In that case the ContentView as well as the Page1 view are slightly different (Page2 stays as it is):
ContentView Update
In the ContentView it is not necessary do make the decision which view will be shown.
struct ContentView: View {
@EnvironmentObject var authenticationViewModel: AuthenticationViewModel
var body: some View {
Page1()
}
}
Page1 Update
On Page1 we now need the NavigationView and the NavigationLink. As soon as isShowingNewAccountView becomes true, it opens Page2, but for me, the NavigationLink is something you can press, like a button. But anyway:
struct Page1: View {
@EnvironmentObject var authenticationViewModel: AuthenticationViewModel
@State private var buttonPressed = false
@State private var email: String = "sebastian@hello.me"
@State private var password: String = "password"
var body: some View {
NavigationView(){
VStack(){
Text("Register")
.font(Font.system(size: 24, weight: .bold))
.padding()
.padding(.vertical, 50)
TextField("E-Mail", text: $email)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
SecureField("Password", text: $password)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
Button(action: {
buttonPressed.toggle()
authenticationViewModel.signUp(email: email, password: password){ success, uid in
if success {
print("UID: \(uid)")
} else {
print("Uh-oh")
}
buttonPressed = false
}
}) {
Text(buttonPressed ? "In Progress" : "Sign Up - Create User")
.foregroundColor(buttonPressed ? Color.pink : Color.cyan)
}
NavigationLink(destination: Page2(), isActive: $authenticationViewModel.isShowingNewAccountView){
EmptyView()}
}
}
}
}
Please see the gif:
