Untitled
unknown
plain_text
17 days ago
6.3 kB
6
Indexable
//
// SignUpView.swift
// NextThing
//
// Created by Richard Clarke on 18/04/2026.
//
import SwiftUI
import FirebaseAuth
import FirebaseFirestore
struct SignUpView: View {
@State private var firstName: String = ""
@State private var lastName: String = ""
@State private var email: String = ""
@State private var password: String = ""
@State private var confirmPassword: String = ""
@State private var errorMessage: String = ""
@State private var isLoading: Bool = false
@State private var showSuccessAlert: Bool = false
@State private var goToLogin: Bool = false
@State private var showVerficationAlert: Bool = false
var body: some View {
NavigationStack {
ZStack {
BackgroundView()
VStack(spacing: 20) {
LogoView()
.offset(y: 60)
Text("Let's Get Started!")
.modifier(KeyTextModifier())
Text("Create an account to get all features")
.foregroundColor(.primaryGold)
TextField("First Name", text: $firstName)
.modifier(InputTextFieldStyle())
TextField("Last Name", text: $lastName)
.modifier(InputTextFieldStyle())
TextField("Email", text: $email)
.modifier(InputTextFieldStyle())
.keyboardType(.emailAddress)
.autocapitalization(.none)
SecureField("Password", text: $password)
.modifier(InputTextFieldStyle())
SecureField("Confirm Password", text: $confirmPassword)
.modifier(InputTextFieldStyle())
if !errorMessage.isEmpty {
Text(errorMessage)
.foregroundColor(.deepRed)
.font(.caption)
}
Button {
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
Task {
await registerUser()
}
} label: {
if isLoading {
ProgressView()
} else {
Text("Create Your Account")
}
}
.modifier(PrimaryButtonStyle())
.disabled(isLoading)
NavigationLink {
LogInView()
} label: {
Text("Already have an account?")
}
.foregroundStyle(.primaryRed)
}
.padding()
.navigationDestination(isPresented: $goToLogin) {
LogInView()
}
}
}
.navigationBarBackButtonHidden(true)
.alert("Account Created", isPresented: $showSuccessAlert) {
Button("Ok") {
goToLogin = true
}
} message: {
Text("A verification email has been sent. Please check your inbox.")
}
}
// Marked MainActor to safely handle UI state mutations on the main thread
@MainActor
func registerUser() async {
errorMessage = ""
// 1. Validate Form Fields
guard validateFields() else { return }
isLoading = true
do {
// 2. Create the user in Firebase Auth
let result = try await Auth.auth().createUser(withEmail: email, password: password)
let uid = result.user.uid
// 3. Prepare the user data dictionary
let userData: [String: Any] = [
"firstName": firstName,
"lastName": lastName,
"email": email,
"uid": uid,
"createdAt": FieldValue.serverTimestamp() // Good practice to track creation time
]
// 4. Save to Firestore under the 'users' collection
let db = Firestore.firestore()
try await db.collection("users").document(uid).setData(userData)
// 5. Send verification email using modern async/await
try await result.user.sendEmailVerification()
print("Verification email sent successfully!")
// 6. Trigger success UI
isLoading = false
showSuccessAlert = true
} catch {
isLoading = false
errorMessage = error.localizedDescription
print("Registration error: \(error.localizedDescription)")
}
}
// Updated validation helper to return a boolean result
func validateFields() -> Bool {
let emailRegex = "^[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$"
let emailPredicate = NSPredicate(format: "SELF MATCHES %@", emailRegex)
let passwordRegex = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[A-Za-z\\d]{8,}$"
let passwordPredicate = NSPredicate(format: "SELF MATCHES %@", passwordRegex)
if firstName.isEmpty || lastName.isEmpty || email.isEmpty || password.isEmpty || confirmPassword.isEmpty {
errorMessage = "Please fill in all fields"
return false
}
if !emailPredicate.evaluate(with: email) {
errorMessage = "Invalid email format"
return false
}
if !passwordPredicate.evaluate(with: password) {
errorMessage = "Password must contain at least 8 characters, 1 uppercase letter, 1 lowercase letter, and 1 number"
return false
}
if password != confirmPassword {
errorMessage = "Passwords do not match"
return false
}
return true
}
}
#Preview {
SignUpView()
}
Editor is loading...
Leave a Comment