Alamofire 基础使用

目录

一、发起请求
  • 1. 参数 <Parameter:Encodable>
  • 2. ParameterEncoder 参数编码器
    * 2.1 JSON参数编码器
    * 2.2 Form参数编码器
  • 3. HTTP Headers 请求头设置
  • 4. 响应处理
二、下载文件
  • 下载Data
  • 下载到指定目录
  • 下载进度
  • 恢复下载
三、上传文件
  • 上传 Data
  • 上传文件
  • 上传 Multipart Data
  • 上传进度

Alamofire是基于Apple提供的 URL加载系统,核心是URLSession和URLSessionTask子类。 早期版本使用Alamofire.request(),5.0版本使用AF命名,AF引用Session.default

一、发起请求

简单示例:

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

两个发起请求的API

/// 1. 配置请求组成 URLRequest、本篇主要内容(基础用法)
open func request<Parameters: Encodable>(_ convertible: URLConvertible,
                                         method: HTTPMethod = .get,
                                         parameters: Parameters? = nil,
                                         encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default,
                                         headers: HTTPHeaders? = nil,
                                         interceptor: RequestInterceptor? = nil) -> DataRequest

/// 2. 直接请求组装好的 URLRequest (高级用法)
open func request(_ urlRequest: URLRequestConvertible, 
                  interceptor: RequestInterceptor? = nil) -> DataRequest

1. 参数 <Parameter:Encodable>

只要参数遵循Encodable协议,那么最终ParameterEncoder都会把Parameter encode成需要的数据类型

举个例子

struct Login: Encodable {
    let email:String
    let password:String
}

let login = Login(email: "aaa", password: "bbb")
AF.request("https://httpbin.org/post",
               method: .post,
               parameters: login,
               encoder: JSONParameterEncoder.default)
    .response { response in
        debugPrint(response)
}

2. ParameterEncoder 参数编码器

参数编码器 最终决定了参数在请求中的存在的样式

我们通过参数编码器把参数编码成服务器需要的数据类型

Alamofire提供了2个两种编码器

  • JSONParameterEncoder
  • URLEncodedFormParameterEncoder
2.1 JSON参数编码器

JSONParameterEncoder 对应的 Content-Typeapplication/json

  • default
  • prettyPrinted 好看的格式打印
  • sortedKeys key排序
/// 最终都是调用 encode 将参数转为 Data 保存在 httpBody 中
open func encode<T>(_ value: T) throws -> Data where T : Encodable

let data = try encoder.encode(parameters)
request.httpBody = data
2.2 Form参数编码器

URLEncodedFormParameterEncoder 对应的 Content-Type

application/x-www-form-urlencoded; charset=utf-8

Form参数编码器只有一个默认的编码器default

  • Destination 决定参数存放的位置
    • methodDependent [.get, .head, .delete] 拼接URL ,默认使用此方式
    • queryString 拼接URL
    • httpBody
/// httpBody 调用encode 将参数转为 Data 保存在 httpBody 中
public func encode(_ value: Encodable) throws -> Data
/// 拼接URL获得新的URL
public func encode(_ value: Encodable) throws -> String {
    ...
    URLEncodedFormEncoder.encode(value)
    ...
}
  • URLEncodedFormEncoder 决定了参数中不同类型的key,value编码方式
    • ArrayEncoding
      • brackets key[] 默认
      • noBrackets key
    • BoolEncoding
      • numeric true as 1, false as 0. 默认
      • literal true as "true", false as "false"
    • DataEncoding
      • deferredToData nil
      • base64 data.base64EncodedString() 默认
      • custom((Data) throws -> String)
    • DateEncoding
      • deferredToDate nil默认
      • secondsSince1970 String(date.timeIntervalSince1970)
      • millisecondsSince1970 String(date.timeIntervalSince1970 * 1000.0)
      • iso8601 ISO8601DateFormatter()
      • formatted(DateFormatter) formatter.string(from: date)
      • custom((Date) throws -> String).
    • KeyEncoding
      • useDefaultKeys 原样式 默认
      • convertToSnakeCase oneTwoThree becomes one_two_three
      • convertToKebabCase oneTwoThree becomes one-two-three.
      • capitalized oneTwoThree becomes OneTwoThree
      • uppercased oneTwoThree becomes ONETWOTHREE.
      • lowercased oneTwoThree becomes onetwothree
      • custom((String) -> String)
    • SpaceEncoding
      • percentEscaped string.replacingOccurrences(of: " ", with: "%20")
      • plusReplaced string.replacingOccurrences(of: " ", with: "+")
    • alphabetizeKeyValuePairs key/value 按照字母排序,默认true

3. HTTP Headers 请求头设置

提供3种初始化方式
/// 1. 无参构造
public init() {}

/// 通过以下方式添加值
func add(name: String, value: String)
func add(_ header: HTTPHeader)
/// 2. 通过 HTTPHeader 数组构造
public init(_ headers: [HTTPHeader])

let headers: HTTPHeaders = [
    HTTPHeader(name: "Authorization", value: "Basic VXNlcm5hbWU6UGFzc3dvcmQ="),
    HTTPHeader(name: "Accept", value: "application/json")
]

AF.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
    debugPrint(response)
}
/// 3. 通过key/value 构造
public init(_ dictionary: [String: String])

let headers: HTTPHeaders = [
    "Authorization": "Basic VXNlcm5hbWU6UGFzc3dvcmQ=",
    "Accept": "application/json"
]

AF.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
    debugPrint(response)
}

4. 响应处理

Alamofire 提供了4种 Response序列化工具

  • DataResponseSerializer 解析为Data
// Response Handler - Unserialized Response
func response(queue: DispatchQueue = .main, 
              completionHandler: @escaping (AFDataResponse<Data?>) -> Void) -> Self

// Response Data Handler - Serialized into 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

//示例
AF.request("https://httpbin.org/get").responseData { response in
    debugPrint("Response: \(response)")
}
  • StringResponseSerializer 解析为String
// Response String Handler - Serialized into 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



//示例
AF.request("https://httpbin.org/get").responseString { response in
    debugPrint("Response: \(response)")
}
  • JSONResponseSerializer 解析为JSON
// Response JSON Handler - Serialized into Any Using JSONSerialization
func responseJSON(queue: DispatchQueue = .main,
                  dataPreprocessor: DataPreprocessor = JSONResponseSerializer.defaultDataPreprocessor,
                  emptyResponseCodes: Set<Int> = JSONResponseSerializer.defaultEmptyResponseCodes,
                  emptyRequestMethods: Set<HTTPMethod> = JSONResponseSerializer.defaultEmptyRequestMethods,
                  options: JSONSerialization.ReadingOptions = .allowFragments,
                  completionHandler: @escaping (AFDataResponse<Any>) -> Void) -> Self
//示例
AF.request("https://httpbin.org/get").responseJSON { response in
    debugPrint("Response: \(response)")
}
  • DecodableResponseSerializer 解析为指定类型<T: Decodable>
// Response Decodable Handler - Serialized into Decodable Type
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

//示例
struct HTTPBinResponse: Decodable { let url: String }
AF.request("https://httpbin.org/get").responseDecodable(of: HTTPBinResponse.self) { response in
    debugPrint("Response: \(response)")
}
  • DataResponseSerializerProtocol 使用自定义解析 Serializer: DataResponseSerializerProtocol
// Response Serializer Handler - Serialize using the passed Serializer
func response<Serializer: DataResponseSerializerProtocol>(queue: DispatchQueue = .main,
                                                          responseSerializer: Serializer,
                                                          completionHandler: @escaping (AFDataResponse<Serializer.SerializedObject>) -> Void) -> Self

//示例 自定义解析为 Dictionary
public final class DictionaryResponseSerializer: ResponseSerializer {
    public func serialize(request: URLRequest?,
                          response: HTTPURLResponse?,
                          data: Data?, error: Error?) throws -> Dictionary<String, Any> {
        guard error == nil else { throw error! }

        guard let data = data, !data.isEmpty else {
            guard emptyResponseAllowed(forRequest: request, response: response) else {
                throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)
            }
            return [:]
        }
        do {
            return try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.fragmentsAllowed) as! Dictionary<String, Any>
        } catch {
            throw AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error))
        }
    }
}

AF.request("https://httpbin.org/get").response(responseSerializer: DictionaryResponseSerializer()) { (response) in
    switch response.result {
    case .success(let bin):
        debugPrint(bin)
    case .failure(let error):
        debugPrint(error)
    }
}

二、下载文件

下载Data
AF.download("https://httpbin.org/image/png").responseData { response in
    if let data = response.value {
        let image = UIImage(data: data)
    }
}
下载到指定目录
let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
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)
    }
}
下载进度
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)
        }
    }
恢复下载
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)
    }
}

三、上传文件

上传 Data

let data = Data("data".utf8)

AF.upload(data, to: "https://httpbin.org/post").responseDecodable(of: HTTPBinResponse.self) { response in
    debugPrint(response)
}

上传文件

let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")

AF.upload(fileURL, to: "https://httpbin.org/post").responseDecodable(of: HTTPBinResponse.self) { response in
    debugPrint(response)
}

上传 Multipart Data

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: HTTPBinResponse.self) { response in
        debugPrint(response)
    }

上传进度

let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")

AF.upload(fileURL, to: "https://httpbin.org/post")
    .uploadProgress { progress in
        print("Upload Progress: \(progress.fractionCompleted)")
    }
    .responseDecodable(of: HTTPBinResponse.self) { response in
        debugPrint(response)
    }

最后封装了一个基于Moya&RXSwift&HandyJSON的网络库使用:PomeloNet

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272