网络与 API
URLSession、Alamofire、REST/GraphQL。
35 分钟
网络与 API
iOS 网络层以 URLSession 为核心,生态中有 Alamofire 简化请求。对照 /roadmaps/ios「Networking」节点:REST JSON 是主流,GraphQL 在部分 App 中使用。
学习目标
- 用 URLSession 发起 GET / POST 并解析 JSON
- 封装 API Client,统一错误处理与解码
- 了解 Alamofire、认证头与 REST 最佳实践
- 对照 Spring Boot REST 与 React fetch
核心概念
URLSession 基础
import Foundation
enum APIError: Error, LocalizedError {
case invalidURL
case httpStatus(Int)
case decodingFailed
var errorDescription: String? {
switch self {
case .invalidURL: return "URL 无效"
case .httpStatus(let code): return "HTTP \(code)"
case .decodingFailed: return "JSON 解析失败"
}
}
}
struct APIClient {
let baseURL: URL
let session: URLSession
init(baseURL: URL, session: URLSession = .shared) {
self.baseURL = baseURL
self.session = session
}
func get<T: Decodable>(_ path: String, as type: T.Type) async throws -> T {
guard let url = URL(string: path, relativeTo: baseURL) else {
throw APIError.invalidURL
}
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Accept")
let (data, response) = try await session.data(for: request)
guard let http = response as? HTTPURLResponse else {
throw APIError.httpStatus(-1)
}
guard (200..<300).contains(http.statusCode) else {
throw APIError.httpStatus(http.statusCode)
}
do {
return try JSONDecoder().decode(T.self, from: data)
} catch {
throw APIError.decodingFailed
}
}
}
// 使用
let client = APIClient(baseURL: URL(string: "https://api.coderoadmap.local")!)
let tracks: [Track] = try await client.get("/api/tracks", as: [Track].self)
async/await 版 URLSession(iOS 15+)替代传统 completion handler,代码更清晰。
POST 与认证
func login(email: String, password: String) async throws -> AuthToken {
var request = URLRequest(url: baseURL.appendingPathComponent("/api/auth/login"))
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONEncoder().encode(["email": email, "password": password])
let (data, response) = try await session.data(for: request)
// 解析 token,存入 Keychain
return try JSONDecoder().decode(AuthToken.self, from: data)
}