往期导航:
简介
Alamofire中对请求相关类型都通过扩展的方式添加了许多方法,这些方法有Alamofire内部业务相关,也有一些辅助工具功能的作用,看清楚后有助于更好的理解Alamofire的架构设计。
相关文件
AlamofireExtended.swift //Alamofire扩展包裹器,用来对系统类进行扩展时包裹使用,避免方法入侵
Notifications.swift //通知相关工具类定义与扩展,定义了Alamofire中用到的各种通知以及相关通知方法,还定义了一个事件监视器来负责发送这些通知
Validation.swift //扩展了Request及其子类,用来检测响应的有效性
Combine.swift //Swift中Combine框架的相关扩展
OperationQueue+Alamofire.swift //OP队列扩展,快速初始化使用
StringEncoding+Alamofire.swift //文本编码类型转换扩展
Result+Alamofire.swift //快速处理Result内部类型以及相关判定
URLSessionConfiguration+Alamofire.swift //快速创建默认配置
DispatchQueue+Alamofire.swift //GCD队列扩展,快速延迟执行闭包使用
复制代码
AlamofireExtended
扩展包裹器,基于Swift的泛型扩展约束实现的为需要扩展的类自由添加方法,同时不对原类型进行过度的方法污染。
当我们需要为某个已存在的类添加一个新的方法时,swift下有两种方法可选:
- 直接添加全局方法
- 扩展该类添加方法
这两种方法都能实现为该类添加新的方法,但是1是全局有效,需要通过方法调用的形式来使用,2的话,会有个问题:扩展的方法,会直接作用在该类型上,整个工程中都可以直接访问到该方法,比如我们为UIButton添加了如下三个方法:
extension UIButton {
func atap1() {}
func atap2() {}
func atap3() {}
}
复制代码
那么在所有能访问到该扩展的地方,就可以调用这些方法:
问题出现了:这三个方法直接出现在button可调用的方法提示中,加入我添加了100个扩展方法,而这些方法都是强业务相关的,那么这种扩展方式就会导致在所有的button在要调用方法的时候,都能看到这些方法提示,一方面会给调用者造成困惑,另一方面会存在同名的风险。这就是方法污染。
为了解决上面问题,我们就可以使用这样的思路来解决:
- 不直接对UIButton进行扩展方法,而是把button用一个struct包裹起来
- 接着对这个struct添加方法
- 调用方法时,我们需要对button扩展一个方法,来获取到这个包裹着button的struct,然后使用这个struct来调用这些方法:
struct ButtonWrapper {
let btn: UIButton
func atap1() {}
func atap2() {}
func atap3() {}
}
extension UIButton {
var btnWrapper: ButtonWrapper {
return ButtonWrapper.init(btn: self)
}
}
func test() {
let btn: UIButton!
btn.btnWrapper.atap1()
}
复制代码
可以看到,这样的话,我们想要为button添加的方法就不会出现在button的可调用列表内了,button的方法列表中值增加了一个获取Wrapper的方法,在这之后,我们不管添加多少个方法,都不会对button造成污染,很好的实现了方法隔离。
以上的实现,有很大的局限性,只能对UIButton进行方法添加,由于Swift泛型与扩展约束的强大,我们可以把ButtonWrapper写成一个泛型的包裹器,可以包裹一个任意类型的泛型对象,之后通过扩展该包裹器同时对泛型类型进行约束来自由为需要的类型进行扩展添加方法。
public struct AlamofireExtension<ExtendedType> {
// 包裹的需要扩展的泛型类型对象
public private(set) var type: ExtendedType
public init(_ type: ExtendedType) {
self.type = type
}
}
复制代码
这时候对任意类型添加扩展方法时,就是通过扩展AlamofireExtension,并使用where约束来实现
extension AlamofireExtension where ExtendedType == UIButton {
func atap1() {}
func atap2() {}
func atap3() {}
}
extension UIButton {
var af: AlamofireExtension<UIButton> {
return .init(self)
}
}
extension AlamofireExtension where ExtendedType == UILabel {
var aText1: String { return "" }
}
extension UILabel {
var af: AlamofireExtension<UILabel> {
return .init(self)
}
}
func test() {
var btn: UIButton!
btn.af.atap1()
var lbl: UILabel!
lbl.af.aText1
}
复制代码
但是这时候还有个问题,每次需要对新类型添加方法时,都要对该类型进行扩展,添加af计算属性来返回AlamofireExtension包裹类型,这个行为在不同类型中高度类似,就可以使用泛型协议+扩展的方式,添加默认实现:
// 使用协议+协议扩展来实现自由为需要扩展的对象添加包裹的能力
public protocol AlamofireExtended {
// 需要扩展的对象的类型
associatedtype ExtendedType
// 静态(类)包裹器
static var af: AlamofireExtension<ExtendedType>.Type { get set }
// 实例变量包裹器
var af: AlamofireExtension<ExtendedType> { get set }
}
// 扩展协议添加默认实现
extension AlamofireExtended {
// 静态(类)包裹器, 包裹的是Type
public static var af: AlamofireExtension<Self>.Type {
get { AlamofireExtension<Self>.self }
set {}
}
// 实例包裹器, 包裹的是对象本身
public var af: AlamofireExtension<Self> {
get { AlamofireExtension(self) }
set {}
}
}
复制代码
这样的话,我们需要对某个类型添加方法是,只要对该类型添加扩展,实现AlamofireExtended协议,即可实现创建包裹器的方法,然后扩展包裹器即可自由为该类型添加各种方法。
extension AlamofireExtension where ExtendedType == UIButton {
func atap1() {}
func atap2() {}
func atap3() {}
}
extension AlamofireExtension where ExtendedType == UILabel {
var aText1: String { return "" }
}
extension UIButton: AlamofireExtended {}
extension UILabel: AlamofireExtended {}
func test() {
let btn: UIButton!
btn.af.atap1()
let lbl: UILabel!
lbl.af.aText1
}
复制代码
Alamofire内部中,实现了AlamofireExtended扩展的主要有两个地方:
- URLSessionConfiguration
- ServerTrustEvaluation中对证书的处理中有很多类型实现了该协议
最后 吹爆Swift!!这个设计相当巧妙,通过调用时先使用***.af的调用,也可以一眼就看出来后面即将调用的方法,是Alamofire相关的扩展方法。
Notifications
Alamofire中使用了许多同通知,主要是请求状态变更时发出来的,因此Alamofire中定义了这些通知的名字,以及通过扩展Notification与NotificationCenter来实现带着Request对象来收发通知使用,同时实现了一个事件监听器,用来监听请求过程中的各个状态,发送对应的通知。
// 扩展Request添加静态常量来定义通知名字
// 使用: Request.didResumeNotification
extension Request {
/// Posted when a `Request` is resumed. The `Notification` contains the resumed `Request`.
public static let didResumeNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didResume")
/// Posted when a `Request` is suspended. The `Notification` contains the suspended `Request`.
public static let didSuspendNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didSuspend")
/// Posted when a `Request` is cancelled. The `Notification` contains the cancelled `Request`.
public static let didCancelNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCancel")
/// Posted when a `Request` is finished. The `Notification` contains the completed `Request`.
public static let didFinishNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didFinish")
/// Posted when a `URLSessionTask` is resumed. The `Notification` contains the `Request` associated with the `URLSessionTask`.
public static let didResumeTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didResumeTask")
/// Posted when a `URLSessionTask` is suspended. The `Notification` contains the `Request` associated with the `URLSessionTask`.
public static let didSuspendTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didSuspendTask")
/// Posted when a `URLSessionTask` is cancelled. The `Notification` contains the `Request` associated with the `URLSessionTask`.
public static let didCancelTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCancelTask")
/// Posted when a `URLSessionTask` is completed. The `Notification` contains the `Request` associated with the `URLSessionTask`.
public static let didCompleteTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCompleteTask")
}
// MARK: -
// 扩展通知对象,快速从userInfo中读写Request对象
// 但是居然是直接扩展, 而不是使用AlamofireExtended来添加
extension Notification {
// 从userInfo获取Request对象
public var request: Request? {
userInfo?[String.requestKey] as? Request
}
// 创建通知对象, 并把Request塞入到userInfo中发出去
init(name: Notification.Name, request: Request) {
self.init(name: name, object: nil, userInfo: [String.requestKey: request])
}
//模拟使用
func test() {
var req: Request!
let noti = Notification.init(name: Request.didResumeNotification, request: req)
let getReq = noti.request
}
}
//*****************以下是个人添加的内容*****************//
// 个人觉得, 这种带有业务相关的扩展方法, 最好使用包裹器进行方法隔离
extension Notification: AlamofireExtended {}
extension AlamofireExtension where ExtendedType == Notification {
public var request: Request? {
type.userInfo?[String.requestKey] as? Request
}
// 创建通知对象, 并把Request塞入到userInfo中发出去
static func createNotificationWith(name: Notification.Name, request: Request) -> Notification {
Notification.init(name: name, object: nil, userInfo: [String.requestKey: request])
}
//模拟使用
func test() {
var req: Request!
let noti = Notification.af.createNotificationWith(name: Request.didResumeNotification, request: req)
let getReq = noti.af.request
}
}
//*****************以上是个人添加的内容*****************//
extension NotificationCenter {
// 快速发送通知
func postNotification(named name: Notification.Name, with request: Request) {
let notification = Notification(name: name, request: request)
post(notification)
}
}
extension String {
// 通知的userInfo中保存Request的key
fileprivate static let requestKey = "org.alamofire.notification.key.request"
}
// 事件监听器, 用来在各个阶段发送对应通知使用
public final class AlamofireNotifications: EventMonitor {
public func requestDidResume(_ request: Request) {
NotificationCenter.default.postNotification(named: Request.didResumeNotification, with: request)
}
public func requestDidSuspend(_ request: Request) {
NotificationCenter.default.postNotification(named: Request.didSuspendNotification, with: request)
}
public func requestDidCancel(_ request: Request) {
NotificationCenter.default.postNotification(named: Request.didCancelNotification, with: request)
}
public func requestDidFinish(_ request: Request) {
NotificationCenter.default.postNotification(named: Request.didFinishNotification, with: request)
}
public func request(_ request: Request, didResumeTask task: URLSessionTask) {
NotificationCenter.default.postNotification(named: Request.didResumeTaskNotification, with: request)
}
public func request(_ request: Request, didSuspendTask task: URLSessionTask) {
NotificationCenter.default.postNotification(named: Request.didSuspendTaskNotification, with: request)
}
public func request(_ request: Request, didCancelTask task: URLSessionTask) {
NotificationCenter.default.postNotification(named: Request.didCancelTaskNotification, with: request)
}
public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) {
NotificationCenter.default.postNotification(named: Request.didCompleteTaskNotification, with: request)
}
}
复制代码
Validation
收到请求响应后,我们可能会需要先对响应进行校验,来判断本次响应是否有效,Alamofire在Request定义了validate方法可以为Request添加用来校验有效性的闭包,在收到响应后,就会执行这些闭包来校验本次有效性是否有效
class Request {
// 保存使用validation来进行有效性校验的闭包
@Protected
fileprivate var validators: [() -> Void] = []
//添加validation,方法会创建一个无出入参数的闭包,用来执行validation校验,然后把这个闭包暂存起来。
@discardableResult
public func validate(_ validation: @escaping Validation) -> Self
}
复制代码
而上面用到的Validation类型,就是在Validation.swift中定义的一个别名,是一个闭包,不同的Request子类有各自的Validation定义。然后在Validation.swift中,对Request进行了各种扩展,添加了默认的检测有效性的方法:检测响应码与MIME类型等方式来进行判断。
整个Validation.swift中分为两大部分内容:
- Request基类扩展,定义辅助类型,私有的响应码、MIME类型校验方法供第二部分的三个子类进行调用
- Request的三个子类扩展,实现校验响应码与MIME方法
1.Request基类扩展
大部分内容都是fileprivate级的,public级别的只有定义了一个Validation别名,供外部使用。fileprivate级别的内容主要是定义了默认的MIME类型,与默认允许的状态码,以及几个供三个子类调用的校验方法。
extension Request {
//MARK: - 辅助类型定义
// 只有这个别名是public的, 其余的都是fileprivate的
// 校验结果别名, 用来标识校验结果正确与否
public typealias ValidationResult = Result<Void, Error>
// 错误原因别名
fileprivate typealias ErrorReason = AFError.ResponseValidationFailureReason
// MIME类型结构体 "image/jpeg"
fileprivate struct MIMEType {
let type: String
let subtype: String
// 是否是通配类型的MIME
var isWildcard: Bool { type == "*" && subtype == "*" }
// 从string格式初始化, 失败返回nil
init?(_ string: String) {
let components: [String] = {
let stripped = string.trimmingCharacters(in: .whitespacesAndNewlines)
let split = stripped[..<(stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)]
return split.components(separatedBy: "/")
}()
if let type = components.first, let subtype = components.last {
self.type = type
self.subtype = subtype
} else {
return nil
}
}
// 校验MIME
func matches(_ mime: MIMEType) -> Bool {
switch (type, subtype) {
case (mime.type, mime.subtype), (mime.type, "*"), ("*", mime.subtype), ("*", "*"):
return true
default:
return false
}
}
}
// 默认允许的响应状态码 2xx - 3xx
fileprivate var acceptableStatusCodes: Range<Int> { 200..<300 }
// 默认允许的ContentType, 会从请求头中的Accept字段取值
fileprivate var acceptableContentTypes: [String] {
if let accept = request?.value(forHTTPHeaderField: "Accept") {
return accept.components(separatedBy: ",")
}
return ["*/*"]
}
// 校验响应的状态码是否与在传入的状态码集合中
fileprivate func validate<S: Sequence>(statusCode acceptableStatusCodes: S,
response: HTTPURLResponse)
-> ValidationResult
where S.Iterator.Element == Int {
// 如果支持的状态码中包含响应的状态码, 返回成功, 否则返回对应错误
if acceptableStatusCodes.contains(response.statusCode) {
return .success(())
} else {
let reason: ErrorReason = .unacceptableStatusCode(code: response.statusCode)
return .failure(AFError.responseValidationFailed(reason: reason))
}
}
// 校验响应的contentType是否在传入的contentType集合中
// 入参有个Data, 如果data为nil, 那就直接认为contentType类型符合
fileprivate func validate<S: Sequence>(contentType acceptableContentTypes: S,
response: HTTPURLResponse,
data: Data?)
-> ValidationResult
where S.Iterator.Element == String {
guard let data = data, !data.isEmpty else { return .success(()) }
return validate(contentType: acceptableContentTypes, response: response)
}
// 校验响应的contentType是否在传入的contentType集合中
fileprivate func validate<S: Sequence>(contentType acceptableContentTypes: S,
response: HTTPURLResponse)
-> ValidationResult
where S.Iterator.Element == String {
// 先获取响应的MIME类型
guard
let responseContentType = response.mimeType,
let responseMIMEType = MIMEType(responseContentType)
else {
// 获取响应的MIME类型失败, 检测下允许的ContentType是不是通配类型
for contentType in acceptableContentTypes {
if let mimeType = MIMEType(contentType), mimeType.isWildcard {
return .success(())
}
}
// 未能获取到响应的MIME, 且请求的ContentType不是通配的
let error: AFError = {
let reason: ErrorReason = .missingContentType(acceptableContentTypes: Array(acceptableContentTypes))
return AFError.responseValidationFailed(reason: reason)
}()
return .failure(error)
}
// 获取到MIME, 开始校验
for contentType in acceptableContentTypes {
if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) {
// 校验成功
return .success(())
}
}
// 校验失败, 返回错误
let error: AFError = {
let reason: ErrorReason = .unacceptableContentType(acceptableContentTypes: Array(acceptableContentTypes),
responseContentType: responseContentType)
return AFError.responseValidationFailed(reason: reason)
}()
return .failure(error)
}
}
复制代码
2.三个子类扩展
三个子类扩展行为一致,都是先定义了Validation闭包别名的类型, 然后分别添加了三个校验方法:
- 校验指定状态码集合
- 校验指定contentType集合
- 校验默认的状态码与contentType集合
这三个校验方法返回的都是Self类型,因此可以串起来进行多次校验
// MARK: - DataRequest子类扩展
extension DataRequest {
// 执行校验的闭包
public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> ValidationResult
// 指定状态码校验
@discardableResult
public func validate<S: Sequence>(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int {
// 调用定义中的校验方法, 添加闭包
validate { [unowned self] _, response, _ in
self.validate(statusCode: acceptableStatusCodes, response: response)
}
}
// 指定contentType校验
// 注意contentType是自动闭包
// 把执行获取contentType的时机推迟到了要执行校验的时候调用
// 这样如果在请求完成前, 取消了请求, 就可以不用获取比较的contentType, 节省资源
@discardableResult
public func validate<S: Sequence>(contentType acceptableContentTypes: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String {
validate { [unowned self] _, response, data in
self.validate(contentType: acceptableContentTypes(), response: response, data: data)
}
}
// 使用默认的状态码与contentType
@discardableResult
public func validate() -> Self {
// 这里多余了, 本来contentType参数就已经是自动闭包了, 可以直接丢进去用, 不需要再包一层闭包
// validate(statusCode: acceptableStatusCodes).validate(contentType: self.acceptableContentTypes)
let contentTypes: () -> [String] = { [unowned self] in
self.acceptableContentTypes
}
// 链式调用, 先校验响应码, 再校验contentType
return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes())
}
}
//MARK: - DataStreamRequest
extension DataStreamRequest {
/// A closure used to validate a request that takes a `URLRequest` and `HTTPURLResponse` and returns whether the
/// request was valid.
public typealias Validation = (_ request: URLRequest?, _ response: HTTPURLResponse) -> ValidationResult
/// Validates that the response has a status code in the specified sequence.
///
/// If validation fails, subsequent calls to response handlers will have an associated error.
///
/// - Parameter statusCode: `Sequence` of acceptable response status codes.
///
/// - Returns: The instance.
@discardableResult
public func validate<S: Sequence>(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int {
validate { [unowned self] _, response in
self.validate(statusCode: acceptableStatusCodes, response: response)
}
}
/// Validates that the response has a content type in the specified sequence.
///
/// If validation fails, subsequent calls to response handlers will have an associated error.
///
/// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes.
///
/// - returns: The request.
@discardableResult
public func validate<S: Sequence>(contentType acceptableContentTypes: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String {
validate { [unowned self] _, response in
self.validate(contentType: acceptableContentTypes(), response: response)
}
}
/// Validates that the response has a status code in the default acceptable range of 200...299, and that the content
/// type matches any specified in the Accept HTTP header field.
///
/// If validation fails, subsequent calls to response handlers will have an associated error.
///
/// - Returns: The instance.
@discardableResult
public func validate() -> Self {
let contentTypes: () -> [String] = { [unowned self] in
self.acceptableContentTypes
}
return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes())
}
}
// MARK: - DownloadRequest
extension DownloadRequest {
/// A closure used to validate a request that takes a URL request, a URL response, a temporary URL and a
/// destination URL, and returns whether the request was valid.
public typealias Validation = (_ request: URLRequest?,
_ response: HTTPURLResponse,
_ fileURL: URL?)
-> ValidationResult
/// Validates that the response has a status code in the specified sequence.
///
/// If validation fails, subsequent calls to response handlers will have an associated error.
///
/// - Parameter statusCode: `Sequence` of acceptable response status codes.
///
/// - Returns: The instance.
@discardableResult
public func validate<S: Sequence>(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int {
validate { [unowned self] _, response, _ in
self.validate(statusCode: acceptableStatusCodes, response: response)
}
}
/// Validates that the response has a content type in the specified sequence.
///
/// If validation fails, subsequent calls to response handlers will have an associated error.
///
/// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes.
///
/// - returns: The request.
@discardableResult
public func validate<S: Sequence>(contentType acceptableContentTypes: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String {
validate { [unowned self] _, response, fileURL in
guard let validFileURL = fileURL else {
return .failure(AFError.responseValidationFailed(reason: .dataFileNil))
}
do {
let data = try Data(contentsOf: validFileURL)
return self.validate(contentType: acceptableContentTypes(), response: response, data: data)
} catch {
return .failure(AFError.responseValidationFailed(reason: .dataFileReadFailed(at: validFileURL)))
}
}
}
/// Validates that the response has a status code in the default acceptable range of 200...299, and that the content
/// type matches any specified in the Accept HTTP header field.
///
/// If validation fails, subsequent calls to response handlers will have an associated error.
///
/// - returns: The request.
@discardableResult
public func validate() -> Self {
let contentTypes = { [unowned self] in
self.acceptableContentTypes
}
return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes())
}
}
复制代码
Result+Alamofire
请求的结果,Alamofire使用了Result来进行封装,成功对象为泛型Success,错误对象为AFError,因此Alamofire对Result进行了扩展,添加了一些辅助函数方便使用
// 请求结果类型别名, 请求结果使用Result来包装, 错误类型为AFError
public typealias AFResult<Success> = Result<Success, AFError>
// 这里的扩展时internal级别的, module之外无法访问
extension Result {
// 快速判断成功
var isSuccess: Bool {
guard case .success = self else { return false }
return true
}
// 快速判断失败
var isFailure: Bool {
!isSuccess
}
// 快速获取成功时的值(失败的时候返回nil)
var success: Success? {
guard case let .success(value) = self else { return nil }
return value
}
// 快速获取失败时的错误(成功时返回nil)
var failure: Failure? {
guard case let .failure(error) = self else { return nil }
return error
}
init(value: Success, error: Failure?) {
if let error = error {
self = .failure(error)
} else {
self = .success(value)
}
}
// 把成功时的值类型变换成新类型(失败时不处理)
// 类似Result自带的map函数, 区别是transform参数, 可以抛出错误
func tryMap<NewSuccess>(_ transform: (Success) throws -> NewSuccess) -> Result<NewSuccess, Error> {
switch self {
case let .success(value):
// 成功时执行变换,do-catch捕捉异常并返回错误
do {
return try .success(transform(value))
} catch {
return .failure(error)
}
case let .failure(error):
// 失败时不做处理
return .failure(error)
}
}
// 把失败时的错误类型变换成新类型(成功时不处理)
// 类似Result自带的mapError, 区别同样是transform可以抛出异常
func tryMapError<NewFailure: Error>(_ transform: (Failure) throws -> NewFailure) -> Result<Success, Error> {
switch self {
case let .failure(error):
//错误时执行变换,do-catch捕捉异常返回错误
do {
return try .failure(transform(error))
} catch {
return .failure(error)
}
case let .success(value):
// 成功时不做处理
return .success(value)
}
}
}
复制代码
OperationQueue+Alamofire
只是简单的扩展了OperationQueue,添加了快速初始化的方法
extension OperationQueue {
convenience init(qualityOfService: QualityOfService = .default,//队列优先级
maxConcurrentOperationCount: Int = OperationQueue.defaultMaxConcurrentOperationCount,// 最大并发op数
underlyingQueue: DispatchQueue? = nil,// 设置GCD队列
name: String? = nil,// 标识符
startSuspended: Bool = false) {//是否初始化之后立刻挂起
self.init()
self.qualityOfService = qualityOfService
self.maxConcurrentOperationCount = maxConcurrentOperationCount
self.underlyingQueue = underlyingQueue
self.name = name
isSuspended = startSuspended
}
}
复制代码
StringEncoding+Alamofire
扩展String.Encoding,添加从字符串创建编码类型的方法
extension String.Encoding {
/// Creates an encoding from the IANA charset name.
///
/// - Notes: These mappings match those [provided by CoreFoundation](https://opensource.apple.com/source/CF/CF-476.18/CFStringUtilities.c.auto.html)
///
/// - Parameter name: IANA charset name.
init?(ianaCharsetName name: String) {
switch name.lowercased() {
case "utf-8":
self = .utf8
case "iso-8859-1":
self = .isoLatin1
case "unicode-1-1", "iso-10646-ucs-2", "utf-16":
self = .utf16
case "utf-16be":
self = .utf16BigEndian
case "utf-16le":
self = .utf16LittleEndian
case "utf-32":
self = .utf32
case "utf-32be":
self = .utf32BigEndian
case "utf-32le":
self = .utf32LittleEndian
default:
return nil
}
}
}
复制代码
DispatchQueue+Alamofire
添加了快速异步延迟执行的方法,系统的after参数传入的时间参数是DispatchTime类型,这里添加的方法,时间参数是秒(s)
extension DispatchQueue {
/// Execute the provided closure after a `TimeInterval`.
///
/// - Parameters:
/// - delay: `TimeInterval` to delay execution.
/// - closure: Closure to execute.
func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) {
asyncAfter(deadline: .now() + delay, execute: closure)
}
}
复制代码
URLSessionConfiguration+Alamofire
使用AlamofireExtended添加了默认的cfg
extension URLSessionConfiguration: AlamofireExtended {}
extension AlamofireExtension where ExtendedType: URLSessionConfiguration {
// Alamofire默认的cfg, 跟URLSessionConfiguration.default一样
// 但是添加了默认请求头: `Accept-Language`, `Accept-Encoding`, `User-Agent`
// 详情可以点进下面这个.default中去看
public static var `default`: URLSessionConfiguration {
let configuration = URLSessionConfiguration.default
configuration.headers = .default
return configuration
}
}
复制代码
Combine
使用iOS 13出的Combine框架,对Request及其子类进行了扩展,使其支持Combine中的一些时间发布与绑定。因为还没去细看Combine框架,所以这部分内容等后续学习了Combine框架之后,再来学习。
纯属个人理解, 可能存在理解错误的地方, 如有错误, 欢迎评论指出~ 感谢~