设计网络层
问题
如何设计一个可扩展的 iOS 网络层?
答案
分层架构
API 协议定义
protocol APIEndpoint {
associatedtype Response: Decodable
var path: String { get }
var method: HTTPMethod { get }
var headers: [String: String]? { get }
var body: Encodable? { get }
}
struct GetUserEndpoint: APIEndpoint {
typealias Response = User
let userId: String
var path: String { "/api/users/\(userId)" }
var method: HTTPMethod { .get }
var headers: [String: String]? { nil }
var body: Encodable? { nil }
}
网络客户端
class NetworkClient {
private let session: URLSession
private let baseURL: URL
private var interceptors: [RequestInterceptor] = []
func request<E: APIEndpoint>(_ endpoint: E) async throws -> E.Response {
var request = buildURLRequest(endpoint)
// 拦截器链 - 请求前
for interceptor in interceptors {
request = try await interceptor.intercept(request: request)
}
let (data, response) = try await session.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
200..<300 ~= httpResponse.statusCode else {
throw NetworkError.serverError(statusCode: (response as? HTTPURLResponse)?.statusCode ?? 0)
}
return try JSONDecoder().decode(E.Response.self, from: data)
}
}
// 拦截器
protocol RequestInterceptor {
func intercept(request: URLRequest) async throws -> URLRequest
}
struct AuthInterceptor: RequestInterceptor {
func intercept(request: URLRequest) async throws -> URLRequest {
var request = request
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
return request
}
}
关键设计点
| 设计点 | 方案 |
|---|---|
| 类型安全 | 泛型 + Codable |
| 可扩展 | 拦截器链模式 |
| 重试 | 自动重试 + 指数退避 |
| Mock | 协议抽象,注入 MockSession |
常见面试问题
Q1: Token 过期如何自动刷新?
答案:在拦截器中检测 401 响应 → 刷新 Token → 重新发起请求。需要加锁避免多个请求并发刷新。