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

使用Combine实现带泛型返回的FlatMap链式网络请求时flatMap未触发

问题原因
  • 核心原因1:asURLRequest方法内的Future存在多个未触发promise的代码分支,导致发布者长期处于pending状态,没有输出任何值或错误,下游的flatMap自然不会被触发。漏处理的分支包括:
    • 当Auth.auth().currentUser为nil(用户未登录)时,没有调用任何promise回调
    • 极端场景下Firebase的getIDToken回调同时返回空token和空error时,没有兜底回调
  • 核心原因2:如果代码中的cancellables数组是定义在函数内部的局部变量,函数执行结束后数组会被释放,对应的Combine订阅会立刻被取消,还没等异步的token获取完成,管道就已经终止了。
修复方案
  1. 补全asURLRequest中的所有分支回调,确保Future一定会输出结果:
func asURLRequest(baseURL: String) -> AnyPublisher<URLRequest, NetworkRequestError> {
    return Deferred {
        Future<URLRequest, NetworkRequestError> { promise in
            if var urlComponents = URLComponents(string: baseURL) {
                urlComponents.path = "\(urlComponents.path)\(path)"
                urlComponents.queryItems = queryItemsFrom(params: queryParams)
                if let finalURL = urlComponents.url {
                    guard let user = Auth.auth().currentUser else {
                        // 补全用户未登录的错误回调
                        promise(.failure(NetworkRequestError.authenticationError))
                        return
                    }
                    print("##### final url -> \(finalURL)")
                    user.getIDToken(completion: { (token, error) in
                        if let fbToken = token {
                            var request = URLRequest(url: finalURL)
                            request.httpMethod = method.rawValue
                            request.httpBody = requestBodyFrom(params: body)
                            let defaultHeaders: HTTPHeaders = [
                                HTTPHeaderField.contentType.rawValue: contentType.rawValue,
                                HTTPHeaderField.acceptType.rawValue: contentType.rawValue,
                                HTTPHeaderField.authentication.rawValue: fbToken
                            ]
                            request.allHTTPHeaderFields = defaultHeaders.merging(headers ?? [:], uniquingKeysWith: { (first, _) in first })
                            print("##### API TOKEN() SUCCESS: \(defaultHeaders)")
                            promise(.success(request))
                            return
                        }
                        if let fbError = error {
                            print("##### API TOKEN() ERROR: \(fbError)")
                            promise(.failure(NetworkRequestError.decodingError))
                            return
                        }
                        // 补全兜底错误回调
                        promise(.failure(NetworkRequestError.unknownError))
                    })
                } else {
                    promise(.failure(NetworkRequestError.decodingError))
                }
            } else {
                promise(.failure(NetworkRequestError.decodingError))
            }
        }
    }.eraseToAnyPublisher()
}

注意:你需要在自己的NetworkRequestError枚举中添加authenticationError、unknownError对应的case,或者替换成你已有的错误类型。

  1. 把cancellables数组改为所属类的实例属性,保证订阅的生命周期和页面/业务类绑定:
// 正确写法:作为类的属性持有,不要放在函数内部
class YourViewController: UIViewController {
    var cancellables = [AnyCancellable]()

    func requestUserData() {
        struct ReadUser: Request {
            typealias ReturnType = UserData
            var path: String
            var method: HTTPMethod = .get
            init(_ id: String) {
                path = "users/\(id)"
            }
        }
        
        let apiClient = APIClient(baseURL: BASE_URL)
        apiClient.dispatch(ReadUser(Auth.auth().currentUser?.uid ?? ""))
            .receive(on: DispatchQueue.main)
            .sink(
                receiveCompletion: { result in
                    switch result {
                    case .failure(let error):
                        print("##### Error loading data: \(error)")
                    default: break
                    }
                },
                receiveValue: { value in
                    // 处理返回的用户数据
                })
            .store(in: &cancellables)
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 06:57:01