Swift3.0 Alamofire初级应用

2022-05-06 16:54:44 浏览数 (1)

序:做过iOS开发的人肯定都知道AFN,现在Swift逐渐流行AFN团队又用Swift写了Alamofire。从头开始学习一下,看看新的Alamofire有什么性能的优化和不同。

大家都知道Alamofire是一个HTTP的网络封装库,首先我们肯定要先知道用Alamofire我们可以干什么。

功能特点

1、请求连接,处理接受不同类型的返回

2、 URL / JSON / plist 参数编码

3、上传 File / Data / Stream / MultipartFormData

4、用请求或者恢复数据下载文件

5、身份认证和url凭证

6、HTTP 返回验证

7、上传或者下载进程显示

8、cURL命令输出

9、动态适应和重试请求

10、TLS证书和公钥锁

11、网络是否可用判断

12、完整的单元检测

组件库

  • AlamofireImage
  • AlamofireNetworkActivityIndicator

环境要求

  • iOS 8.0 / macOS 10.10 / tvOS 9.0 / watchOS 2.0
  • Xcode 8.1
  • Swift 3.0

安装

CocoaPods

podfile文件:

代码语言:javascript复制
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
use_frameworks!

target '<Your Target Name>' do
    pod 'Alamofire', '~> 4.4'
end

然后到终端进入项目目录:

代码语言:javascript复制
$ pod install

其他安装方法见gitHub

应用

首先导入Alamofire模块,发一个简单的GET请求

代码语言:javascript复制
import Alamofire

Alamofire.request("https://httpbin.org/get")

返回的回调:

代码语言:javascript复制
Alamofire.request("https://httpbin.org/get").responseJSON { response in
    print("Request: (String(describing: response.request))")   // original url request
    print("Response: (String(describing: response.response))") // http url response
    print("Result: (response.result)")                         // response serialization result

    if let json = response.result.value {
        print("JSON: (json)") // serialized json response
    }

    if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
        print("Data: (utf8Text)") // original server data as UTF8 string
    }
}

Alamofire 包括默认回调总共有五种返回类型回调包括:

代码语言:javascript复制
// Response Handler - Unserialized Response
func response(
    queue: DispatchQueue?,
    completionHandler: @escaping (DefaultDataResponse) -> Void)
    -> Self

// Response Data Handler - Serialized into Data
func responseData(
    queue: DispatchQueue?,
    completionHandler: @escaping (DataResponse<Data>) -> Void)
    -> Self

// Response String Handler - Serialized into String
func responseString(
    queue: DispatchQueue?,
    encoding: String.Encoding?,
    completionHandler: @escaping (DataResponse<String>) -> Void)
    -> Self

// Response JSON Handler - Serialized into Any
func responseJSON(
    queue: DispatchQueue?,
    completionHandler: @escaping (DataResponse<Any>) -> Void)
    -> Self

// Response PropertyList (plist) Handler - Serialized into Any
func responsePropertyList(
    queue: DispatchQueue?,
    completionHandler: @escaping (DataResponse<Any>) -> Void))
    -> Self

响应验证

在默认情况下,Alamofire对待任何完整的请求不管什么响应内容都是成功的,加上验证之后,在得到响应回调之前,先要经过验证,类型或者状态不匹配的则会报错。

手动验证
代码语言:javascript复制
Alamofire.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 .failure(let error):
            print(error)
        }
    }
自动验证

自动验证状态码在200-300之间的区间,且内容类型是响应能接受的匹配类型。

代码语言:javascript复制
Alamofire.request("https://httpbin.org/get").validate().responseJSON { response in
    switch response.result {
    case .success:
        print("Validation Successful")
    case .failure(let error):
        print(error)
    }
}

响应缓存

响应缓存依赖于系统框架URLCache.

HTTP 请求方式

HTTP请求方式的枚举列表在下面的文件定义  RFC 7231 §4.3 :

代码语言:javascript复制
public enum HTTPMethod: String {
    case options = "OPTIONS"
    case get     = "GET"
    case head    = "HEAD"
    case post    = "POST"
    case put     = "PUT"
    case patch   = "PATCH"
    case delete  = "DELETE"
    case trace   = "TRACE"
    case connect = "CONNECT"
}

上面的那些方式的值可以当做Alamofire.request的method的参数设置

代码语言:javascript复制
Alamofire.request("https://httpbin.org/get") // method defaults to `.get`

Alamofire.request("https://httpbin.org/post", method: .post)
Alamofire.request("https://httpbin.org/put", method: .put)
Alamofire.request("https://httpbin.org/delete", method: .delete)

默认是Get方式

参数编码

Alamofire支持三种类型的参数编码包括:URL、JSON和PropertyList。它还可以支持任何自定义编码符合ParameterEncoding协议。

URL Encoding
GET请求的URL编码
代码语言:javascript复制
let parameters: Parameters = ["foo": "bar"]

// All three of these calls are equivalent
Alamofire.request("https://httpbin.org/get", parameters: parameters) // encoding defaults to `URLEncoding.default`
Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding.default)
Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding(destination: .methodDependent))

// https://httpbin.org/get?foo=bar
POST请求的URL编码
代码语言:javascript复制
let parameters: Parameters = [
    "foo": "bar",
    "baz": ["a", 1],
    "qux": [
        "x": 1,
        "y": 2,
        "z": 3
    ]
]

// All three of these calls are equivalent
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters)
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: URLEncoding.default)
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: URLEncoding.httpBody)

// HTTP body: foo=bar&baz[]=a&baz[]=1&qux[x]=1&qux[y]=2&qux[z]=3
JSON Encoding
代码语言:javascript复制
let parameters: Parameters = [
    "foo": [1,2,3],
    "bar": [
        "baz": "qux"
    ]
]

// Both calls are equivalent
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: JSONEncoding.default)
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: JSONEncoding(options: []))

// HTTP body: {"foo": [1, 2, 3], "bar": {"baz": "qux"}}
Property List Encoding

PropertyListEncoding使用PropertyListSerialization创建一个参数对象的plist表示,根据相关的格式和写作选项值,设置为请求的主体。content - type HTTP请求头字段的编码设置为 application/x-plist .。

Custom Encoding
代码语言:javascript复制
struct JSONStringArrayEncoding: ParameterEncoding {
	private let array: [String]

    init(array: [String]) {
        self.array = array
    }

    func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
        var urlRequest = try urlRequest.asURLRequest()

        let data = try JSONSerialization.data(withJSONObject: array, options: [])

        if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
            urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
        }

        urlRequest.httpBody = data

        return urlRequest
    }
}
给URLRequest 的参数手动编码
代码语言:javascript复制
let url = URL(string: "https://httpbin.org/get")!
var urlRequest = URLRequest(url: url)

let parameters: Parameters = ["foo": "bar"]
let encodedURLRequest = try URLEncoding.queryString.encode(urlRequest, with: parameters)

HTTP Headers(请求头)

给一个请求添加一个自定义的请求头,这个请求头必须是全局的,在你请求的时候更容易去获取和改变请求头。

代码语言:javascript复制
let headers: HTTPHeaders = [
    "Authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
    "Accept": "application/json"
]

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

身份认证

Authentication依赖于 URLCredential 和URLAuthenticationChallenge.这两个系统框架。

Supported Authentication Schemes

  • HTTP Basic
  • HTTP Digest
  • Kerberos
  • NTLM
HTTP Basic Authentication
代码语言:javascript复制
let user = "user"
let password = "password"

Alamofire.request("https://httpbin.org/basic-auth/(user)/(password)")
    .authenticate(user: user, password: password)
    .responseJSON { response in
        debugPrint(response)
    }

根据服务器的实现方法,也可能需要一个授权头

代码语言:javascript复制
let user = "user"
let password = "password"

var headers: HTTPHeaders = [:]

if let authorizationHeader = Request.authorizationHeader(user: user, password: password) {
    headers[authorizationHeader.key] = authorizationHeader.value
}

Alamofire.request("https://httpbin.org/basic-auth/user/password", headers: headers)
    .responseJSON { response in
        debugPrint(response)
    }
证书认证
代码语言:javascript复制
let user = "user"
let password = "password"

let credential = URLCredential(user: user, password: password, persistence: .forSession)

Alamofire.request("https://httpbin.org/basic-auth/(user)/(password)")
    .authenticate(usingCredential: credential)
    .responseJSON { response in
        debugPrint(response)
    }

下载文件

代码语言:javascript复制
Alamofire.download("https://httpbin.org/image/png").responseData { response in
    if let data = response.result.value {
        let image = UIImage(data: data)
    }
}

下载文件目的地

代码语言:javascript复制
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
    let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    let fileURL = documentsURL.appendingPathComponent("pig.png")

    return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}

Alamofire.download(urlString, to: destination).response { response in
    print(response)

    if response.error == nil, let imagePath = response.destinationURL?.path {
        let image = UIImage(contentsOfFile: imagePath)
    }
}

你也可以使用建议目的地的API.

代码语言:javascript复制
let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
Alamofire.download("https://httpbin.org/image/png", to: destination)
下载进度
代码语言:javascript复制
Alamofire.download("https://httpbin.org/image/png")
    .downloadProgress { progress in
        print("Download Progress: (progress.fractionCompleted)")
    }
    .responseData { response in
        if let data = response.result.value {
            let image = UIImage(data: data)
        }
    }

下载进度的API还可以设置线程参数,线程参数可以决定下载进度在哪个线程调起。

代码语言:javascript复制
let utilityQueue = DispatchQueue.global(qos: .utility)

Alamofire.download("https://httpbin.org/image/png")
    .downloadProgress(queue: utilityQueue) { progress in
        print("Download Progress: (progress.fractionCompleted)")
    }
    .responseData { response in
        if let data = response.result.value {
            let image = UIImage(data: data)
        }
    }
恢复下载
代码语言:javascript复制
class ImageRequestor {
    private var resumeData: Data?
    private var image: UIImage?

    func fetchImage(completion: (UIImage?) -> Void) {
        guard image == nil else { completion(image) ; return }

        let destination: DownloadRequest.DownloadFileDestination = { _, _ in
            let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
            let fileURL = documentsURL.appendingPathComponent("pig.png")

            return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
        }

        let request: DownloadRequest

        if let resumeData = resumeData {
            request = Alamofire.download(resumingWith: resumeData)
        } else {
            request = Alamofire.download("https://httpbin.org/image/png")
        }

        request.responseData { response in
            switch response.result {
            case .success(let data):
                self.image = UIImage(data: data)
            case .failure:
                self.resumeData = response.resumeData
            }
        }
    }
}

上传数据到服务器

上传Data类型:
代码语言:javascript复制
let imageData = UIPNGRepresentation(image)!

Alamofire.upload(imageData, to: "https://httpbin.org/post").responseJSON { response in
    debugPrint(response)
}

上传一个文件:

代码语言:javascript复制
let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")

Alamofire.upload(fileURL, to: "https://httpbin.org/post").responseJSON { response in
    debugPrint(response)
}

上传 Multipart Form Data

代码语言:javascript复制
Alamofire.upload(
    multipartFormData: { multipartFormData in
        multipartFormData.append(unicornImageURL, withName: "unicorn")
        multipartFormData.append(rainbowImageURL, withName: "rainbow")
    },
    to: "https://httpbin.org/post",
    encodingCompletion: { encodingResult in
    	switch encodingResult {
    	case .success(let upload, _, _):
            upload.responseJSON { response in
                debugPrint(response)
            }
    	case .failure(let encodingError):
    	    print(encodingError)
    	}
    }
)
上传进度
代码语言:javascript复制
let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")

Alamofire.upload(fileURL, to: "https://httpbin.org/post")
    .uploadProgress { progress in // main queue by default
        print("Upload Progress: (progress.fractionCompleted)")
    }
    .downloadProgress { progress in // main queue by default
        print("Download Progress: (progress.fractionCompleted)")
    }
    .responseJSON { response in
        debugPrint(response)
    }

0 人点赞