首页
/ Alamofire 完整使用指南:从发起请求到流式处理的实战详解

Alamofire 完整使用指南:从发起请求到流式处理的实战详解

2026-09-05 15:59:40作者:仰钰奇

本文基于 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(核心为 URLSessionURLSessionTask 子类)之上。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。最简单的请求只需一个可转换为 URLString

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,最后用 encoderparameters 编码进请求。

第二种接受任意 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.swiftconnectdeletegetheadoptionspatchpostputquerytrace。注意其 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):JSONParameterEncoderURLEncodedFormParameterEncoder,覆盖现代服务最常见的两种编码。

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 编码为 1false 编码为 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(默认):使用各类型指定的键;
  • .convertToSnakeCaseoneTwoThreeone_two_three
  • .convertToKebabCaseoneTwoThreeone-two-three
  • .capitalized:仅首字母大写(UpperCamelCase):oneTwoThreeOneTwoThree
  • .uppercasedONETWOTHREE
  • .lowercasedonetwothree
  • .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

  • .dropKeynil 值整体从输出中丢弃(与 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 值并设置为 URLRequesthttpBody;若未设置,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 手动编码 URLRequestParameterEncoder 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.defaultaf.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. 响应处理

DataRequestDownloadRequest 分别对应 DataResponse<Success, Failure: Error>DownloadResponse<Success, Failure: Error>。两者都由"序列化类型 + 错误类型"两个泛型组成;默认错误类型都是 AFError,因此公开 API 使用简化的 AFDataResponse<Success> / AFDownloadResponse<Success>UploadRequestDataRequest 的子类,使用同样的 DataResponse 类型。

响应处理的本质是把回调闭包"挂"到 DataRequest 上,待请求完成后执行——而不是阻塞等待:

AF.request("https://httpbin.org/get").responseDecodable(of: DecodableType.self) { response in
    debugPrint(response)
}

闭包接收的是由 DecodableResponseSerializer 根据 URLRequestHTTPURLResponseData 和请求过程中产生的 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..<500500..<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.successvalue 即服务端 Data

AF.request("https://httpbin.org/get").responseData { response in
    debugPrint("Response: \(response)")
}

responseString:使用 StringResponseSerializerData 按指定编码转为 String。未指定编码时,使用 HTTPURLResponse 中声明的文本编码;无法确定时回退到 .isoLatin1

AF.request("https://httpbin.org/get").responseString { response in
    debugPrint("Response: \(response)")
}

responseDecodable:使用 DecodableResponseSerializerData 通过指定 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 转其他类型)始终在后台执行,位于发起请求的 SessionrootQueue 或(若提供了)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,需通过自定义 SessionURLSessionConfiguration 实现(参见 AdvancedUsage.md 的 Session 配置章节)。

8. 认证

认证在系统框架层面由 URLCredentialURLAuthenticationChallenge 处理。支持 Basic、Digest、Kerberos、NTLM 等方案。注意:这套 API 面向会向客户端发起授权质询(challenge)的服务端,不适用于"必须携带 Authorization 头"的普通 API 场景(后者应走手动认证)。

HTTP Basic 认证Requestauthenticate 方法会在收到挑战时自动提供 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.downloadDownloadRequestDownloadResponse 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)上,后台 URLSessionConfigurationresumeData 存在系统级 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、文件 URLInputStream大量数据时,则应使用 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 都能通过 uploadProgressdownloadProgress 分别报告上传进度与响应数据下载进度:

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

queueHandler 闭包的调用队列:

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

解码失败不会终止流,而是在 OutputResult 中产生 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 流取消的四种方式

  1. 与其他 Request 一样调用 cancel(),取消底层 task 并完成流:
let request = AF.streamRequest(...).responseStream(...)
...
request.cancel()
  1. DataStreamSerializer 遇到错误时自动取消。该行为默认关闭,创建请求时传入 automaticallyCancelOnStreamError: true 开启:
AF.streamRequest(..., automaticallyCancelOnStreamError: true).responseStream(...)
  1. Handler 闭包中抛出错误也会取消请求,该错误会被保存在请求上并可在 Completion 值中获取:
AF.streamRequest(...).responseStream { stream in
    // Process stream.
    throw SomeError() // Cancels request.
}
  1. 通过 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 的排序、数组、布尔、DataDate、键名、键路径、空格与 nil 九类编码定制)、默认 HTTP 头及其定制途径、验证链、五种响应处理器与队列语义、URLCache 缓存、三种认证方式、下载目标与断点续传、上传与进度、DataStreamRequest 的流式处理模型与四种取消方式、URLSessionTaskMetrics 指标与 cURL 调试输出。

需要进一步深入时,可结合以下仓库资源:AdvancedUsage.md 讲解 async/await、自定义 Session 配置、RequestAdapter/RequestRetrier 等高级主题;核心实现在 Source/Core/ 目录(Session.swiftDataRequest.swiftDownloadRequest.swiftDataStreamRequest.swiftRequest.swift 等),功能模块在 Source/Features/ 目录(ParameterEncoder.swiftURLEncodedFormEncoder.swiftValidation.swiftResponseSerialization.swift 等),对应的测试用例(如 Tests/ParameterEncodingTests.swiftTests/DownloadTests.swiftTests/DataStreamTests.swift)可用于验证各行为的预期边界。当前仓库版本为 5.12.0(见 Source/Alamofire.swift),要求 Swift 6.0 及以上编译器。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384