Sign in with Apple Keychain savedEmail Stored Incorrectly

Using personal physical iPhone for simulations. Can't get Keychain to read or store AppleID name/email. I want to avoid hard reseting physical phone.


Logs confirm Keychain is working, but userIdentifier and savedEmail are not being stored correctly.

πŸ”„ Initializing UserManager... βœ… Saved testKeychain to Keychain: Test Value βœ… Retrieved testKeychain from Keychain: Test Value πŸ” Keychain Test - Retrieved Value: Test Value ⚠️ Keychain Retrieve Warning: No stored value found for userIdentifier ⚠️ Keychain Retrieve Warning: No stored value found for savedEmail πŸ” Debug - Retrieved from Keychain: userIdentifier=nil, savedEmail=nil ⚠️ No stored userIdentifier in Keychain. User needs to sign in. πŸ“¦ Converting User to CKRecord: Unknown, No Email βœ… User saved locally: Unknown, No Email βœ… User saved to CloudKit: Unknown, No Email


Below UserManager.swift if someone can help troubleshoot. Or step by step tutorial to configure a project and build a User Login & User Account creation for Apple Only app.

import Foundation
import CloudKit
import AuthenticationServices
import SwiftData

@MainActor
class UserManager: ObservableObject {
    @Published var user: User?
    @Published var isLoggedIn = false
    @Published var errorMessage: String?

    private let database = CKContainer.default().publicCloudDatabase

    init() {
        print("πŸ”„ Initializing UserManager...")

        // πŸ” Keychain Debug Test
        let testKey = "testKeychain"
        KeychainHelper.shared.save("Test Value", forKey: testKey)
        let retrievedValue = KeychainHelper.shared.retrieve(forKey: testKey)
        
        print("πŸ” Keychain Test - Retrieved Value: \(retrievedValue ?? "nil")")

        fetchUser() // Continue normal initialization
    }

    // βœ… Sign in & Save User
    func handleSignIn(_ authResults: ASAuthorization) {
        guard let appleIDCredential = authResults.credential as? ASAuthorizationAppleIDCredential else {
            errorMessage = "Error retrieving Apple credentials"
            print("❌ ASAuthorization Error: Invalid credentials received")
            return
        }

        let userIdentifier = appleIDCredential.user
        let fullName = appleIDCredential.fullName?.givenName ?? retrieveSavedName()
        var email = appleIDCredential.email ?? retrieveSavedEmail()

        print("πŸ” Apple Sign-In Data: userIdentifier=\(userIdentifier), fullName=\(fullName), email=\(email)")

        // πŸ”„ If Apple doesn't return an email, check if it exists in Keychain
        if appleIDCredential.email == nil {
            print("⚠️ Apple Sign-In didn't return an email. Retrieving saved email from Keychain.")
        }

        // βœ… Store userIdentifier & email in Keychain
        KeychainHelper.shared.save(userIdentifier, forKey: "userIdentifier")
        KeychainHelper.shared.save(email, forKey: "savedEmail")

        let newUser = User(fullName: fullName, email: email, userIdentifier: userIdentifier)
        saveUserToCloudKit(newUser)
    }
    
    func saveUserToCloudKit(_ user: User) {
        let record = user.toRecord()

        Task {
            do {
                try await database.save(record)
                DispatchQueue.main.async {
                    self.user = user
                    self.isLoggedIn = true
                    self.saveUserLocally(user)
                    print("βœ… User saved to CloudKit: \(user.fullName), \(user.email)")
                }
            } catch {
                DispatchQueue.main.async {
                    self.errorMessage = "Error saving user: \(error.localizedDescription)"
                    print("❌ CloudKit Save Error: \(error.localizedDescription)")
                }
            }
        }
    }

    // βœ… Fetch User from CloudKit
    func fetchUser() {
        let userIdentifier = KeychainHelper.shared.retrieve(forKey: "userIdentifier")
        let savedEmail = KeychainHelper.shared.retrieve(forKey: "savedEmail")

        print("πŸ” Debug - Retrieved from Keychain: userIdentifier=\(userIdentifier ?? "nil"), savedEmail=\(savedEmail ?? "nil")")

        guard let userIdentifier = userIdentifier else {
            print("⚠️ No stored userIdentifier in Keychain. User needs to sign in.")
            return
        }

        let predicate = NSPredicate(format: "userIdentifier == %@", userIdentifier)
        let query = CKQuery(recordType: "User", predicate: predicate)

        Task { [weak self] in
            guard let self = self else { return }
            do {
                let results = try await self.database.records(matching: query, resultsLimit: 1).matchResults
                if let (_, result) = results.first {
                    switch result {
                    case .success(let record):
                        DispatchQueue.main.async {
                            let fetchedUser = User(record: record)
                            self.user = User(
                                fullName: fetchedUser.fullName,
                                email: savedEmail ?? fetchedUser.email,
                                userIdentifier: userIdentifier
                            )
                            self.isLoggedIn = true
                            self.saveUserLocally(self.user!)
                            print("βœ… User loaded from CloudKit: \(fetchedUser.fullName), \(fetchedUser.email)")
                        }
                    case .failure(let error):
                        DispatchQueue.main.async {
                            print("❌ Error fetching user from CloudKit: \(error.localizedDescription)")
                        }
                    }
                }
            } catch {
                DispatchQueue.main.async {
                    print("❌ CloudKit fetch error: \(error.localizedDescription)")
                }
            }
        }
    }

    // βœ… Save User Locally
    private func saveUserLocally(_ user: User) {
        if let encoded = try? JSONEncoder().encode(user) {
            UserDefaults.standard.set(encoded, forKey: "savedUser")
            UserDefaults.standard.set(user.fullName, forKey: "savedFullName")
            UserDefaults.standard.set(user.email, forKey: "savedEmail")
            print("βœ… User saved locally: \(user.fullName), \(user.email)")
        } else {
            print("❌ Local Save Error: Failed to encode user data")
        }
    }

    // βœ… Retrieve Previously Saved Name
    private func retrieveSavedName() -> String {
        return UserDefaults.standard.string(forKey: "savedFullName") ?? "Unknown"
    }

    // βœ… Retrieve Previously Saved Email
    private func retrieveSavedEmail() -> String {
        return KeychainHelper.shared.retrieve(forKey: "savedEmail") ?? UserDefaults.standard.string(forKey: "savedEmail") ?? "No Email"
    }

    // βœ… Sign Out
    func signOut() {
        isLoggedIn = false
        user = nil
        UserDefaults.standard.removeObject(forKey: "savedUser")
        print("πŸšͺ Signed Out")
    }
}