SwiftUI中Apple登录Firebase Auth无法获取用户姓名的解决方法
解决Apple登录获取姓名为空并存入数据库的问题
核心原因
Apple登录的fullName字段仅在用户首次授权登录时返回,后续用户再次登录(比如通过Face ID快速登录)时,这个字段会是空的。所以必须抓住首次登录的时机把姓名存入数据库,后续登录直接从数据库读取展示。
代码修正方案
// // AuthenticationViewModel.swift // TrackMe // import Foundation import SwiftUI import AuthenticationServices import CryptoKit import FirebaseAuth @MainActor class AuthenticationViewModel : ObservableObject{ @Published var currentNonce : String? = "" @Published var errorMessage: String = "" // 补上之前缺失的errorMessage属性 private let viewModel: UsersViewModel = UsersViewModel() // 改用let,无需ObservedObject } extension AuthenticationViewModel { func handleSignInWithAppleRequest(_ request: ASAuthorizationAppleIDRequest){ request.requestedScopes = [.fullName, .email] let nonce = randomNonceString() currentNonce = nonce request.nonce = sha256(nonce) } func handleSignInWithAppleCompletion(_ result: Result<ASAuthorization, Error>){ switch result { case .failure(let failure): errorMessage = failure.localizedDescription case .success(let success): guard let appleIDCredential = success.credential as? ASAuthorizationAppleIDCredential else { errorMessage = "无法获取Apple授权凭证" return } guard let nonce = currentNonce else{ fatalError("无效状态:收到登录回调,但未发送登录请求") } guard let appleIdToken = appleIDCredential.identityToken else { errorMessage = "无法获取ID Token" return } guard let idTokenString = String(data: appleIdToken, encoding: .utf8) else{ errorMessage = "无法转换Token为字符串" return } let credential = OAuthProvider.credential(withProviderID: "apple.com", idToken: idTokenString, rawNonce: nonce) Task { do { let authResult = try await Auth.auth().signIn(with: credential) let userId = authResult.user.uid // 处理姓名逻辑 var displayName: String = "" if let fullName = appleIDCredential.fullName { // 首次登录,拼接姓名 let givenName = fullName.givenName ?? "" let familyName = fullName.familyName ?? "" displayName = "\(givenName) \(familyName)".trimmingCharacters(in: .whitespaces) // 存入自定义数据库(仅首次登录调用,避免覆盖) viewModel.createNewUser(userName: displayName, userId: userId) // 可选:同步姓名到Firebase用户信息,方便后续快速读取 let profileUpdate = authResult.user.createProfileChangeRequest() profileUpdate.displayName = displayName try await profileUpdate.commitChanges() } else { // 非首次登录,优先从Firebase用户信息读取,否则从自定义数据库拉取 if let existingName = authResult.user.displayName { displayName = existingName } else { displayName = await viewModel.fetchUserName(for: userId) ?? "匿名用户" } } print("最终展示姓名:\(displayName)") // 这里可将displayName赋值给UI展示用的变量 } catch { errorMessage = error.localizedDescription print("登录错误:\(error.localizedDescription)") } } } } // 补充缺失的辅助函数 private func randomNonceString(length: Int = 32) -> String { precondition(length > 0) let charset: [Character] = Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._") var result = "" var remainingLength = length while remainingLength > 0 { let randoms: [UInt8] = (0..<16).map { _ in var random: UInt8 = 0 let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random) if errorCode != errSecSuccess { fatalError("无法生成随机数:SecRandomCopyBytes失败,错误码\(errorCode)") } return random } randoms.forEach { random in if remainingLength == 0 { return } if random < charset.count { result.append(charset[Int(random)]) remainingLength -= 1 } } } return result } private func sha256(_ input: String) -> String { let inputData = Data(input.utf8) let hashedData = SHA256.hash(data: inputData) return hashedData.compactMap { String(format: "%02x", $0) }.joined() } }
关键注意事项
- 首次登录才会返回姓名:必须在首次登录时将姓名写入自定义数据库,后续登录无法再从Apple获取该字段。
- 兜底处理:如果用户首次登录时拒绝授权姓名,要设置默认值(比如“匿名用户”)避免UI异常。
- Firebase同步:将姓名同步到Firebase Auth用户信息后,后续登录可以直接从
authResult.user.displayName读取,无需频繁请求自定义数据库。
内容的提问来源于stack exchange,提问作者DevAcc
相关产品推荐
相关产品推荐

