Alamofire 提供了优雅可组合的接口构建Http网络请求。它不是自己实现网络请求,而是建立在苹果底层库提供的 URL Loading System 之上。系统的核心是 URLSession 和 URLSessionTask 的子类们。Alamofire 包装了这些APis, 和其他的APis, 更方便的调用, 提供多种使用http网络的现在应用开发必备的函数。然而, 更重要的是知道 Alamofire的多种行为从哪里来的,熟悉 URL loading System 是非常重要的。最终,网络功能受系统限制,并且时刻记得和尊重规则。
此外,Alamofire(通常是URL加载系统)中的联网是异步完成的。 异步编程可能会使不熟悉该概念的程序员感到沮丧,但这样做的理由非常充分。
旁白:AF命名空间
Alamofire的文档的早期版本使用了Alamofire.request()之类的示例。 尽管该API似乎需要Alamofire前缀,但实际上在没有它的情况下效果很好。 导入Alamofire的任何文件中均可全局使用该请求方法和其他功能。 从Alamofire 5开始,此功能已移出全局名称空间,并移入AF枚举(充当名称空间)。 这允许Alamofire提供相同的便利功能,而不必每次使用Alamofire都污染全局名称空间。 同样,由Alamofire扩展的类型将使用af属性扩展名将Alamofire添加的功能与其他扩展名分开。
Alamofire 提供了多种便捷方法来创建http请求。 最简单的,只提供一个String 就可以转成成 一个URL对象:
AF.request("https://httpbin.org/get").response { response in
debugPrint(response) }
类型中用于发出请求的两个顶级API的一种形式。 它的完整定义如下所示:
open func request<Parameters: Encodable>(_ convertible: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default,
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil) -> DataRequest
AF包含自己的HTTPHeaders, 保留顺序,不区分大小写,header键值对。 HTTPHeader类型封装了 一个键值对。
let headers: HTTPHeaders = [
"Authorization": "Basic VXNlcm5hbWU6UGFzc3dvcmQ=",
"Accept": "application/json"
]
AF.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
debugPrint(response)
}
let headers: HTTPHeaders = [
.authorization(username: "Username", password: "Password"),
.accept("application/json")
]
AF.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
debugPrint(response)
}
数组,map 都行
如果headers是不变的,推荐用 URLSessionConfiguration 来设置。会自动的应用到 URLSeesionTask上。
默认的 Alamofire Session提供了默认的 headers。
如果需要自定义这些header, 应该创建一个自定义的URLSessionConfiguration, 更新 defaultHTTPHeaders 属性。
默认情况下,无论响应内容如何,Alamofire都会将所有已完成的请求视为成功。 如果响应的状态码或MIME类型不可接受,则在响应处理程序之前调用validate()会导致生成错误。
AF.request("https://httpbin.org/get").responseJSON { response in
debugPrint(response)
}
Alamofire 默认有6种方式处理响应:
// Response Handler - Unserialized Response
func response(queue: DispatchQueue = .main,
completionHandler: @escaping (AFDataResponse<Data?>) -> Void) -> Self
// Response Serializer Handler - Serialize using the passed Serializer
func response<Serializer: DataResponseSerializerProtocol>(queue: DispatchQueue = .main,
responseSerializer: Serializer,
completionHandler: @escaping (AFDataResponse<Serializer.SerializedObject>) -> Void) -> Self
// Response Data Handler - Serialized into Data
func responseData(queue: DispatchQueue = .main,
completionHandler: @escaping (AFDataResponse<Data>) -> Void) -> Self
// Response String Handler - Serialized into String
func responseString(queue: DispatchQueue = .main,
encoding: String.Encoding? = nil,
completionHandler: @escaping (AFDataResponse<String>) -> Void) -> Self
// Response JSON Handler - Serialized into Any Using JSONSerialization
func responseJSON(queue: DispatchQueue = .main,
options: JSONSerialization.ReadingOptions = .allowFragments,
completionHandler: @escaping (AFDataResponse<Any>) -> Void) -> Self
// Response Decodable Handler - Serialized into Decodable Type
func responseDecodable<T: Decodable>(of type: T.Type = T.self,
queue: DispatchQueue = .main,
decoder: DataDecoder = JSONDecoder(),
completionHandler: @escaping (AFDataResponse<T>) -> Void) -> Self
response handler 不处理数据,只是简单的从URLSessionDelegate转发数据。相当于 使用 cURL执行请求
AF.request("https://httpbin.org/get").response { response in
debugPrint("Response: \(response)")
}
responseData handler 使用 DataResponseSerializer 来提取验证服务端返回的数据。如果没有错误并且data返回了。响应结果 Result 将会 是 .success ,并且从服务端返回值
AF.request("https://httpbin.org/get").responseData { response in
debugPrint("Response: \(response)")
}
解析成字符串
AF.request("https://httpbin.org/get").responseString { response in
debugPrint("Response: \(response)")
}
解析成 Json
AF.request("https://httpbin.org/get").responseJSON { response in
debugPrint("Response: \(response)")
}
自定义解析方法
struct HTTPBinResponse: Decodable { let url: String }
AF.request("https://httpbin.org/get").responseDecodable(of: HTTPBinResponse.self) { response in
debugPrint("Response: \(response)")
}
请求默认是主线程。可指定一个队列来处理
let utilityQueue = DispatchQueue.global(qos: .utility)
AF.request("https://httpbin.org/get").responseJSON(queue: utilityQueue) { response in
print("Executed on utility queue.")
debugPrint(response)
}
下载解析数量会耗费大量内存
下载目录默认是系统临时目录,系统会在未来某个时间点删除掉,如果要保存时间更长一点,需要移动文件。
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)
下载进度
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)
}
}
只有当服务端返回了 Content-Length 头部 才能实现
另外,downloadProgress提供了一个 携带 queue参数的请求
let utilityQueue = DispatchQueue.global(qos: .utility)
AF.download("https://httpbin.org/image/png")
.downloadProgress(queue: utilityQueue) { progress in
print("Download Progress: \(progress.fractionCompleted)")
}
.responseData { response in
if let data = response.value {
let image = UIImage(data: data)
}
}
取消和回复请求,所有的Request 都有 cancel操作, DownloadRequest还有 resume操作。
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) // Makes resumeData available in response only.
download.cancel { data in
resumeData = data
}
AF.download(resumingWith: resumeData).responseData { response in
if let data = response.value {
let image = UIImage(data: data)
}
}