当前位置: 首页 > 工具软件 > Alamofire > 使用案例 >

Alamofire入门笔记

龙新荣
2023-12-01

新人开始学习IOS开发,今天看了Alamofire的基本命令,参考了官网,总结一下,官方文档如下。可以用cocapods引入alamofire库:

https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md

  1. 发送请求:
AF.request("<url>").reponse { … }

此方法的完整定义:

open func request<Parameters: Encodable>(_ convertible: URLConvertible,
                                         method: HTTPMethod = .get,
                                         parameters: Parameters? = nil,
                                         encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default,
                                         headers: HTTPHeaders? = nil,
                                         interceptor: RequestInterceptor? = nil) -> DataRequest

第一行的例子其实使用的是这个简单的定义:

open func request(_urlRequest: URLRequestConvertible, 
                  interceptor: RequestInterceptor?=nil) ->DataRequest
  1. 可以使用Modifier来自定义请求
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(...)
  1. 往请求里面添加参数
    参数必须是Encodable类型,并且满足ParameterEncoder协议。如下例子就是创建一个Login请求的例子:
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)
}

encoder有两种,一种是JSON格式的JSONParameterEncoder,另一种是URLEncodedFormParameterEncoder

  1. 通过URL编码格式发送请求
    通过URL发送请求的API参数不同,参数有:(请求路径、请求参数(可选)、请求编译器(.methodDependent/.queryString/.httpBody)),编译器根据请求的Http类型决定。

例如POST请求:
let parameters: [String: [String]] = [
“foo”: [“bar”],
“baz”: [“a”, “b”],
“qux”: [“x”, “y”, “z”]
]

// All three of these calls are equivalent
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))

// HTTP body: “qux[]=x&qux[]=y&qux[]=z&baz[]=a&baz[]=b&foo[]=bar”

  1. URLEncodedFormEncoder
    从Swift4.x开始,对字典采用哈希算法会导致在传入Collection类型的时候内部排序混乱,可以使用URLEncodedFormEncoder来对需要编码的键值对进行排序。
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(alphabetizeKeyValuePairs: false))

Format的传参根据需要进行编码的类型不同而不同,每一个参数对应的方法也不同,具体可以参考官方文档:
https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#manual-authentication

  1. 通过JSON编码发送请求
    通过JSON发送请求似乎就没有URL那么麻烦了。编码器的种类有3种,都是可以根据名字知道意思的。例子如下:
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"]}

可以自定义JSON编码器行为,通过穿件JSONEncoder实例来实现:

let encoder = JSONEncoder()
encoder.dateEncoding = .iso8601
encoder.keyEncodingStrategy = .convertToSnakeCase
let parameterEncoder = JSONParameterEncoder(encoder: encoder)

也可以在外部手动创建参数:

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)
  1. HttpHeader:
    创建HttpHeader的方式有两种:
    一种是类似于在POSTMAN的Header中手动填写键值对的方式:
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)
}

另一种是类似于POSTMAN中填写username和password的方式:

let headers: HTTPHeaders = [
    .authorization(username: "Username", password: "Password"),
    .accept("application/json")
]

AF.request("https://httpbin.org/headers", headers: headers).responseDecodable(of: DecodableType.self) { response in
    debugPrint(response)
}
  1. Response验证
    自动化验证(自动化验证会要求返回的Http状态码在200-300之间):
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)
        }
    }
  1. Response处理
    alamofire有5种响应体:
// 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,
                  dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
                  emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
                  emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods,
                  completionHandler: @escaping (AFDataResponse<Data>) -> Void) -> Self

// 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

// 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

1)Response Handler - 不做任何处理,仅仅根据url进行返回:

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

2)Response Data Handler - 通过序列化提取出响应的数据:

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

3)Response String Handler - 通过序列化提取出响应的String:

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

4)Response Decodable Handler - 通过序列化将Data转换为DataDecoder(类型自己定义)

struct DecodableType: Decodable { let url: String }

AF.request("https://httpbin.org/get").responseDecodable(of: DecodableType.self) { response in
    debugPrint("Response: \(response)")
}
  1. 认证
    认证的几种方式:

1)直接在url种提供:

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)
    }

2)上面的变体,实例化URLCredential对象:

let user = "user"
let password = "password"

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)
    }

3)手动认证,提高复用,简化代码(不用调用authenticate这个API,看着烦):

let user = "user"
let password = "password"

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)
    }
  1. 下载file
    alamofire支持下载到内存或者硬盘上,下载的文件都会放在一个临时文件夹里,需要及时转移。

1)基本例子:

AF.download("https://httpbin.org/image/png").responseURL { response in
    // Read file from provided file URL.
}

2)下载到目的地文件:
正如前面所说的,所有的文件下载都会放在一个临时区,需要制定destination。提供Destination,会在文件移动到destination之前执行其中的闭包操作,可以指定两种:
.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)

3)报告下载进度:
使用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)
        }
    }

4)取消和恢复下载:
取消下载提供了两个API:
cancel(producingResumeData: Bool):允许控制是否恢复下载,只能在DownloadResponse上用
cancel(byProducingResumeData: (_ resumeData: Data?) -> Void):允许恢复下载,但是只能在完成程序中使用

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)
    }
}
  1. 上传file
    如果只是少量的数据(比如发送一个数据对象),用request就足够了。但是如果要发送图片之类的,可以使用upload()
    下面是三个上传file的例子:
    1)
let data = Data("data".utf8)

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

2)

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)
}

3)

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)
    }
  1. 展示上传进度
    和下载一样,上传也可以展示上传的进度,使用uploadProgress API:
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)")
    }
    .downloadProgress { progress in
        print("Download Progress: \(progress.fractionCompleted)")
    }
    .responseDecodable(of: DecodableType.self) { response in
        debugPrint(response)
    }
 类似资料: