Swift从Firebase获取登录用户数据失败,模拟器报EXC_BAD_ACCESS错误
问题分析与修复方案
你的代码出现Thread 1: EXC_BAD_ACCESS崩溃,通常是内存访问错误,结合Firebase登录和数据获取逻辑,主要排查以下几个关键点:
1. @AppStorage 不直接支持URL类型
@AppStorage默认仅支持基础数据类型(字符串、数字、布尔值等),直接存储URL?会导致内存解析错误。需要给URL添加RawRepresentable扩展实现类型转换:
extension URL: RawRepresentable { public init?(rawValue: String) { guard let url = URL(string: rawValue) else { return nil } self = url } public var rawValue: String { absoluteString } }
修改LoginView中的profileURL声明:
@AppStorage("user_profile_url") var profileURL: URL? = nil
2. 缺失错误处理导致状态异常
loginUser和fetchUser方法的do-catch块未处理错误,会导致错误被静默忽略,进而引发内存或状态紊乱:
func loginUser(){ isLoading = true closeKeyboard() Task{ [weak self] in guard let self = self else { return } do{ try await Auth.auth().signIn(withEmail: self.emailID, password: self.password) print("User Found") try await self.fetchUser() } catch { await self.setError(error) } } } func fetchUser()async throws{ guard let userID = Auth.auth().currentUser?.uid else{ throw NSError(domain: "AuthError", code: -1, userInfo: [NSLocalizedDescriptionKey: "未找到当前登录用户"]) } let user = try await Firestore.firestore().collection("Users").document(userID).getDocument(as: User.self) await MainActor.run(body: { userUID = userID userNameStored = user.username profileURL = user.userProfileURL logStatus = true isLoading = false // 登录成功后结束加载状态 }) }
3. Firebase初始化时机优化
在App的init中直接调用FirebaseApp.configure()可能存在线程安全问题,改用@StateObject管理Firebase初始化更符合SwiftUI生命周期:
class FirebaseManager: ObservableObject { static let shared = FirebaseManager() init() { FirebaseApp.configure() } } @main struct LoginForiOS: App { @StateObject private var firebaseManager = FirebaseManager() var body: some Scene { WindowGroup { ContentView() .environmentObject(firebaseManager) } } }
4. 检查User模型的Codable实现
fetchUser中使用getDocument(as: User.self)依赖User模型正确实现Codable,确保字段名和类型与Firestore中的数据完全匹配:
struct User: Codable, Identifiable { let id: String let username: String let userProfileURL: URL? // 对应Firestore中的字段名,需根据实际调整 enum CodingKeys: String, CodingKey { case id = "uid" case username case userProfileURL = "profile_url" } }
5. UI操作强制在主线程执行
closeKeyboard这类UI操作必须在主线程完成,避免线程冲突:
func closeKeyboard() { DispatchQueue.main.async { UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) } }
内容的提问来源于stack exchange,提问作者ZIYAXSERVERS
相关产品推荐
相关产品推荐

