Alamofire 基礎(chǔ)使用

目錄

一簇秒、發(fā)起請求
  • 1. 參數(shù) <Parameter:Encodable>
  • 2. ParameterEncoder 參數(shù)編碼器
    * 2.1 JSON參數(shù)編碼器
    * 2.2 Form參數(shù)編碼器
  • 3. HTTP Headers 請求頭設(shè)置
  • 4. 響應(yīng)處理
二、下載文件
  • 下載Data
  • 下載到指定目錄
  • 下載進度
  • 恢復(fù)下載
三秀鞭、上傳文件
  • 上傳 Data
  • 上傳文件
  • 上傳 Multipart Data
  • 上傳進度

Alamofire是基于Apple提供的 URL加載系統(tǒng)趋观,核心是URLSession和URLSessionTask子類。 早期版本使用Alamofire.request()锋边,5.0版本使用AF命名皱坛,AF引用Session.default

一、發(fā)起請求

簡單示例:

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

兩個發(fā)起請求的API

/// 1. 配置請求組成 URLRequest豆巨、本篇主要內(nèi)容(基礎(chǔ)用法)
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. 參數(shù) <Parameter:Encodable>

只要參數(shù)遵循Encodable協(xié)議麸恍,那么最終ParameterEncoder都會把Parameter encode成需要的數(shù)據(jù)類型

舉個例子

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 參數(shù)編碼器

參數(shù)編碼器 最終決定了參數(shù)在請求中的存在的樣式

我們通過參數(shù)編碼器把參數(shù)編碼成服務(wù)器需要的數(shù)據(jù)類型

Alamofire提供了2個兩種編碼器

  • JSONParameterEncoder
  • URLEncodedFormParameterEncoder
2.1 JSON參數(shù)編碼器

JSONParameterEncoder 對應(yīng)的 Content-Typeapplication/json

  • default
  • prettyPrinted 好看的格式打印
  • sortedKeys key排序
/// 最終都是調(diào)用 encode 將參數(shù)轉(zhuǎn)為 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參數(shù)編碼器

URLEncodedFormParameterEncoder 對應(yīng)的 Content-Type

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

Form參數(shù)編碼器只有一個默認的編碼器default

  • Destination 決定參數(shù)存放的位置
    • methodDependent [.get, .head, .delete] 拼接URL ,默認使用此方式
    • queryString 拼接URL
    • httpBody
/// httpBody 調(diào)用encode 將參數(shù)轉(zhuǎn)為 Data 保存在 httpBody 中
public func encode(_ value: Encodable) throws -> Data
/// 拼接URL獲得新的URL
public func encode(_ value: Encodable) throws -> String {
    ...
    URLEncodedFormEncoder.encode(value)
    ...
}
  • URLEncodedFormEncoder 決定了參數(shù)中不同類型的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 請求頭設(shè)置

提供3種初始化方式
/// 1. 無參構(gòu)造
public init() {}

/// 通過以下方式添加值
func add(name: String, value: String)
func add(_ header: HTTPHeader)
/// 2. 通過 HTTPHeader 數(shù)組構(gòu)造
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 構(gòu)造
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. 響應(yīng)處理

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)
        }
    }
恢復(fù)下載
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的網(wǎng)絡(luò)庫使用:PomeloNet

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瓤球,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子敏弃,更是在濱河造成了極大的恐慌卦羡,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異绿饵,居然都是意外死亡欠肾,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門拟赊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刺桃,“玉大人,你說我怎么就攤上這事吸祟∩龋” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵屋匕,是天一觀的道長葛碧。 經(jīng)常有香客問我,道長过吻,這世上最難降的妖魔是什么进泼? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮纤虽,結(jié)果婚禮上乳绕,老公的妹妹穿的比我還像新娘。我一直安慰自己廓推,他們只是感情好刷袍,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著樊展,像睡著了一般呻纹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上专缠,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天雷酪,我揣著相機與錄音,去河邊找鬼涝婉。 笑死哥力,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的墩弯。 我是一名探鬼主播吩跋,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼渔工!你這毒婦竟也來了锌钮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤引矩,失蹤者是張志新(化名)和其女友劉穎梁丘,沒想到半個月后侵浸,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡氛谜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年掏觉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片值漫。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡澳腹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出惭嚣,到底是詐尸還是另有隱情遵湖,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布晚吞,位于F島的核電站延旧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏槽地。R本人自食惡果不足惜迁沫,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望捌蚊。 院中可真熱鬧集畅,春花似錦、人聲如沸缅糟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窗宦。三九已至赦颇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赴涵,已是汗流浹背价捧。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工梧奢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人撮躁。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓植榕,卻偏偏與公主長得像烟号,于是被迫代替她去往敵國和親立美。 傳聞我的和親對象是個殘疾皇子癣籽,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355