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

SwiftUI中Combine订阅器早于Firebase异步返回结果如何解决

问题解决

核心原因

你遇到的问题确实是Firebase异步调用导致的:map是同步转换操作符,不会等待异步回调执行完成,就会直接返回初始的false值,回调里修改valid的操作是在map已经返回结果之后才执行的,自然不会生效。

解决方案

你需要用flatMap操作符替代map,把上游的邮箱输入事件转换为异步请求的Publisher,Combine会自动等待异步请求完成后再向下游传递结果。

首先封装Firestore查询为Combine Publisher,我们用Future来包装回调式API:

func checkEmailIsValid(_ email: String) -> AnyPublisher<Bool, Never> {
    return Future { promise in
        Firestore.firestore()
            .collection("users")
            .whereField("email", isEqualTo: email)
            .getDocuments { snapshot, err in
                guard err == nil, let snapshot = snapshot else {
                    print("查询出错")
                    promise(.success(false))
                    return
                }
                let isValid = snapshot.documents.isEmpty
                print("This email is \(isValid ? "valid" : "invalid")")
                promise(.success(isValid))
            }
    }
    .eraseToAnyPublisher()
}

修改后的订阅逻辑:

func addEmailSubscriber() {
    $email
        .debounce(for: .seconds(1.0), scheduler: DispatchQueue.main)
        // 过滤空输入,减少不必要的请求
        .filter { !$0.isEmpty }
        // 用flatMap把输入的邮箱转换为异步查询的Publisher
        .flatMap { [weak self] email -> AnyPublisher<Bool, Never> in
            guard let self = self else { return Just(false).eraseToAnyPublisher() }
            return self.checkEmailIsValid(email)
        }
        .sink { [weak self] isValid in
            self?.emailIsValid = isValid
        }
        .store(in: &cancellabes)
}

方案优化建议

你当前直接查询Firestore的users集合校验邮箱的方案存在安全风险:需要把users集合的email字段读权限开放给所有未登录用户,很容易被恶意遍历爬取所有注册邮箱。如果你的应用使用了Firebase Auth做登录能力,更优的方案是用Firebase Auth自带的fetchSignInMethods(forEmail:)接口校验邮箱是否已注册,不需要暴露用户集合的权限,示例如下:

func checkEmailRegistered(_ email: String) -> AnyPublisher<Bool, Never> {
    return Future { promise in
        Auth.auth().fetchSignInMethods(forEmail: email) { methods, err in
            guard err == nil, let methods = methods else {
                promise(.success(false))
                return
            }
            // methods为空说明邮箱未注册
            promise(.success(methods.isEmpty))
        }
    }
    .eraseToAnyPublisher()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.15 20:06:08