Swift Combine实现Token刷新时Sink返回错误问题排查
问题分析与修复方案
1. 401错误判断逻辑失效
你当前用error as? NetworkError == .unauthorised(code: 401, error: "Unauthorized")判断401错误,但带关联值的枚举默认未实现Equatable,且服务器返回的错误消息可能和硬编码的"Unauthorized"不一致,导致判断不成立,直接抛出错误到下游。
修复:
改用模式匹配直接检查错误类型和状态码:
.tryCatch { [weak self] error -> AnyPublisher<Data, NetworkError> in guard let self = self else { throw error as? NetworkError ?? .unknown(code: -1, error: "实例已释放") } // 正确识别401未授权错误 guard let networkError = error as? NetworkError, case .unauthorised(let code, _) = networkError, code == 401 else { throw error as? NetworkError ?? .unknown(code: -1, error: error.localizedDescription) } // 后续刷新逻辑 }
2. 刷新后请求未携带新Token
如果updateRequest(req)没有正确将新Token注入请求头,重新发起的请求依然会返回401,导致流程失败。
确认updateRequest逻辑:
确保方法正确读取新Token并更新请求头:
private func updateRequest(_ req: NetworkRequest) -> NetworkRequest { var updatedReq = req guard let token = UserDefaults.token else { return updatedReq } updatedReq.headers["Authorization"] = "Bearer \(token)" return updatedReq }
3. 修正后的完整tryCatch代码块
整合上述修复,确保刷新后重新请求的逻辑正确:
.tryCatch { [weak self] error -> AnyPublisher<Data, NetworkError> in guard let self = self else { throw error as? NetworkError ?? .unknown(code: -1, error: "实例已释放") } guard let networkError = error as? NetworkError, case .unauthorised(let code, _) = networkError, code == 401 else { throw error as? NetworkError ?? .unknown(code: -1, error: error.localizedDescription) } return self.refreshToken .flatMap { _ -> AnyPublisher<Data, NetworkError> in let updatedReq = self.updateRequest(req) return self.request(updatedReq) } .eraseToAnyPublisher() }
4. 额外优化:避免重复刷新Token
如果多个请求同时触发401,当前代码会重复调用刷新接口。可以用锁和共享Publisher避免重复请求:
private var refreshTokenPublisher: AnyPublisher<Void, NetworkError>? private let refreshTokenLock = DispatchQueue(label: "com.yourapp.refreshTokenLock") private func refreshTokenIfNeeded() -> AnyPublisher<Void, NetworkError> { return refreshTokenLock.sync { if let existing = refreshTokenPublisher { return existing } let publisher = refreshToken .handleEvents(receiveCompletion: { [weak self] _ in self?.refreshTokenLock.sync { self?.refreshTokenPublisher = nil } }) .share() .eraseToAnyPublisher() refreshTokenPublisher = publisher return publisher } }
之后在tryCatch中用refreshTokenIfNeeded()替代refreshToken即可。
验证步骤
- 调试时打印
updateRequest返回的请求头,确认新Token已正确注入 - 检查重新发起的请求状态码,确认不再返回401
- 若使用了重复刷新优化,可同时触发多个请求验证是否只发起一次刷新
内容的提问来源于stack exchange,提问作者clopex
相关产品推荐
相关产品推荐

