You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中Firebase Auth登录后检查Firestore用户存在性及线程优化问题

Firebase登录后检查用户存在性的异步处理优化

问题描述

我实现了SwiftUI代码用于Firebase Auth登录后,检查用户是否存在于Firestore的users集合中。尝试用DispatchSemaphore让耗时的checkIfTheUserExistsInDataBase函数优先执行,结果导致应用冻结,求更好的实现方式。

当前实现代码

@Published var isNewUser: Bool?

init() {
  self.isNewUser = false
}

func checkIfTheUserExistsInDataBase(
  userID: String?, completion: @escaping (_ isNewuser: Bool) -> Void
) {
  let docRef = db.collection("users").whereField("user_id", isEqualTo: userID!).limit(to: 1)
  docRef.getDocuments { querySnapshot, error in
    if error != nil {
      print(error?.localizedDescription)
    } else {
      if let doc = querySnapshot?.documents, doc.isEmpty {
        completion(true)
      } else {
        completion(false)
      }
    }
  }
}

func login(
  email: String, password: String,
  completion: @escaping (_ error: Error?, _ isEmailVerified: Bool) -> Void
) {
  Auth.auth().signIn(withEmail: email, password: password) { authDataResult, error in
    if error == nil {
      if authDataResult!.user.isEmailVerified {
        DispatchQueue.main.async {
          self.checkIfTheUserExistsInDataBase(userID: authDataResult?.user.uid) { isNewUser in
            self.isNewUser = isNewUser
          }
        }
        UserDefaults.standard.set(authDataResult?.user.uid, forKey: CurrentUserDefaults.userID)
        completion(error, true)
      } else {
        print("Email not verified")
        completion(error, false)
      }
    } else {
      completion(error, false)
    }
  }
}

解决方案

为什么信号量会导致冻结

DispatchSemaphore会阻塞当前线程等待异步操作完成,如果在主线程调用,直接卡住UI线程导致应用冻结。Firebase的API本身就是异步非阻塞设计,完全不需要用信号量强制同步。

优化方案1:调整回调顺序(基于原回调模式)

原代码在用户存在性检查完成前就调用了completion,逻辑顺序混乱。把completion和UserDefaults设置移到检查完成的回调里,确保所有异步操作完成后再通知上层:

@Published var isNewUser: Bool?

init() {
  self.isNewUser = false
}

func checkIfTheUserExistsInDataBase(userID: String?, completion: @escaping (Bool) -> Void) {
  guard let userID = userID else {
    completion(false)
    return
  }
  
  let docRef = db.collection("users").whereField("user_id", isEqualTo: userID).limit(to: 1)
  docRef.getDocuments { querySnapshot, error in
    if let error = error {
      print(error.localizedDescription)
      completion(false)
      return
    }
    
    let isNew = querySnapshot?.documents.isEmpty ?? true
    completion(isNew)
  }
}

func login(email: String, password: String, completion: @escaping (Error?, Bool) -> Void) {
  Auth.auth().signIn(withEmail: email, password: password) { authDataResult, error in
    guard let authResult = authDataResult, error == nil else {
      completion(error, false)
      return
    }
    
    if !authResult.user.isEmailVerified {
      print("Email not verified")
      completion(nil, false)
      return
    }
    
    // 先完成用户存在性检查,再执行后续操作
    self.checkIfTheUserExistsInDataBase(userID: authResult.user.uid) { [weak self] isNewUser in
      guard let self = self else { return }
      
      // 主线程更新@Published变量
      DispatchQueue.main.async {
        self.isNewUser = isNewUser
      }
      
      UserDefaults.standard.set(authResult.user.uid, forKey: CurrentUserDefaults.userID)
      completion(nil, true)
    }
  }
}

优化方案2:使用async/await(Swift 5.5+推荐)

Firebase从iOS 15+/macOS 12+开始支持async/await API,代码更简洁易读,彻底避免回调嵌套:

import FirebaseAuth
import FirebaseFirestore

@Published var isNewUser: Bool?

init() {
  self.isNewUser = false
}

// 转为async函数
func checkIfTheUserExistsInDataBase(userID: String?) async -> Bool {
  guard let userID = userID else { return false }
  
  do {
    let querySnapshot = try await db.collection("users")
      .whereField("user_id", isEqualTo: userID)
      .limit(to: 1)
      .getDocuments()
    
    return querySnapshot.documents.isEmpty
  } catch {
    print(error.localizedDescription)
    return false
  }
}

// 转为async函数
func login(email: String, password: String) async throws -> Bool {
  let authResult = try await Auth.auth().signIn(withEmail: email, password: password)
  
  guard authResult.user.isEmailVerified else {
    print("Email not verified")
    return false
  }
  
  // 等待用户存在性检查完成
  let isNewUser = await checkIfTheUserExistsInDataBase(userID: authResult.user.uid)
  
  // 主线程更新@Published变量
  await MainActor.run {
    self.isNewUser = isNewUser
  }
  
  UserDefaults.standard.set(authResult.user.uid, forKey: CurrentUserDefaults.userID)
  return true
}

关键优化点

  1. 移除DispatchSemaphore,避免阻塞主线程导致UI冻结
  2. 修复逻辑顺序:确保用户存在性检查完成后再执行后续操作
  3. 替换强制解包为可选绑定,提升代码安全性
  4. 用async/await简化异步代码,可读性和可维护性更强
  5. 确保@Published变量在主线程更新(SwiftUI状态必须在主线程修改)

内容的提问来源于stack exchange,提问作者Giorgi Samkharadze

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 22:25:25