Untitled

 avatar
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