Alamofire 完整使用指南:从发起请求到流式处理的实战详解
本文基于 Alamofire 官方 Usage 文档,系统讲解 Alamofire 5.x 的核心使用方式:两种顶层请求 API、HTTP 方法、Encodable 参数编码(URLEncodedFormParameterEncoder / JSONParameterEncoder 及全部编码定制选项)、HTTP 头、响应验证与五种响应处理器、下载/上传、DataStreamRequest 流式处理、统计指标与 cURL 调试输出。读完之后,你可以覆盖绝大多数 iOS/macOS/tvOS 项目的 HTTP 网络需求,并能从源码层面理解每个 API 背后的行为。
1. 引言:Alamofire 建立在 URL Loading System 之上
Alamofire 提供优雅且可组合的 HTTP 网络接口,但它并不自行实现 HTTP 网络功能,而是构建在 Foundation 框架的 URL Loading System(核心为 URLSession 与 URLSessionTask 子类)之上。Alamofire 将这些 API 以及许多其他 API 包装为更易用的接口。理解这一点很重要:Alamofire 的网络能力上限受底层 URL Loading System 制约,其行为与最佳实践应始终牢记。
同时需要注意:Alamofire(以及 URL Loading System 整体)的网络请求是异步执行的。请求结果只在响应闭包的作用域内可用,任何依赖服务端返回数据的逻辑都必须在响应闭包内完成。
1.1 AF 全局命名空间与 af 前缀扩展
早期版本的文档示例写作 Alamofire.request()。从 Alamofire 5 开始,这种污染全局命名空间的机制被移除,取而代之的是一个指向 Session.default 的全局引用 AF。这一点在 Source/Alamofire.swift 中可以直接确认:
/// Reference to `Session.default` for quick bootstrapping and examples.
public let AF = Session.default
因此 AF.request(...) 本质上就是在默认 Session 上发起请求。同理,Alamofire 扩展的类型统一使用 af 前缀的属性/扩展(如 URLSessionConfiguration.af.default),以区分 Alamofire 增加的功能与其他扩展。
2. 发起请求:两种顶层 API
所有示例均要求源文件顶部 import Alamofire。最简单的请求只需一个可转换为 URL 的 String:
AF.request("https://httpbin.org/get").response { response in
debugPrint(response)
}
从源码看,Session 提供两种顶层 request 入口,定义见 Source/Core/Session.swift:
第一种接受各个独立组件,支持 Encodable 参数与每请求的 RequestInterceptor:
open func request<Parameters: Encodable & Sendable>(_ convertible: any URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoder: any ParameterEncoder = URLEncodedFormParameterEncoder.default,
headers: HTTPHeaders? = nil,
interceptor: (any RequestInterceptor)? = nil,
shouldAutomaticallyResume: Bool? = nil,
requestModifier: RequestModifier? = nil) -> DataRequest
其内部实现(见 RequestEncodableConvertible.asURLRequest(),Session.swift)展示了完整的组装顺序:先用 URL、method、headers 构造 URLRequest,再应用 requestModifier,最后用 encoder 把 parameters 编码进请求。
第二种接受任意 URLRequestConvertible 值,参数全部封装在该值中,适合构建更强大的抽象(详见 AdvancedUsage.md):
open func request(_ convertible: any URLRequestConvertible,
interceptor: (any RequestInterceptor)? = nil,
shouldAutomaticallyResume: Bool? = nil) -> DataRequest
还存在一组接受
Parameters字典与ParameterEncoding类型的旧式request方法(见 Session.swift),该 API 不再推荐使用,未来将被废弃移除。
2.1 设置其他 URLRequest 属性:RequestModifier
常见的定制参数不够用时,可以在创建请求时传入 RequestModifier 闭包来修改生成的 URLRequest。该类型是 @Sendable (inout URLRequest) throws -> Void,定义见 Source/Core/Session.swift:
AF.request("https://httpbin.org/get", requestModifier: { $0.timeoutInterval = 5 }).response(...)
它也支持尾闭包语法:
AF.request("https://httpbin.org/get") { urlRequest in
urlRequest.timeoutInterval = 5
urlRequest.allowsConstrainedNetworkAccess = false
}
.response(...)
注意:RequestModifier 只作用于"URL + 独立组件"方式的创建方法,不作用于直接由 URLRequestConvertible 值创建的场景(因为后者应能自行设置所有参数)。从源码结构看,当大多数请求都需要在创建时修改时,推荐改为采用 URLRequestConvertible 协议。
2.2 HTTP 方法
HTTPMethod 是一个 RawRepresentable 结构体,列出 RFC 7231 §4.3 定义的 HTTP 方法,源码见 Source/Core/HTTPMethod.swift:connect、delete、get、head、options、patch、post、put、query、trace。注意其 rawValue 是大小写敏感比较的。
AF.request("https://httpbin.org/get")
AF.request("https://httpbin.org/post", method: .post)
AF.request("https://httpbin.org/put", method: .put)
AF.request("https://httpbin.org/delete", method: .delete)
要牢记:不同 HTTP 方法语义不同,服务端期望的参数编码也不同。例如在 GET 请求中携带 body 数据不受 URLSession 和 Alamofire 支持,会返回错误。
如果需要 HTTPMethod 未内置的方法,可以直接扩展该类型:
extension HTTPMethod {
static let custom = HTTPMethod(rawValue: "CUSTOM")
}
AF.request("https://httpbin.org/headers", method: .custom)
此外,Alamofire 在 URLRequest 上提供了桥接扩展,使其 httpMethod 字符串属性与 HTTPMethod 值互转(method 的 get/set 双向可用)。
3. 请求参数与 Parameter Encoders
Alamofire 支持把任意 Encodable 类型作为请求参数,参数会经过 ParameterEncoder 协议类型的处理,被加入 URLRequest 后发送。Alamofire 内置两个 ParameterEncoder 实现(见 Source/Core/ParameterEncoder.swift):JSONParameterEncoder 与 URLEncodedFormParameterEncoder,覆盖现代服务最常见的两种编码。
struct Login: Encodable {
let email: String
let password: String
}
let login = Login(email: "test@test.test", password: "testPassword")
AF.request("https://httpbin.org/post",
method: .post,
parameters: login,
encoder: JSONParameterEncoder.default).response { response in
debugPrint(response)
}
3.1 URLEncodedFormParameterEncoder
URLEncodedFormParameterEncoder 将值编码为 url-encoded 字符串,设置到(或追加到已有的)URL query string,或作为 HTTP body。通过 Destination 枚举控制字符串去向,共三个 case:
.methodDependent(默认):.get、.head、.delete请求应用到已有的 query string;其他 HTTP 方法则设置为 HTTP body;.queryString:设置或追加到请求URL的 query;.httpBody:设置为URLRequest的 HTTP body。
带 HTTP body 的编码请求,若未设置 Content-Type,会被自动设为 application/x-www-form-urlencoded; charset=utf-8。
GET 请求 + URL 编码参数(三种等价写法,最终都得到 https://httpbin.org/get?foo=bar):
let parameters = ["foo": "bar"]
AF.request("https://httpbin.org/get", parameters: parameters) // encoding 默认为 `URLEncoding.default`
AF.request("https://httpbin.org/get", parameters: parameters, encoder: URLEncodedFormParameterEncoder.default)
AF.request("https://httpbin.org/get", parameters: parameters, encoder: URLEncodedFormParameterEncoder(destination: .methodDependent))
POST 请求 + URL 编码参数(HTTP body 为 qux[]=x&qux[]=y&qux[]=z&baz[]=a&baz[]=b&foo[]=bar):
let parameters: [String: [String]] = [
"foo": ["bar"],
"baz": ["a", "b"],
"qux": ["x", "y", "z"]
]
AF.request("https://httpbin.org/post", method: .post, parameters: parameters)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: URLEncodedFormParameterEncoder.default)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: URLEncodedFormParameterEncoder(destination: .httpBody))
底层实际编码由 URLEncodedFormEncoder 完成(源码见 Source/Features/URLEncodedFormEncoder.swift),它暴露了一整套定制维度。所有定制的通用模式都是:自建 URLEncodedFormEncoder 实例,再包进 URLEncodedFormParameterEncoder(encoder:)。
3.1.1 键值对排序(alphabetizeKeyValuePairs)
自 Swift 4.2 起,Dictionary 的内部顺序在运行时是随机的、每次启动不同,这会导致编码出的参数顺序不稳定,影响缓存等行为。默认情况下 URLEncodedFormEncoder 会对编码的键值对排序,保证输出稳定;但这也可能不符合类型实际实现的编码顺序。设为 false 可恢复实现顺序(同时也意味着 Dictionary 的随机顺序):
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(alphabetizeKeyValuePairs: false))
3.1.2 Array 参数编码(ArrayEncoding)
由于没有公开的集合类型编码规范,Alamofire 默认采用"键后追加 []"的约定:foo = [1, 2] 编码为 foo[]=1&foo[]=2;嵌套字典值则为 foo[bar]=baz。
.brackets:每个值都追加空方括号(默认);.noBrackets:不追加方括号,foo = [1, 2]编码为foo=1&foo=2。
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(arrayEncoding: .noBrackets))
3.1.3 Bool 参数编码(BoolEncoding)
.numeric(默认):true编码为1,false编码为0;.literal:编码为字符串字面量true/false。
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(boolEncoding: .numeric))
3.1.4 Data 参数编码(DataEncoding)
.deferredToData:使用Data原生Encodable支持;.base64(默认):编码为 Base 64 字符串;.custom((Data) throws -> String):自定义闭包。
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(dataEncoding: .base64))
3.1.5 Date 参数编码(DateEncoding)
.deferredToDate(默认):使用Date原生Encodable支持;.secondsSince1970:自 1970 年 1 月 1 日 UTC 午夜起的秒数;.millisecondsSince1970:同上,毫秒;.iso8601:按 ISO 8601 / RFC 3339 标准编码;.formatted(DateFormatter):使用给定DateFormatter;.custom((Date) throws -> String):自定义闭包。
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(dateEncoding: .iso8601))
3.1.6 编码键名(KeyEncoding)
.useDefaultKeys(默认):使用各类型指定的键;.convertToSnakeCase:oneTwoThree→one_two_three;.convertToKebabCase:oneTwoThree→one-two-three;.capitalized:仅首字母大写(UpperCamelCase):oneTwoThree→OneTwoThree;.uppercased:ONETWOTHREE;.lowercased:onetwothree;.custom((String) -> String):自定义闭包。
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(keyEncoding: .convertToSnakeCase))
3.1.7 对象键路径(KeyPathEncoding)
嵌套对象键路径默认使用方括号编码(如 parent[child][grandchild]):
.brackets:每个子键包裹方括号,如parent[child][grandchild];.dots:以点分隔,如parent.child.grandchild;
也可用自定义闭包创建编码,例如 KeyPathEncoding { "-\($0)" } 得到 parent-child-grandchild:
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(keyPathEncoding: .brackets))
3.1.8 空格编码(SpaceEncoding)
旧式表单编码器用 + 表示空格,部分服务端至今仍期望这种形式:
.percentEscaped(默认):标准百分号转义," "→"%20";.plusReplaced:" "→"+"。
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(spaceEncoding: .plusReplaced))
3.1.9 Optional 编码(NilEncoding)
Optional 在表单数据中没有标准编码方式,Alamofire 提供 NilEncoding:
.dropKey:nil值整体从输出中丢弃(与 Swift 其他编码器一致),如otherValue=2;.dropValue:丢弃值但保留键,如nilValue=&otherValue=2;.null:编码为字符串null,如nilValue=null&otherValue=2;
还可通过闭包创建自定义编码:
extension URLEncodedFormEncoder.NilEncoding {
static let customEncoding = NilEncoding { "customNilValue" }
}
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(nilEncoding: .dropKey))
3.2 JSONParameterEncoder
JSONParameterEncoder 使用 Swift 的 JSONEncoder 编码 Encodable 值并设置为 URLRequest 的 httpBody;若未设置,Content-Type 会被设为 application/json。
POST + JSON 编码参数:
let parameters: [String: [String]] = [
"foo": ["bar"],
"baz": ["a", "b"],
"qux": ["x", "y", "z"]
]
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: JSONParameterEncoder.default)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: JSONParameterEncoder.prettyPrinted)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: JSONParameterEncoder.sortedKeys)
// HTTP body: {"baz":["a","b"],"foo":["bar"],"qux":["x","y","z"]}
自定义 JSONEncoder:
let encoder = JSONEncoder()
encoder.dateEncoding = .iso8601
encoder.keyEncodingStrategy = .convertToSnakeCase
let parameterEncoder = JSONParameterEncoder(encoder: encoder)
脱离 Alamofire 手动编码 URLRequest:ParameterEncoder API 也可直接使用:
let url = URL(string: "https://httpbin.org/get")!
var urlRequest = URLRequest(url: url)
let parameters = ["foo": "bar"]
let encodedURLRequest = try URLEncodedFormParameterEncoder.default.encode(parameters,
into: urlRequest)
4. HTTP 头
Alamofire 提供了自己的 HTTPHeaders 类型(源码见 Source/Core/HTTPHeaders.swift):一个保序且大小写不敏感的 HTTP 头名/值对表示。单个键值对由 HTTPHeader 封装,并提供常用头的静态构造方法。
let headers: HTTPHeaders = [
"Authorization": "Basic VXNlcm5hbWU6UGFzc3dvcmQ=",
"Accept": "application/json"
]
AF.request("https://httpbin.org/headers", headers: headers).responseDecodable(of: DecodableType.self) { response in
debugPrint(response)
}
也可以由 HTTPHeader 数组构造:
let headers: HTTPHeaders = [
.authorization(username: "Username", password: "Password"),
.accept("application/json")
]
默认 Session 会为每个 Request 附带一组默认头:
Accept-Encoding,默认br;q=1.0, gzip;q=0.8, deflate;q=0.6(遵循 RFC 7230 §4.2.3);Accept-Language,默认为系统偏好语言的前 6 个,格式如en;q=1.0(遵循 RFC 7231 §5.3.5);User-Agent,包含当前应用版本信息,例如iOS Example/1.0 (com.alamofire.iOS-Example; build:1; iOS 13.0.0) Alamofire/5.0.0(遵循 RFC 7231 §5.5.3)。
这些默认头在 Source/Core/HTTPHeaders.swift 中由 HTTPHeaders.default 统一定义。若需定制,应创建自定义 URLSessionConfiguration 并更新其 headers 属性,再应用到新的 Session;使用 URLSessionConfiguration.af.default 可以在定制配置的同时保留 Alamofire 的默认头。这一机制的源码实现见 Source/Extensions/URLSessionConfiguration+Alamofire.swift,其中 af.default 与 af.ephemeral 都基于系统默认配置并附加 headers = .default。
对不变的 HTTP 头,建议在
URLSessionConfiguration上设置,这样底层URLSession创建的每个URLSessionTask都会自动应用,无需逐请求传入。
5. 响应验证
默认情况下,Alamofire 把任何已完成的请求都视为成功,无论响应内容如何。在响应处理器之前调用 validate(),会在响应状态码或 MIME 类型不可接受时产生错误。
自动验证:validate() 自动校验状态码在 200..<300 范围内,且(若请求提供了 Accept 头)响应 Content-Type 头与请求 Accept 头匹配:
AF.request("https://httpbin.org/get").validate().responseData { response in
debugPrint(response)
}
手动验证:
AF.request("https://httpbin.org/get")
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.responseData { response in
switch response.result {
case .success:
print("Validation Successful")
case let .failure(error):
print(error)
}
}
6. 响应处理
DataRequest 与 DownloadRequest 分别对应 DataResponse<Success, Failure: Error> 与 DownloadResponse<Success, Failure: Error>。两者都由"序列化类型 + 错误类型"两个泛型组成;默认错误类型都是 AFError,因此公开 API 使用简化的 AFDataResponse<Success> / AFDownloadResponse<Success>。UploadRequest 是 DataRequest 的子类,使用同样的 DataResponse 类型。
响应处理的本质是把回调闭包"挂"到 DataRequest 上,待请求完成后执行——而不是阻塞等待:
AF.request("https://httpbin.org/get").responseDecodable(of: DecodableType.self) { response in
debugPrint(response)
}
闭包接收的是由 DecodableResponseSerializer 根据 URLRequest、HTTPURLResponse、Data 和请求过程中产生的 Error 生成的 DataResponse<DecodableType, AFError> 值。
Alamofire 内置五种数据响应处理器:
// 1. Response Handler —— 未序列化的原始响应
func response(queue: DispatchQueue = .main,
completionHandler: @escaping (AFDataResponse<Data?>) -> Void) -> Self
// 2. Response Serializer Handler —— 使用传入的 Serializer 序列化
func response<Serializer: DataResponseSerializerProtocol>(queue: DispatchQueue = .main,
responseSerializer: Serializer,
completionHandler: @escaping (AFDataResponse<Serializer.SerializedObject>) -> Void) -> Self
// 3. Response Data Handler —— 序列化为 Data
func responseData(queue: DispatchQueue = .main,
dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods,
completionHandler: @escaping (AFDataResponse<Data>) -> Void) -> Self
// 4. Response String Handler —— 序列化为 String
func responseString(queue: DispatchQueue = .main,
dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor,
encoding: String.Encoding? = nil,
emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods,
completionHandler: @escaping (AFDataResponse<String>) -> Void) -> Self
// 5. Response Decodable Handler —— 序列化为 Decodable 类型
func responseDecodable<T: Decodable>(of type: T.Type = T.self,
queue: DispatchQueue = .main,
dataPreprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor,
decoder: DataDecoder = JSONDecoder(),
emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes,
emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods,
completionHandler: @escaping (AFDataResponse<T>) -> Void) -> Self
这些处理器都不会对 HTTPURLResponse 做任何验证——400..<500、500..<600 状态码不会自动触发 Error,需要通过第 5 节的 validate() 方法链实现。
6.1 各处理器说明
response:不评估任何响应数据,只是转发 URLSessionDelegate 直接给出的所有信息,相当于用 cURL 执行请求。官方强烈建议优先使用带 Response/Result 类型的其他序列化器。
AF.request("https://httpbin.org/get").response { response in
debugPrint("Response: \(response)")
}
responseData:使用 DataResponseSerializer 提取并验证服务端返回的 Data。无错误且有 Data 时,Result 为 .success,value 即服务端 Data。
AF.request("https://httpbin.org/get").responseData { response in
debugPrint("Response: \(response)")
}
responseString:使用 StringResponseSerializer 把 Data 按指定编码转为 String。未指定编码时,使用 HTTPURLResponse 中声明的文本编码;无法确定时回退到 .isoLatin1。
AF.request("https://httpbin.org/get").responseString { response in
debugPrint("Response: \(response)")
}
responseDecodable:使用 DecodableResponseSerializer 把 Data 通过指定 DataDecoder(对 Decoder 的协议抽象)解码为传入的 Decodable 类型。
struct DecodableType: Decodable { let url: String }
AF.request("https://httpbin.org/get").responseDecodable(of: DecodableType.self) { response in
debugPrint("Response: \(response)")
}
6.2 链式响应处理器
响应处理器可以链式添加:
AF.request("https://httpbin.org/get")
.responseString { response in
print("Response String: \(response.value)")
}
.responseDecodable(of: DecodableType.self) { response in
print("Response DecodableType: \(response.value)")
}
注意:同一
Request上的多个处理器意味着服务端数据要被序列化多次。生产环境应尽量避免,仅用于调试或确无更好选择时。
6.3 响应处理器队列
处理器闭包默认在 .main 队列执行,也可以传入指定 DispatchQueue。而实际的序列化工作(Data 转其他类型)始终在后台执行,位于发起请求的 Session 的 rootQueue 或(若提供了)serializationQueue 上:
let utilityQueue = DispatchQueue.global(qos: .utility)
AF.request("https://httpbin.org/get").responseDecodable(of: DecodableType.self, queue: utilityQueue) { response in
print("This closure is executed on utilityQueue.")
debugPrint(response)
}
7. 响应缓存
响应缓存在系统框架层面由 URLCache 处理:它提供内存 + 磁盘的复合缓存,并允许分别调整两部分大小。默认 Alamofire 使用 URLCache.shared 实例;若要定制所用的 URLCache,需通过自定义 Session 的 URLSessionConfiguration 实现(参见 AdvancedUsage.md 的 Session 配置章节)。
8. 认证
认证在系统框架层面由 URLCredential 与 URLAuthenticationChallenge 处理。支持 Basic、Digest、Kerberos、NTLM 等方案。注意:这套 API 面向会向客户端发起授权质询(challenge)的服务端,不适用于"必须携带 Authorization 头"的普通 API 场景(后者应走手动认证)。
HTTP Basic 认证:Request 的 authenticate 方法会在收到挑战时自动提供 URLCredential:
let user = "user"
let password = "password"
AF.request("https://httpbin.org/basic-auth/\(user)/\(password)")
.authenticate(username: user, password: password)
.responseDecodable(of: DecodableType.self) { response in
debugPrint(response)
}
使用 URLCredential:
let credential = URLCredential(user: user, password: password, persistence: .forSession)
AF.request("https://httpbin.org/basic-auth/\(user)/\(password)")
.authenticate(with: credential)
.responseDecodable(of: DecodableType.self) { response in
debugPrint(response)
}
使用
URLCredential时,若服务端发出挑战,底层URLSession实际会发起两次请求:第一次不带凭据(可能触发挑战),Alamofire 收到挑战后附加凭据,URLSession再重试。
手动认证:对必须始终携带 Authorization 头等认证信息而不发起质询的 API,直接加头即可:
let headers: HTTPHeaders = [.authorization(username: user, password: password)]
AF.request("https://httpbin.org/basic-auth/user/password", headers: headers)
.responseDecodable(of: DecodableType.self) { response in
debugPrint(response)
}
但凡是所有请求都需要的头,更合适的做法是放进自定义 URLSessionConfiguration,或使用 RequestAdapter(见 AdvancedUsage.md)。
9. 下载数据到文件
除把数据取入内存外,Alamofire 还提供 Session.download、DownloadRequest 与 DownloadResponse API 来下载到磁盘。小型 JSON 响应适合内存下载,而图片、视频等大资产应落盘以避免内存问题:
AF.download("https://httpbin.org/image/png").responseURL { response in
// 从给定的文件 URL 读取文件
}
DownloadRequest 除拥有 DataRequest 的全部响应处理器外还有 responseURL——它只返回下载数据所在位置的 URL,不会把 Data 从磁盘读入内存。而 responseDecodable 等其他处理器则需要从磁盘读取数据,大文件场景会占大量内存,需留意。
9.1 下载目标(Destination)
所有下载数据最初都存放在系统临时目录,系统会在未来某时删除它。若文件需要长期保留,必须提供 Destination 闭包把文件移走。在临时文件被移动到 destinationURL 之前,闭包返回的 Options 会先被执行,当前支持两个:
.createIntermediateDirectories:为目的地 URL 创建中间目录;.removePreviousFile:移除目的地 URL 上已存在的旧文件。
let destination: DownloadRequest.Destination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent("image.png")
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
AF.download("https://httpbin.org/image/png", to: destination).response { response in
debugPrint(response)
if response.error == nil, let imagePath = response.fileURL?.path {
let image = UIImage(contentsOfFile: imagePath)
}
}
也可以使用建议下载目录 API:
let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
AF.download("https://httpbin.org/image/png", to: destination)
9.2 下载进度
任何 DownloadRequest 都可通过 downloadProgress API 报告进度:
AF.download("https://httpbin.org/image/png")
.downloadProgress { progress in
print("Download Progress: \(progress.fractionCompleted)")
}
.responseData { response in
if let data = response.value {
let image = UIImage(data: data)
}
}
URLSession(从而 Alamofire)的进度报告只有当服务端正确返回Content-Length头时才有效;否则进度会停在0.0,直到下载完成瞬间跳到1.0。
downloadProgress 还接受 queue 参数,指定进度闭包执行的队列:
let progressQueue = DispatchQueue(label: "com.alamofire.progressQueue", qos: .utility)
AF.download("https://httpbin.org/image/png")
.downloadProgress(queue: progressQueue) { progress in
print("Download Progress: \(progress.fractionCompleted)")
}
.responseData { response in
if let data = response.value {
let image = UIImage(data: data)
}
}
9.3 取消与断点续传
除所有 Request 都有的 cancel() 外,DownloadRequest 还能生成 resume data 以便后续续传,有两种形式:
cancel(producingResumeData: Bool):控制是否产生 resume data,但它只通过DownloadResponse暴露;cancel(byProducingResumeData: (_ resumeData: Data?) -> Void):行为相同,但 resume data 直接通过完成闭包给出。
若 DownloadRequest 被取消或中断,底层 URLSessionDownloadTask 可能生成 resume data,可用于从断点重启下载。
重要:在部分 Apple 平台版本(iOS 10–10.2、macOS 10.12–10.12.2、tvOS 10–10.1、watchOS 3–3.1.1)上,后台
URLSessionConfiguration的resumeData存在系统级 bug,数据写入错误、始终无法恢复下载。
var resumeData: Data!
let download = AF.download("https://httpbin.org/image/png").responseData { response in
if let data = response.value {
let image = UIImage(data: data)
}
}
// download.cancel(producingResumeData: true) // resumeData 仅在 response 中可用
download.cancel { data in
resumeData = data
}
AF.download(resumingWith: resumeData).responseData { response in
if let data = response.value {
let image = UIImage(data: data)
}
}
10. 上传数据到服务端
用 JSON 或 URL 编码参数发送较小数据时,request() API 通常够用;需要发送来自内存 Data、文件 URL 或 InputStream 的大量数据时,则应使用 upload() API。
上传 Data:
let data = Data("data".utf8)
AF.upload(data, to: "https://httpbin.org/post").responseDecodable(of: DecodableType.self) { response in
debugPrint(response)
}
上传文件:
let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")
AF.upload(fileURL, to: "https://httpbin.org/post").responseDecodable(of: DecodableType.self) { response in
debugPrint(response)
}
上传 Multipart 表单数据(MultipartFormData 实现见 Source/Features/MultipartFormData.swift):
AF.upload(multipartFormData: { multipartFormData in
multipartFormData.append(Data("one".utf8), withName: "one")
multipartFormData.append(Data("two".utf8), withName: "two")
}, to: "https://httpbin.org/post")
.responseDecodable(of: DecodableType.self) { response in
debugPrint(response)
}
上传进度:任何 UploadRequest 都能通过 uploadProgress 与 downloadProgress 分别报告上传进度与响应数据下载进度:
AF.upload(fileURL, to: "https://httpbin.org/post")
.uploadProgress { progress in
print("Upload Progress: \(progress.fractionCompleted)")
}
.downloadProgress { progress in
print("Download Progress: \(progress.fractionCompleted)")
}
.responseDecodable(of: DecodableType.self) { response in
debugPrint(response)
}
11. 从服务端流式接收数据
大型下载或长连接场景更适合流式处理而非累积 Data。Alamofire 为此提供 DataStreamRequest 及关联 API(实现见 Source/Core/DataStreamRequest.swift)。它虽然提供与多数 Request 类似的 API,但有关键差异:DataStreamRequest 从不把 Data 累积在内存或写入磁盘,添加的 responseStream 闭包会随着 Data 到达而被反复调用,并在连接完成或出错时再被调用一次。
每个 Handler 闭包都捕获一个 Stream 值,其中既包含正在处理的 Event,也包含可取消请求的 CancellationToken:
public struct Stream<Success, Failure: Error> {
/// Latest `Event` from the stream.
public let event: Event<Success, Failure>
/// Token used to cancel the stream.
public let token: CancellationToken
/// Cancel the ongoing stream by canceling the underlying `DataStreamRequest`.
public func cancel() {
token.cancel()
}
}
Event 是表示两种流状态的枚举:
public enum Event<Success, Failure: Error> {
/// 每次收到新增 `Data` 时产生,关联值包含处理该 `Data` 的 `Result`。
case stream(Result<Success, Failure>)
/// 实例完成时产生(无论因流结束、取消还是错误),关联 `Completion` 值包含最终状态。
case complete(Completion)
}
Completion 包含流结束时 DataStreamRequest 的状态:
public struct Completion {
public let request: URLRequest? // 最后发出的 `URLRequest`
public let response: HTTPURLResponse? // 最后收到的 `HTTPURLResponse`
public let metrics: URLSessionTaskMetrics? // 最后产生的 `URLSessionTaskMetrics`
public let error: AFError? // 若有,实例产生的 `AFError`
}
11.1 流式 Data
func responseStream(on queue: DispatchQueue = .main, stream: @escaping Handler<Data, Never>) -> Self
queue 即 Handler 闭包的调用队列:
AF.streamRequest(...).responseStream { stream in
switch stream.event {
case let .stream(result):
switch result {
case let .success(data):
print(data)
}
case let .complete(completion):
print(completion)
}
}
上例中
Result的.failure分支无需处理,因为接收Data不可能失败。
11.2 流式 String
func responseStreamString(on queue: DispatchQueue = .main,
stream: @escaping StreamHandler<String, Never>) -> Self
String 值按 UTF8 解码,解码不会失败:
AF.streamRequest(...).responseStreamString { stream in
switch stream.event {
case let .stream(result):
switch result {
case let .success(string):
print(string)
}
case let .complete(completion):
print(completion)
}
}
11.3 流式 Decodable
func responseStreamDecodable<T: Decodable>(of type: T.Type = T.self,
on queue: DispatchQueue = .main,
using decoder: DataDecoder = JSONDecoder(),
preprocessor: DataPreprocessor = PassthroughPreprocessor(),
stream: @escaping Handler<T, AFError>) -> Self
解码失败不会终止流,而是在 Output 的 Result 中产生 AFError:
AF.streamRequest(...).responseStreamDecodable(of: SomeType.self) { stream in
switch stream.event {
case let .stream(result):
switch result {
case let .success(value):
print(value)
case let .failure(error):
print(error)
}
case let .complete(completion):
print(completion)
}
}
11.4 产生 InputStream
除了 StreamHandler 闭包,DataStreamRequest 还能产生一个 InputStream,按字节到达的方式读取:
func asInputStream(bufferSize: Int = 1024) -> InputStream
这样产生的 InputStream 在开始读取前必须调用 open()(或传给会自动打开它的 API)。方法返回后,调用方负责保持 InputStream 值存活并在读取完成后调用 close():
let inputStream = AF.streamRequest(...)
.responseStream { output in
...
}
.asInputStream()
11.5 流取消的四种方式
- 与其他
Request一样调用cancel(),取消底层 task 并完成流:
let request = AF.streamRequest(...).responseStream(...)
...
request.cancel()
- 当
DataStreamSerializer遇到错误时自动取消。该行为默认关闭,创建请求时传入automaticallyCancelOnStreamError: true开启:
AF.streamRequest(..., automaticallyCancelOnStreamError: true).responseStream(...)
- 从
Handler闭包中抛出错误也会取消请求,该错误会被保存在请求上并可在Completion值中获取:
AF.streamRequest(...).responseStream { stream in
// Process stream.
throw SomeError() // Cancels request.
}
- 通过
Stream值上的cancel()方法取消:
AF.streamRequest(...).responseStream { stream in
// Decide to cancel request.
stream.cancel()
}
12. 统计指标:URLSessionTaskMetrics
Alamofire 会为每个 Request 收集 URLSessionTaskMetrics,其中封装了底层网络连接与请求/响应时序的丰富统计信息:
AF.request("https://httpbin.org/get").responseDecodable(of: DecodableType.self) { response in
print(response.metrics)
}
由于系统缺陷
FB7624529,watchOS 上URLSessionTaskMetrics的收集目前是禁用的。
13. cURL 命令输出:请求调试利器
调试平台问题往往令人沮丧。Alamofire 的 Request 类型可以生成等价的 cURL 命令以便调试。由于 Request 的创建是异步的,该 API 同时提供同步与异步版本。想尽快拿到 cURL 命令,可在请求上链式调用 cURLDescription:
AF.request("https://httpbin.org/get")
.cURLDescription { description in
print(description)
}
.responseDecodable(of: DecodableType.self) { response in
debugPrint(response.metrics)
}
其典型输出:
$ curl -v \
-X GET \
-H "Accept-Language: en;q=1.0" \
-H "Accept-Encoding: br;q=1.0, gzip;q=0.9, deflate;q=0.8" \
-H "User-Agent: Demo/1.0 (com.demo.Demo; build:1; iOS 15.0.0) Alamofire/1.0" \
"https://httpbin.org/get"
把该命令粘到终端执行,即可脱离 App 环境复现同一请求,快速定位是客户端行为还是服务端问题。
14. 小结与延伸阅读
本文覆盖了 Documentation/Usage.md 的全部实战要点:AF 全局引用与两种 Session.request 入口、RequestModifier 定制、完整的 ParameterEncoder 体系(含 URLEncodedFormEncoder 的排序、数组、布尔、Data、Date、键名、键路径、空格与 nil 九类编码定制)、默认 HTTP 头及其定制途径、验证链、五种响应处理器与队列语义、URLCache 缓存、三种认证方式、下载目标与断点续传、上传与进度、DataStreamRequest 的流式处理模型与四种取消方式、URLSessionTaskMetrics 指标与 cURL 调试输出。
需要进一步深入时,可结合以下仓库资源:AdvancedUsage.md 讲解 async/await、自定义 Session 配置、RequestAdapter/RequestRetrier 等高级主题;核心实现在 Source/Core/ 目录(Session.swift、DataRequest.swift、DownloadRequest.swift、DataStreamRequest.swift、Request.swift 等),功能模块在 Source/Features/ 目录(ParameterEncoder.swift、URLEncodedFormEncoder.swift、Validation.swift、ResponseSerialization.swift 等),对应的测试用例(如 Tests/ParameterEncodingTests.swift、Tests/DownloadTests.swift、Tests/DataStreamTests.swift)可用于验证各行为的预期边界。当前仓库版本为 5.12.0(见 Source/Alamofire.swift),要求 Swift 6.0 及以上编译器。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00