Alamofire使用结构体来封装method与headers以及header对象,简化处理操做:ios
定义了一个结构体来封装method,使用一个rawValue来保存字符串,并实现了三个必要协议swift
须要注意的是:封装的字符串均为大写,所以比较HTTPMethod.get == HTTPMethod(rawValue: "get")会是false的数组
public struct HTTPMethod: RawRepresentable, Equatable, Hashable {
/// `CONNECT` method.
public static let connect = HTTPMethod(rawValue: "CONNECT")
/// `DELETE` method.
public static let delete = HTTPMethod(rawValue: "DELETE")
/// `GET` method.
public static let get = HTTPMethod(rawValue: "GET")
/// `HEAD` method.
public static let head = HTTPMethod(rawValue: "HEAD")
/// `OPTIONS` method.
public static let options = HTTPMethod(rawValue: "OPTIONS")
/// `PATCH` method.
public static let patch = HTTPMethod(rawValue: "PATCH")
/// `POST` method.
public static let post = HTTPMethod(rawValue: "POST")
/// `PUT` method.
public static let put = HTTPMethod(rawValue: "PUT")
/// `TRACE` method.
public static let trace = HTTPMethod(rawValue: "TRACE")
public let rawValue: String
public init(rawValue: String) {
self.rawValue = rawValue
}
}
复制代码
吐槽:整这么麻烦,直接enum多简单:markdown
enum HTTPMethod: String {
case get = "GET"
case post = "POST"
//...其余几个
}
//并且使用方法同样:
HTTPMethod.get
HTTPMethod.init(rawValue: "GET")
HTTPMethod.get.rawValue
复制代码
方便管理http请求头app
对应每一个请求头,持有name跟valuepost
public struct HTTPHeader: Hashable {
public let name: String
public let value: String
public init(name: String, value: String) {
self.name = name
self.value = value
}
}
// 而后扩展了一下添加字符串描述方法
extension HTTPHeader: CustomStringConvertible {
public var description: String {
"\(name): \(value)"
}
}
复制代码
而后定义了一大堆static方法来快速建立默认请求头:学习
extension HTTPHeader {
public static func accept(_ value: String) -> HTTPHeader {
HTTPHeader(name: "Accept", value: value)
}
public static func acceptCharset(_ value: String) -> HTTPHeader {
HTTPHeader(name: "Accept-Charset", value: value)
}
/// 有默认值(系统语言)
public static func acceptLanguage(_ value: String) -> HTTPHeader {
HTTPHeader(name: "Accept-Language", value: value)
}
/// 有默认值(见HTTPHeader.defaultAcceptEncoding)
public static func acceptEncoding(_ value: String) -> HTTPHeader {
HTTPHeader(name: "Accept-Encoding", value: value)
}
/// Basic帐号密码认证, 格式为: Basic [用户名:密码]的base64编码
public static func authorization(username: String, password: String) -> HTTPHeader {
let credential = Data("\(username):\(password)".utf8).base64EncodedString()
return authorization("Basic \(credential)")
}
/// Bearer token认证
public static func authorization(bearerToken: String) -> HTTPHeader {
authorization("Bearer \(bearerToken)")
}
/// 其余认证字段
public static func authorization(_ value: String) -> HTTPHeader {
HTTPHeader(name: "Authorization", value: value)
}
public static func contentDisposition(_ value: String) -> HTTPHeader {
HTTPHeader(name: "Content-Disposition", value: value)
}
/// Alamofire的ParameterEncoding与ParameterEncoder会设置该请求头, 因此不必手动设置
public static func contentType(_ value: String) -> HTTPHeader {
HTTPHeader(name: "Content-Type", value: value)
}
/// 有默认值(见HTTPHeader.defaultUserAgent)
public static func userAgent(_ value: String) -> HTTPHeader {
HTTPHeader(name: "User-Agent", value: value)
}
}
复制代码
而后定义了三个默认请求头:ui
extension HTTPHeader {
/// 默认设备支持的AcceptEncoding [Accept-Encoding HTTP header documentation](https://tools.ietf.org/html/rfc7230#section-4.2.3) .
public static let defaultAcceptEncoding: HTTPHeader = {
let encodings: [String]
if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *) {
encodings = ["br", "gzip", "deflate"]
} else {
encodings = ["gzip", "deflate"]
}
return .acceptEncoding(encodings.qualityEncoded())
}()
/// 默认的AcceptLanguage为设备的语言 [Accept-Language HTTP header documentation](https://tools.ietf.org/html/rfc7231#section-5.3.5).
public static let defaultAcceptLanguage: HTTPHeader = {
.acceptLanguage(Locale.preferredLanguages.prefix(6).qualityEncoded())
}()
/// 拼装Alamofire默认的UA
///
/// See the [User-Agent header documentation](https://tools.ietf.org/html/rfc7231#section-5.5.3).
///格式: app名字/app版本号 (bundleid; build:构件号; ios版本号) Alamofire版本号
/// Example: `iOS Example/1.0 (org.alamofire.iOS-Example; build:1; iOS 13.0.0) Alamofire/5.0.0`
public static let defaultUserAgent: HTTPHeader = {
let info = Bundle.main.infoDictionary
let executable = (info?[kCFBundleExecutableKey as String] as? String) ??
(ProcessInfo.processInfo.arguments.first?.split(separator: "/").last.map(String.init)) ??
"Unknown"
let bundle = info?[kCFBundleIdentifierKey as String] as? String ?? "Unknown"
let appVersion = info?["CFBundleShortVersionString"] as? String ?? "Unknown"
let appBuild = info?[kCFBundleVersionKey as String] as? String ?? "Unknown"
let osNameVersion: String = {
let version = ProcessInfo.processInfo.operatingSystemVersion
let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
let osName: String = {
#if os(iOS)
#if targetEnvironment(macCatalyst)
return "macOS(Catalyst)"
#else
return "iOS"
#endif
#elseif os(watchOS)
return "watchOS"
#elseif os(tvOS)
return "tvOS"
#elseif os(macOS)
return "macOS"
#elseif os(Linux)
return "Linux"
#elseif os(Windows)
return "Windows"
#else
return "Unknown"
#endif
}()
return "\(osName) \(versionString)"
}()
let alamofireVersion = "Alamofire/\(version)"
let userAgent = "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(alamofireVersion)"
return .userAgent(userAgent)
}()
}
// 扩展下Collection用来拼接值
extension Collection where Element == String {
func qualityEncoded() -> String {
enumerated().map { index, encoding in
let quality = 1.0 - (Double(index) * 0.1)
return "\(encoding);q=\(quality)"
}.joined(separator: ", ")
}
}
复制代码
定义了一个结构体来管理HTTPHeader请求头对象数组, 并遵照了几个方便操做的协议。编码
public struct HTTPHeaders {
/// 请求头数组
private var headers: [HTTPHeader] = []
/// 空初始化方法
public init() {}
/// 使用请求头数组初始化
public init(_ headers: [HTTPHeader]) {
self.init()
//
headers.forEach { update($0) }
}
/// 使用string格式的字段来初始化, 不区分大小写
public init(_ dictionary: [String: String]) {
self.init()
// 使用遍历,并调update是由于: 在update时会把name所有转为小写, 这样就能够过滤掉字典中大小写不一样致使的重复值
dictionary.forEach { update(HTTPHeader(name: $0.key, value: $0.value)) }
}
/// 添加请求头
public mutating func add(name: String, value: String) {
update(HTTPHeader(name: name, value: value))
}
/// 添加请求头
public mutating func add(_ header: HTTPHeader) {
update(header)
}
/// 更新请求头, 不区分大小写, 若是存在就更新, 不存在就添加
public mutating func update(name: String, value: String) {
update(HTTPHeader(name: name, value: value))
}
/// 更新请求头
public mutating func update(_ header: HTTPHeader) {
// index方法是本身实现的Array扩展, 会先把name转为小写, 再找找有没有存在的
guard let index = headers.index(of: header.name) else {
headers.append(header)
return
}
headers.replaceSubrange(index...index, with: [header])
}
/// 删除请求头, 不区分大小写
public mutating func remove(name: String) {
guard let index = headers.index(of: name) else { return }
headers.remove(at: index)
}
/// 对本身升序排序, 不区分大小写
public mutating func sort() {
headers.sort { $0.name.lowercased() < $1.name.lowercased() }
}
/// 返回一个排序后的数组, 不影响本身的顺序
public func sorted() -> HTTPHeaders {
var headers = self
headers.sort()
return headers
}
/// 根据name返回value, 不存在返回nil, 不区分大小写
public func value(for name: String) -> String? {
guard let index = headers.index(of: name) else { return nil }
return headers[index].value
}
/// 下标返回
public subscript(_ name: String) -> String? {
get { value(for: name) }
set {
if let value = newValue {
update(name: name, value: value)
} else {
remove(name: name)
}
}
}
/// 把所有的请求头转成string字典(准备加入到请求中)
public var dictionary: [String: String] {
let namesAndValues = headers.map { ($0.name, $0.value) }
return Dictionary(namesAndValues, uniquingKeysWith: { _, last in last })
}
}
复制代码
extension HTTPHeaders: ExpressibleByDictionaryLiteral {
/// 快速使用string字典初始化
/// 用法: let headers: HTTPHeaders = ["key": "value", "key2": "value2"]
public init(dictionaryLiteral elements: (String, String)...) {
self.init()
elements.forEach { update(name: $0.0, value: $0.1) }
}
}
extension HTTPHeaders: ExpressibleByArrayLiteral {
/// 快速使用HTTPHeader数组初始化
/// 用法: let headers: HTTPHeaders = [header1, header2]//(header1, header2均为HTTPHeader对象)
public init(arrayLiteral elements: HTTPHeader...) {
self.init(elements)
}
}
extension HTTPHeaders: Sequence {
// 遵循迭代器协议, 为了实现下面的Collection协议
public func makeIterator() -> IndexingIterator<[HTTPHeader]> {
headers.makeIterator()
}
}
extension HTTPHeaders: Collection {
// 实现Collection协议
public var startIndex: Int {
headers.startIndex
}
public var endIndex: Int {
headers.endIndex
}
public subscript(position: Int) -> HTTPHeader {
headers[position]
}
public func index(after i: Int) -> Int {
headers.index(after: i)
}
}
extension HTTPHeaders: CustomStringConvertible {
/// string格式的描述信息
public var description: String {
headers.map { $0.description }
.joined(separator: "\n")
}
}
复制代码
默认请求头有三个默认头:
extension HTTPHeaders {
public static let `default`: HTTPHeaders = [.defaultAcceptEncoding,
.defaultAcceptLanguage,
.defaultUserAgent]
}
复制代码
extension Array where Element == HTTPHeader {
func index(of name: String) -> Int? {
//先把名字转为小写
let lowercasedName = name.lowercased()
//返回index,不存在会返回nil
return firstIndex { $0.name.lowercased() == lowercasedName }
}
}
复制代码
//URLRequest在swift中是结构体,想不到吧(~ ̄▽ ̄)~
extension URLRequest {
/// Returns `allHTTPHeaderFields` as `HTTPHeaders`.
public var headers: HTTPHeaders {
get { allHTTPHeaderFields.map(HTTPHeaders.init) ?? HTTPHeaders() }
set { allHTTPHeaderFields = newValue.dictionary }
}
}
extension HTTPURLResponse {
/// Returns `allHeaderFields` as `HTTPHeaders`.
public var headers: HTTPHeaders {
(allHeaderFields as? [String: String]).map(HTTPHeaders.init) ?? HTTPHeaders()
}
}
extension URLSessionConfiguration {
/// Returns `httpAdditionalHeaders` as `HTTPHeaders`.
public var headers: HTTPHeaders {
get { (httpAdditionalHeaders as? [String: String]).map(HTTPHeaders.init) ?? HTTPHeaders() }
set { httpAdditionalHeaders = newValue.dictionary }
}
}
复制代码
扩展了URLRequest结构体,对method进行使用HTTPMethod对象进行读写,并使用method进行了URLRequest有效性进行了判断(判断会在Session中调用)
extension URLRequest {
public var method: HTTPMethod? {
get { httpMethod.flatMap(HTTPMethod.init) }
set { httpMethod = newValue?.rawValue }
}
public func validate() throws {
if method == .get, let bodyData = httpBody {
// get方法不容许有httpbody, 若是有, 会抛出错误, 并把这个data给一块儿抛出去, 方便调试
throw AFError.urlRequestValidationFailed(reason: .bodyDataInGETRequest(bodyData))
}
}
}
复制代码
以上纯属我的理解,不免有错误,若是发现有错,欢迎评论指出~~将第一时间改正,也欢迎评论讨论,很是感谢~~