Swift异步网络请求:如何在出错时实现重试机制?
异步网络请求添加自动重试逻辑(无需修改外部Do Catch)
核心思路
- 抽离核心请求逻辑到内部函数,便于复用和重试调用
- 仅针对特定可重试错误(如Token过期)触发重试,其他错误直接抛出
- 内置Token刷新逻辑,重试时自动使用新Token
- 设置默认重试次数,外部调用无需修改原有代码
修改后的完整代码
首先补充NetworkError枚举值,用于区分可重试错误:
enum NetworkError: Error { case badURL case invalidToken case invalidResponse case decodingError case tokenExpired case tokenRefreshFailed case maxRetriesExceeded }
然后改造request函数,加入重试和Token刷新逻辑:
func request<T: Codable>(_ endpoint: Endpoint, retryCount: Int = 3) async throws -> T { var attempt = 0 while attempt < retryCount { do { return try await performCoreRequest(endpoint: endpoint) } catch NetworkError.tokenExpired { // Token过期,尝试刷新并重试 attempt += 1 if attempt >= retryCount { throw NetworkError.tokenRefreshFailed } try await refreshToken() } catch { // 非重试类错误直接抛出,不改变原有错误处理逻辑 throw error } } throw NetworkError.maxRetriesExceeded } // 核心请求逻辑抽离 private func performCoreRequest<T: Codable>(endpoint: Endpoint) async throws -> T { guard let url = endpoint.url else { throw NetworkError.badURL } guard let token = idToken else { throw NetworkError.invalidToken } let request = buildRequest(from: url, methodType: endpoint.methodType, idToken: token) let (data, response) = try await URLSession.shared.data(for: request) guard let httpResponse = response as? HTTPURLResponse else { throw NetworkError.invalidResponse } // 识别Token过期(通常对应401状态码) if httpResponse.statusCode == 401 { throw NetworkError.tokenExpired } guard httpResponse.statusCode == 200 else { throw NetworkError.invalidResponse } let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase do { let result = try decoder.decode(T.self, from: data) print("Result: \(result)") return result } catch { throw NetworkError.decodingError } } // Token刷新逻辑(根据实际业务实现) private func refreshToken() async throws { // 构造刷新Token的请求 guard let refreshUrl = URL(string: "https://your-api-domain.com/refresh-token") else { throw NetworkError.badURL } let refreshEndpoint = Endpoint(url: refreshUrl, methodType: .post) // 刷新请求不重试,避免循环调用 let tokenResponse: TokenResponse = try await request(refreshEndpoint, retryCount: 0) // 更新全局Token idToken = tokenResponse.accessToken } // 示例Token响应模型 struct TokenResponse: Codable { let accessToken: String }
关键说明
- 无需修改外部调用:默认重试次数设为3,外部调用
request(endpoint)时无需额外参数,原有Do Catch逻辑完全兼容 - 精准重试控制:仅对
tokenExpired错误触发重试,其他错误(如URL无效、解码失败)直接抛出,不干扰原有错误处理 - 避免重复刷新:刷新Token时调用
request传入retryCount:0,防止刷新请求本身触发重试导致循环 - 线程安全优化:如果需要处理多请求同时触发Token刷新的场景,可以添加异步锁(如
Actor)避免重复请求刷新接口
内容的提问来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

