源码分析

(test工程+源码注释工程)https://www.dropbox.com/sh/v7poaxekotxlaf1/AADQb7NbrrfHXJLrBt1Q51vVa?dl=0

整体的流程图

创建Session用来发送请求,可以自定义创建,也可以直接使用AF单例,默认的Session来发送请求

外部调用request()方法,传入URLConvertible,创建Request对象

  1. 内部开始先用URLConvertible来创建URLRequestConvertible对象
  2. 用URLRequestConvertible创建Request(这个Request就是返回给外部的对象),保存到Session中,然后开始对Request进行预处理
  3. 先创建初始URLRequest,用预处理器对其进行预处理,在预处理前后都有使用方法来告知Request,流程变更,用来通知事件监听器,所以看EventMonitor协议中,有一大堆生命周期的回调事件。
  4. 预处理完成,返回Request对象

外部调用response系列方法,在这些方法中实现对响应的处理

  1. 内部会先创建对原始响应数据的处理闭包

    1. 先解析响应(还记录了解析所花的时间)
    2. 解析失败的重试
    3. 解析成功执行外部传入的完成回调
  2. 把这个闭包追加保存到Request的responseSerializers数组中
  3. 检测下当前Request是不是已经完成了,完成的话重新标记成执行中
  4. 检测下当前Request是否已经完成了全部响应的解析,如果是的话,就手动执行processNextResponseSerializer()方法开始继续执行responseSerializers数组中的解析闭包
  5. 检测下当前Request是否需要发送请求,需要的话,就调用resume()方法发送请求

AF.request为例,调用情况

简单的request请求,并对request后的AFDataResponse<String>不做处理

func requestAction() {/// 打印百度的htmlString/// 网址string是作为URLConvertible协议类型传入,实现一个asURL的方法转换为URLAF.request("https://www.baidu.com").responseString { resp inswitch resp.result {case let .success(str):debugPrint("request success:\(str)")case let .failure(err):debugPrint("request fail:\(err)")}}}

输入的是String类型的"https://www.baidu.com",输入的网址string是作为URLConvertible协议类型传入的,该协议可以看作是URL类型的协议抽象,需要实现一个asURL的方法,用来生成请求用的URL。

跳到第一个方法

使用传入的参数调用RequestEncodableConvertible()方法创建了一个convertible局部变量,类型只是个简单的结构体,实现了URLRequestConvertible协议类型

/// request 默认是get请求open func request(_ convertible: URLConvertible,method: HTTPMethod = .get,parameters: Parameters? = nil,encoding: ParameterEncoding = URLEncoding.default,headers: HTTPHeaders? = nil,interceptor: RequestInterceptor? = nil,requestModifier: RequestModifier? = nil) -> DataRequest {let convertible = RequestConvertible(url: convertible,method: method,parameters: parameters,encoding: encoding,headers: headers,requestModifier: requestModifier)return request(convertible, interceptor: interceptor)}

第二个方法

使用入参的convertible + 拦截器对象

Session所持有的rootQueue(回调执行队列),serializationQueue(响应解析队列),eventMonitor(事件监听器对象),以及把自己作为RequestDelegate对象,构建出了DataRequest对象

open func request(_ convertible: URLRequestConvertible, interceptor: RequestInterceptor? = nil) -> DataRequest {let request = DataRequest(convertible: convertible,underlyingQueue: rootQueue,serializationQueue: serializationQueue,eventMonitor: eventMonitor,interceptor: interceptor,delegate: self)perform(request)return request}

第三个方法

使用了rootQueue队列执行,先判断request要是取消了的话,直接return

    /// 准备发送请求 主入口func perform(_ request: Request) {rootQueue.async {//现在rootQueue中判断是否请求被取消guard !request.isCancelled else { return }//塞入到正在请求的Request集合中self.activeRequests.insert(request)//在requestQueue队列发送请求self.requestQueue.async {// Leaf types must come first, otherwise they will cast as their superclass.switch request {//Request为uploadRequest//然后在rootQueue告知事件监听器didCreateUploadable, 然后调用performSetupOperations方法//创建失败的话先在rootQueue告知监视器didFailToCreateUploadable, 错误消息为createUploadableFailed//然后在request中决定是否重试case let r as UploadRequest: self.performUploadRequest(r) // UploadRequest must come before DataRequest due to subtype relationship.//直接调用performSetupOperationscase let r as DataRequest: self.performDataRequest(r)//如果是新建下载, 直接调用performSetupOperations方法//如果是断点续传, 在rootQueue调用didReceiveResumeData方法, 详见下方断点续传部分case let r as DownloadRequest: self.performDownloadRequest(r)///直接调用performSetupOperationscase let r as DataStreamRequest: self.performDataStreamRequest(r)default: fatalError("Attempted to perform unsupported Request subclass: \(type(of: request))")}}}}

调用了performDataRequest

//直接调用performSetupOperationscase let r as DataRequest: self.performDataRequest(r)

第四个方法

检测是否在RequestQueue执行,然后继续调用performSetupOperations()方法继续处理

/// 处理成功后, 会调用didCreateURLRequest方法来更新状态func performDataRequest(_ request: DataRequest) {//当前在requestQueuedispatchPrecondition(condition: .onQueue(requestQueue))performSetupOperations(for: request, convertible: request.convertible)}

第五个方法

  • 构建原始URLRequest对象T
  • 对T进行有效行校验
  • 告知Request,T已经初始化完成,把T传给Request进行内部处理
  • 如果Request中有请求适配器,逐一调用适配器对T进行处理
  • 处理完成之后,告知Request已经适配完成
  • 最终调用didCreateURLRequest()方法完成
    func performSetupOperations(for request: Request,convertible: URLRequestConvertible,shouldCreateTask: @escaping () -> Bool = { true }){//当前在RequestQueuedispatchPrecondition(condition: .onQueue(requestQueue))//URLRequestConvertible生成的URLRequestlet initialRequest: URLRequestdo {initialRequest = try convertible.asURLRequest()//检测请求是否有效(get请求不能带body参数)try initialRequest.validate()} catch {//在rootQueue队列上报错误rootQueue.async { request.didFailToCreateURLRequest(with: error.asAFError(or: .createURLRequestFailed(error: error))) }return}//在rootQueue通知request,初始化URLRequest成功rootQueue.async { request.didCreateInitialURLRequest(initialRequest) }//检查是否有请求适配器guard !request.isCancelled else { return }guard let adapter = adapter(for: request) else {guard shouldCreateTask() else { return }//没有拦截器的话直接通知rootQueue.async { self.didCreateURLRequest(initialRequest, for: request) }return}//使用拦截器中的适配器来预处理请求adapter.adapt(initialRequest, for: self) { result indo {let adaptedRequest = try result.get()try adaptedRequest.validate()//通知处理完成self.rootQueue.async { request.didAdaptInitialRequest(initialRequest, to: adaptedRequest) }guard shouldCreateTask() else { return }self.rootQueue.async { self.didCreateURLRequest(adaptedRequest, for: request) }} catch {//任何错误都抛出requestAdaptationFailed错误self.rootQueue.async { request.didFailToAdaptURLRequest(initialRequest, withError: .requestAdaptationFailed(error: error)) }}}}

因为没有拦截器所以调用了didCreateURLRequest(initialRequest, for: request)

第六个方法

/// 当创建请求完成, 拦截适配器处理完成之后, 就会来到这里的逻辑func didCreateURLRequest(_ urlRequest: URLRequest, for request: Request) {dispatchPrecondition(condition: .onQueue(rootQueue))///通知创建request成功request.didCreateURLRequest(urlRequest)guard !request.isCancelled else { return }// 创建URLSessionTask,基类Request为实现该方法,几个子类各自实现 ```let task = request.task(for: urlRequest, using: session)// 写入session的request—Task数据队列保存requestTaskMap[request] = task// 线程安全保存taskrequest.didCreateTask(task)updateStatesForTask(task, request: request)}

request.task(for: urlRequest, using: session)

创建了URLSessionTask,这就是真正用来发送请求的task,然后把这个task存在了Session的requestTaskMap字典中,key是request,这样可以在任意时候根据request找到它所对应的task。开始发送请求。

AF对请求的处理完成

Response (接受响应)处理

Request() -> response()

因为写的是responseString

第一步

唯一需要必须传入的参数就是完成的回调闭包

completionHandler: @escaping (AFDataResponse<String>) -> Void) -> Self
/// Adds a handler using a `StringResponseSerializer` to be called once the request has finished./// 添加解析方法/// - Parameters:///   - queue:               The queue on which the completion handler is dispatched. `.main` by default.///   - dataPreprocessor:    `DataPreprocessor` which processes the received `Data` before calling the///                          `completionHandler`. `PassthroughPreprocessor()` by default.///   - encoding:            The string encoding. Defaults to `nil`, in which case the encoding will be determined///                          from the server response, falling back to the default HTTP character set, `ISO-8859-1`.///   - emptyResponseCodes:  HTTP status codes for which empty responses are always valid. `[204, 205]` by default.///   - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.///   - completionHandler:   A closure to be executed once the request has finished.////// - Returns:               The request.@discardableResultpublic 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(queue: queue,responseSerializer: StringResponseSerializer(dataPreprocessor: dataPreprocessor,encoding: encoding,emptyResponseCodes: emptyResponseCodes,emptyRequestMethods: emptyRequestMethods),completionHandler: completionHandler)}

执行的是response(

第二步

对数据进行一大堆的逻辑、解析处理

/// Adds a handler to be called once the request has finished./// 使用自定义解析器解析/// - Parameters:///   - queue:              The queue on which the completion handler is dispatched. `.main` by default///   - responseSerializer: The response serializer responsible for serializing the request, response, and data.///   - completionHandler:  The code to be executed once the request has finished.////// - Returns:              The request.@discardableResultpublic func response<Serializer: DataResponseSerializerProtocol>(queue: DispatchQueue = .main,responseSerializer: Serializer,completionHandler: @escaping (AFDataResponse<Serializer.SerializedObject>) -> Void)-> Self {appendResponseSerializer {// Start work that should be on the serialization queue.// 开始解析响应, 必须在响应解析队列完成, 因为有解析操作, 所以开始计时let start = ProcessInfo.processInfo.systemUptime// 用入参解析器解析数据,catch错误并转换成AFErrorlet result: AFResult<Serializer.SerializedObject> = Result {try responseSerializer.serialize(request: self.request,response: self.response,data: self.data,error: self.error)}.mapError { error inerror.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error)))}// 用app启动的时间差值来计算出解析所花的时间let end = ProcessInfo.processInfo.systemUptime// End work that should be on the serialization queue.// 用request内部队列来继续处理self.underlyingQueue.async {// 组装DataResponse,Success类型为序列化协议中的泛型SerializedObject类型let response = DataResponse(request: self.request,response: self.response,data: self.data,metrics: self.metrics,serializationDuration: end - start,result: result)// 告知监听器self.eventMonitor?.request(self, didParseResponse: response)guard let serializerError = result.failure, let delegate = self.delegate else {self.responseSerializerDidComplete { queue.async { completionHandler(response) } }return}// 解析出错,准备重试delegate.retryResult(for: self, dueTo: serializerError) { retryResult in// 是否完成的回调,nil表示要重试var didComplete: (() -> Void)?defer {// 用defer处理(也就是return 或者执行最后一行再处理defer里的内容)if let didComplete = didComplete {self.responseSerializerDidComplete { queue.async { didComplete() } }}}/// 根据参数retryResult判断是否重试switch retryResult {case .doNotRetry: // 不重试直接完成didComplete = { completionHandler(response) }case let .doNotRetryWithError(retryError): // 不重试,把error替换掉// 用新的retryError初始化resultlet result: AFResult<Serializer.SerializedObject> = .failure(retryError.asAFError(orFailWith: "Received retryError was not already AFError"))// 封装新的Responselet response = DataResponse(request: self.request,response: self.response,data: self.data,metrics: self.metrics,serializationDuration: end - start,result: result)didComplete = { completionHandler(response) }case .retry, .retryWithDelay: //重试delegate.retryRequest(self, withDelay: retryResult.delay)}}}}return self}

这一大堆的处理,其实就是appendResponseSerializer()的方法接收一个很多数据处理的闭包参数

最后返回的是 return self

responseString方法返回的类型是self,可以连续链式的调用responseString

第三步

解析数据

根据Request当前状态来延迟调用

/// Appends the response serialization closure to the instance.//////  - Note: This method will also `resume` the instance if `delegate.startImmediately` returns `true`.///  线程安全添加响应解析回调/// - Parameter closure: The closure containing the response serialization call.func appendResponseSerializer(_ closure: @escaping () -> Void) {$mutableState.write { mutableState in// 加入数组mutableState.responseSerializers.append(closure)// 如果请求完成了,标记为继续if mutableState.state == .finished {mutableState.state = .resumed}//如果响应解析完成了, 调一下解析方法if mutableState.responseSerializerProcessingFinished {underlyingQueue.async { self.processNextResponseSerializer() }}// 如果不是完成状态,且可以转换为继续状态if mutableState.state.canTransitionTo(.resumed) {// 问session是否需要立即开始,是的话立即调用resume()underlyingQueue.async { if self.delegate?.startImmediately == true { self.resume() } }}}}
  • 如果当前Request被标记finished,表示这个Request已经请求完成了,现在是正在对他再一次执行response,那就把request标记成**resumed,表示正在执 ** ,后续逻辑就不会发送请求,而是直接调用入解析回调。
  • 检测下responseSerializerProcessingFinished字段,该字段是用来标记当前正在执行的某个解析回调是否完成了,如果完成了,就手动调用一下processNextResponseSerializer()方法来执行下一个回调闭包。如果尚未完成,就不需要手动调用。因为解析完成的逻辑会自己检测执行下一个解析。递归的来着。。。
  • 最后,需要判断下,当前request能否变成resume,这个操作是因为:只有initialized,suspended,这两个状态,可以变成resume,代表需要对task调用resume方法来发送请求/继续请求。而且,必须request的startImmediately设置为true才会自动调用resume()方法,否则,需要手动调用resume()才能发送请求。

自定义AlamofireManager

实例化session

class AlamofireManager {private static var session: Session!class func shareInstance() ->  Session {//不应该每次调用此方法都要初始化,应当检测是否初始化过,如果已经初始化就不需要进行初始化if session == nil {let configuration = URLSessionConfiguration.default/// 请求timeout截止时间为50秒configuration.timeoutIntervalForRequest = 50session = Session(configuration: configuration)}return session}
}

前提准备BaseBean

Swift的反射功能,的iOS Runtime不一样,Swift的反射用了另一套API,实现机制也完全不一样

反射是一种计算机处理方式。是程序可以访问、检测和修改它本身状态或行为的一种能力。

  • 动态地创建对象和属性,
  • 动态地获取一个类里面所有的属性,方法。
  • 获取它的父类,或者是实现了什么样的接口(协议)
  • 获取这些类和属性的访问限制(Public 或者 Private)
  • 动态地获取运行中对象的属性值,同时也能给它赋值(KVC)
  • 动态调用实例方法或者类方法
  • 动态的给类添加方法或者属性,还可以交换方法(只限于Objective-C)
class BaseBean: NSObject,NSCoding {required override init() {}//当子类中定义了异于父类中的构造方法(参数名称不同、参数个数不同、参数类型不同),//那么父类中使用required修饰的构造方法在子类中必须实现,而且这个方法必须使用required关键字而不能使用override修饰required init?(coder: NSCoder) {super.init()// 反射机制(是指可以动态获取类型、成员信息)let mir = Mirror(reflecting: self)for item in mir.children {if let label = item.label, let cachedValue = coder.decodeObject(forKey: label) {if let stringValue = cachedValue as? String {self.setValue(stringValue, forKey: label)}else if let intValue = cachedValue as? Int {self.setValue(intValue, forKey: label)}else {}}}}func encode(with coder: NSCoder) {let mir = Mirror(reflecting: self)for item in mir.children {if let label = item.label, let labelValue = self.value(forKey: label) {coder.encode(labelValue, forKey: label)}}}
}

如果model使用NSObject类型,而不是Codable类型,就需要JSON转NSObject类型的model

为更方便,引入了使用swiftyJSON

https://github.com/SwiftyJSON/SwiftyJSON

import SwiftyJSON// JSON转NSObject类型的model
class DataArchiveUtils<D: NSObject>: NSObject {/// 列表类型class func getDatas(json: JSON, dataKey: String) -> [D]? {let dataData = json[dataKey].description.data(using: .utf8)let data = Reflect<D>.mapObjects(data: dataData)return data}class func getData(json: JSON, dataKey: String) -> D? {let dataData = json[dataKey].description.data(using: .utf8)let data = Reflect<D>.mapObject(data: dataData)return data}}

Any类型的话也可以使用 TTReflect

https://github.com/TifaTsubasa/TTReflect

把有用的抽出来

///转换json为NSObject的对象 data转为继承NSObject的对象或NSObject的数组对象
///Swift版JsonModel自动映射 https://github.com/TifaTsubasa/TTReflect
class Reflect<M: NSObject> {// MARK: - reflect with json/**map object with json- returns: special type object*/class func mapObject(json: Any?) -> M {guard let json = json else { return M() }guard json is NSDictionary || json is [String: AnyObject] else {return M()}let model = M()model.mapProperty(json)return model}class func mapObjects(json: Any?) -> [M] {guard let json = json else {return [M]()}guard json is NSArray || json is [AnyObject] else {return [M]()}guard let arrayJson = json as? [AnyObject] else {return [M]()}let models: [M] = arrayJson.map {return Reflect<M>.mapObject(json: $0)}return models}// MARK: - reflect with dataclass func mapObject(data: Data?) -> M {guard let data = data else { return M() }do {let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)return Reflect<M>.mapObject(json: json as AnyObject?)} catch {debugPrint("Serializat json error: \(error)")}return M()}class func mapObjects(data: Data?) -> [M] {guard let data = data else { return [M]() }do {let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)return Reflect<M>.mapObjects(json: json as AnyObject?)} catch {debugPrint("Serializat json error: \(error)")}return [M]()}// MARK: - reflect with plist nameclass func mapObject(_ plistName: String?) -> M {let plistPath = Bundle.main.path(forResource: plistName, ofType: "plist")guard let path = plistPath else {debugPrint("Reflect error: Error plist name")return M()}let json = NSDictionary(contentsOfFile: path)return Reflect<M>.mapObject(json: json)}class func mapObjects(_ plistName: String?) -> [M] {let plistPath = Bundle.main.path(forResource: plistName, ofType: "plist")guard let path = plistPath else {debugPrint("Reflect error: Error plist name")return [M]()}let json = NSArray(contentsOfFile: path)return Reflect<M>.mapObjects(json: json)}
}// MARK: - object map setting protocol
@objc
public protocol TTReflectProtocol {@objc optional func setupMappingReplaceProperty() -> [String: String]@objc optional func setupMappingObjectClass() -> [String: AnyClass]@objc optional func setupMappingElementClass() -> [String: AnyClass]@objc optional func setupMappingIgnorePropertyNames() -> [String]
}// MARK: - private function
extension NSObject: TTReflectProtocol {// main functionfileprivate func mapProperty(_ json: Any) {if json is NSNull { return }// mapping settinglet replacePropertyName = self.getMappingReplaceProperty()let ignorePropertyNames = self.getMappingIgnorePropertyNames()let mappingObjectClass = self.getMappingObjectClass()let mappingElementClass = self.getMappingElementClass()let keys = ergodicObjectKeys()for key in keys {let jsonKey = replacePropertyName[key] ?? keylet jsonValue = (json as AnyObject).value(forKey: jsonKey)guard !ignorePropertyNames.contains(key) else {continue}  // ignore propertyguard let value = jsonValue else {continue}if value is NSNull {  // ignore null porpertydebugPrint("Reflect error: The key \(jsonKey) value is \(value)")continue}setPropertyValue(value as AnyObject, forKey: key)// map sub objectif let trackObject = mapSubObject(key, jsonKey: jsonKey, mappingObjectClass: mappingObjectClass, value: value as AnyObject){setValue(trackObject, forKey: key)}// map sub arrayif let trackObjects = mapSubObjectArray(key, jsonKey: jsonKey, mappingElementClass: mappingElementClass, value: value as AnyObject) {setValue(trackObjects, forKey: key)}}}fileprivate func mapSubObject(_ key: String, jsonKey: String, mappingObjectClass: [String: AnyClass], value: AnyObject) -> AnyObject? {guard mappingObjectClass.keys.contains(jsonKey) else {return nil}guard let objClass = mappingObjectClass[jsonKey] as? NSObject.Type else {fatalError("Reflect error: Sub-model is not a subclass of NSObject")}let model = objClass.init()guard value is NSDictionary || value is [String: AnyObject] else {debugPrint("Reflect error: Error key: \(key) -- mapping sub-model without a dictionary json")return nil}model.mapProperty(value)return model}fileprivate func mapSubObjectArray(_ key: String, jsonKey: String, mappingElementClass: [String: AnyClass], value: AnyObject) -> AnyObject? {guard mappingElementClass.keys.contains(jsonKey) else {return nil}guard let objClass = mappingElementClass[jsonKey] as? NSObject.Type else {fatalError("Reflect error: Sub-model is not a subclass of NSObject")}guard let subArrayJson = value as? [AnyObject] else {debugPrint("Reflect error: Error key: \(key) -- mapping sub-model array without a array json")return nil}let submodelArray: [NSObject] = subArrayJson.map {let submodel = objClass.init()if $0 is NSDictionary || $0 is [String: AnyObject] {submodel.mapProperty($0)} else {debugPrint("Reflect error: Error key: \(key) -- mapping sub-model array element without a dictionary json")}return submodel}return submodelArray as AnyObject}fileprivate func setPropertyValue(_ value: AnyObject?, forKey key: String) {// convert typevar transFlag: Bool?var transValue: AnyObject?let valueTuple = (self.value(forKey: key), value)switch valueTuple {case let (objValue as NSNumber, jsonValue as NSString):transFlag = falseif objValue.isBool { // string -> boolif jsonValue == "true" {transValue = true as AnyObject?transFlag = true}} else { // string -> numberif let res = NumberFormatter().number(from: jsonValue as String) {transValue = restransFlag = true}}case let (_ as NSString, jsonValue as NSNumber):transValue = "\(jsonValue)" as AnyObject?transFlag = truedefault:setValue(value, forKey: key)}if let transFlag = transFlag {if transFlag {// Debug.DebugPRINT(obj: "Reflect warning: The key \(key) have different type value")setValue(transValue, forKey: key)} else {// Debug.DebugPRINT(obj: "Reflect error: The key \(key) map error type")}}}//func ergodicObjectKeys() -> [String] {var keys = [String]()let mirror = Mirror(reflecting: self)if let objectKeys = reflectObjectKeys(mirror) {keys = objectKeys}return keys}func reflectObjectKeys(_ mirror: Mirror?) -> [String]? { // iOS8+guard let mirror = mirror else { return nil }var keys = mirror.children.compactMap {$0.label}if mirror.superclassMirror?.subjectType != NSObject.self {if let subKeys = reflectObjectKeys(mirror.superclassMirror) {keys.append(contentsOf: subKeys)}}return keys}func getObjectKeys(_ cls: AnyClass) -> [String] { // iOS 7var keys = [String]()var propNum: UInt32 = 0let propList = class_copyPropertyList(cls, &propNum)for index in 0..<numericCast(propNum) {let prop: objc_property_t = propList![index]keys.append(String(validatingUTF8: property_getName(prop))!)}if class_getSuperclass(cls) != NSObject.self {if let k = class_getSuperclass(cls) {keys.append(contentsOf: getObjectKeys(k))}}return keys}fileprivate func getMappingReplaceProperty() -> [String: String] {var replacePropertyName = [String: String]()return getProtocolSetting(&replacePropertyName, aSelector: #selector(TTReflectProtocol.setupMappingReplaceProperty))}fileprivate func getMappingIgnorePropertyNames() -> [String] {var ignorePropertyNames = [String]()return getProtocolSetting(&ignorePropertyNames, aSelector: #selector(TTReflectProtocol.setupMappingIgnorePropertyNames))}fileprivate func getMappingObjectClass() -> [String: AnyClass] {var mappingObjectClass = [String: AnyClass]()return getProtocolSetting(&mappingObjectClass, aSelector: #selector(TTReflectProtocol.setupMappingObjectClass))}fileprivate func getMappingElementClass() -> [String: AnyClass] {var mappingElementClass = [String: AnyClass]()return getProtocolSetting(&mappingElementClass, aSelector: #selector(TTReflectProtocol.setupMappingElementClass))}fileprivate func getProtocolSetting<T>(_ emptySetting: inout T, aSelector: Selector) -> T {guard self.responds(to: aSelector) else {return emptySetting}let res = self.perform(aSelector)emptySetting = res?.takeUnretainedValue() as! Treturn emptySetting}
}

还有一个就是JSONDecoder,Data类型转换model,model是继承codable

let decoder = JSONDecoder()let model = try? decoder.decode(NetModel.self, from: jsonData)if model != nil {success(model!)}

自定义request RequestClient<T: BaseBean>: NSObject

T:继承BaseBean,主要用到是callback第一个参数的请求结果 Reflect<T>.mapObject(data: response.data),callback的第二个为请求是否成功,nil为不成功判断,第三个为原始数据

class RequestClient<T: BaseBean>: NSObject {/// 请求接口////// - parameter method: 请求方式/// - parameter url: 请求url/// - parameter params: 请求参数/// - parameter callback: 回调,第一个参数为请求结果,第二个参数为错误信息,nil表示成功,第三个参数是请求返回原始数据class func request(method: HTTPMethod, url: String, params: [String: Any], callback:((T?, String?,String?) -> Void)?) {let newURL = "https://user.52jtg.com/" + urllet p = paramsdebugPrint(newURL)debugPrint(p)/// post放在body里,其余放在queryString里let encode = URLEncoding(destination: method == HTTPMethod.post ? .httpBody: .queryString)AlamofireManager.shareInstance().request(newURL, method: method, parameters: p, encoding: encode, headers: nil).responseString { response in/// 序列化响应的结果if response.result.isSuccess  {do {let json = try JSON(data: response.data!)debugPrint(json)if json["successCode"] == "true" || json["successCode"] == true {/// data转换为NSObject对象callback?(Reflect<T>.mapObject(data: response.data),nil,response.value)}else {let message: String = json["message"].stringValuecallback?(nil, message, response.value)}} catch (let error) {let str = String(data: response.data!, encoding: String.Encoding.utf8)debugPrint(str ?? "")debugPrint(error.localizedDescription)callback?(nil,error.localizedDescription,response.value)}}else { //response.result.isFailureif let error = response.error?.localizedDescription {callback?(nil, "请求失败: \(error)",response.value)}else {callback?(nil, "请求失败:未知错误",response.value)}}}}

上传图片

 class func upLoad(imgs: [UIImage],url: String, params: [String: Any],progressClosure: ((Double) -> Void)? = nil, callback: ((T?, String?, String?) -> Void)?) {

写了比较少用到的请求json文件,该JSON为swiftyJSON类

class RequestJsonData {/// 获取JSON/// - Parameters:///   - url: url转换///   - params: Parameters///   - method: 默认postclass func getJSON(url: String, params: [String : String], method: HTTPMethod = .post,completed:((JSON)-> Void)?,failed:((String?)->Void)?) {

运用到实例当中,我用家长端的一个请求数据为例子

https://eolinker.52jtg.com/#/home/project/inside/api/detail?groupID=-1&apiID=1866&projectName=今托管-家长端&projectID=14

class func requestModel(success: @escaping(NetModel)-> Void,failue: @escaping Failure) {var params: [String: Any] = [:]params = ["keyword":"","lat":22.99430230034722,"lon": 113.3336423068576,"page": 1,"pageSize":20,"sortName": "DISTANCE"]let urlTest = "search/preferred-shop/index-home-keyword-shop/?requestType=json"//不使用泛型直接转换,// RequestClient.request(method: .get, url: urlTest, params: params) { bean, error,result in/// 使用继承NSObject的泛型转换RequestClient<NetBean>.request(method: .get, url: urlTest, params: params) { bean, error,result inif error == nil || error == "" {if let jsonData = result?.data(using: .utf8) {// BaseBean解析//data转换为NSObject对象,model继承 BaseBean// 代码:callback?(Reflect<T>.mapObject(data: response.data),nil,response.value)let beanModel = beanprint(beanModel?.result)// 直接对原始数据使用JSONDecoder解析// model继承codableprint(jsonData)let decoder = JSONDecoder()let model = try? decoder.decode(NetModel.self, from: jsonData)if model != nil {success(model!)}}}else {print(error ?? "错误信息未知")}}}

可以选择两种形式返回,一个是success闭包返回继承codable的model数据返回,另一个也可以使用继承BaseBean(NSObject)的model数据返回。

func requestAction2() {NetTool.requestModel { model inself.model = modelprint(model)} failue: { error inprint(error.localizedDescription)}}

如果不是默认格式的数据,也可以自己写一个出来的alamofire请求

 func shareInstance() {var params: [String: Any] = [:]params = ["keyword":"","lat":22.99430230034722,"lon":113.3336423068576,"page": 1,"pageSize":20,"sortName": "DISTANCE"]let urlTest = "https://user.52jtg.com/search/preferred-shop/index-home-keyword-shop/?requestType=json"AlamofireManager.shareInstance().request(urlTest, method: .get, parameters: params).responseString { response inif response.result.isSuccess {var json: JSON?if let dataStr = response.data {json = JSON(dataStr)}if json?["successCode"]  == true || json?["successCode"] == "true" {if let result = json?["result"] {self.modelList = DataArchiveUtils<NetListBean>.getDatas(json: result, dataKey: "list") ?? []print(self.modelList)}}}}}

取消当前的请求(用URL来判断)

    /// 取消当前sessionManager下某个请求class func cancelRequest(path: String) {if session != nil {let urlpath = "https://user.52jtg.com/" + pathAF.session.getAllTasks { sessionTask insessionTask.forEach { task inif task.currentRequest?.url?.path == urlpath {task.cancel()}}}}}

设置cookies

/// 设置所有的cookiesclass func setCookies(properties: [HTTPCookiePropertyKey : String]) {if let cookie = HTTPCookie(properties: properties) {// 单例AF.session.configuration.httpCookieStorage?.setCookie(cookie)}}///使用案例let cookieProps = [HTTPCookiePropertyKey.domain: "##put your domain here##",HTTPCookiePropertyKey.path: "/123",HTTPCookiePropertyKey.name: "fffff",HTTPCookiePropertyKey.value: "21312"]AlamofireManager.setCookies(properties: cookieProps)

删除cookies

        // 删除所有的cookielet cstorage = HTTPCookieStorage.shared
//        if let cookies = cstorage.cookies(for: url) {  // 也可以单独删除cookiesif let cookies = cstorage.cookies {for cookie in cookies {cstorage.deleteCookie(cookie)}}

下载

    /// 下载file/// - Parameters:///   - file: 真实的url下载链接///   - fileName: 下载后的名称func downLoadFile(file: String, fileName: String) {var fileStr = fileif fileStr.isIncludeChinese() {fileStr = file.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? ""}var url = URL(string: fileStr)let destination: DownloadRequest.Destination = { _, response inlet documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]let fileURL = documentsURL.appendingPathComponent("file1/\(fileName)")// 两个参数表示如果又同名文件则会覆盖,如果路径中文文件夹不存在则会自动创建url = fileURLreturn (fileURL,[.removePreviousFile,.createIntermediateDirectories])}AlamofireManager.shareInstance().download(fileStr,to: destination).downloadProgress { progress inprint(progress)}.responseData { response inif let url = url {// 验证文件self.docController = UIDocumentInteractionController(url: url)self.docController.presentOptionsMenu(from: self.view.bounds,in: self.view ,animated: true)}}}

多表单

核心语句

         AlamofireManager.shareInstance().upload(multipartFormData: { multipartFormData in// 保存类型for imageData in imageDataArray {let timeStamp = Int(Date().timeIntervalSince1970)multipartFormData.append(imageData, withName: "files",fileName: "\(timeStamp)_\(imageData).jpeg",mimeType: "image/jpeg")}}, to: url).responseString

multipartFormData

回车换行字符串,内部做好数据的格式

// 封装回车换行字符串enum EncodingCharacters {static let crlf = "\r\n"}/// 封装多表单数据的分隔符,该分隔符需要存放在body头中enum BoundaryGenerator {enum BoundaryType {/// 起始:--分隔符\r\ncase initial/// 中间:\r\n --分隔符\r\ncase encapsulated/// 结束:\r\n --分隔符 -- \r\ncase final}
/// 生成分隔符Data,拼接数据用static func boundaryData(forBoundaryType boundaryType: BoundaryType, boundary: String) -> Data {let boundaryText: Stringswitch boundaryType {case .initial:boundaryText = "--\(boundary)\(EncodingCharacters.crlf)"case .encapsulated:boundaryText = "\(EncodingCharacters.crlf)--\(boundary)\(EncodingCharacters.crlf)"case .final:boundaryText = "\(EncodingCharacters.crlf)--\(boundary)--\(EncodingCharacters.crlf)"}return Data(boundaryText.utf8)}

只需要调用multipartFormData.append(imageData, withName: "files",fileName: "\(timeStamp)_\(imageData).jpeg",mimeType: "image/jpeg")就能转换为标准格式的表单头(contents)

/// 最终编码的表单数据格式:/// - `前分隔符(若是第一个数据块, 就没有前分隔符)`/// - `表单头`/// - `表单数据`/// - `后分隔符(若是最后一个数据块, 后分隔符是终结分隔符)`public func append(_ stream: InputStream, withLength length: UInt64, headers: HTTPHeaders) {// 封装成bodyPart对象let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length)// 存入数组bodyParts.append(bodyPart)}

执行uploadrequest的build 数据整合并判断可以upload

    func build() throws -> UploadRequest.Uploadable {let uploadable: UploadRequest.Uploadableif multipartFormData.contentLength < encodingMemoryThreshold {// 把保存多表单的数据encode(编码)let data = try multipartFormData.encode()uploadable = .data(data)} else {let tempDirectoryURL = fileManager.temporaryDirectorylet directoryURL = tempDirectoryURL.appendingPathComponent("org.alamofire.manager/multipart.form.data")let fileName = UUID().uuidStringlet fileURL = directoryURL.appendingPathComponent(fileName)try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)do {try multipartFormData.writeEncodedData(to: fileURL)} catch {// Cleanup after attempted write if it fails.try? fileManager.removeItem(at: fileURL)throw error}uploadable = .file(fileURL, shouldRemove: true)}return uploadable}
  • 数据就是通过,格式容器初始化

  • 然后用户传递需要上传的数据,填充进去

  • 包装成一个个 bodyPart,通过一个结合容器收集bodyParts

  • 全部包装完毕,遍历 bodyParts 进行详细编码

  • 首先拼接分隔符,拼接固定格式头信息,然后通过 stream 读取具体值,

  • 通过data 传进,调用 URLSession 响应的方法,

  • 通过 SessionDelegate 接受上传代理 - 最后下发给UploadTaskDelegate 最终返回上传情况

后面跟Request原理差不多 也是执行perform(request)

.responseString { response in ///响应结果

HTTPS

1、认证服务器:第一阶段服务器会提供经CA机构认证颁发的服务器证书,如果认证该服务器证书的CA机构,存在于浏览器的受信任CA机构列表中,并且服务器证书中的信息与当前正在访问的网站(域名等)一致,那么浏览器就认为服务端是可信的,并从服务器证书中取得服务器公钥,用于后续流程

2、协商会话密钥。客户端在认证完服务器,获得服务器的公钥之后,利用该公钥与服务器进行加密通信,协商出两个会话密钥,分别是用于加密客户端往服务端发送数据的客户端会话密钥,用于加密服务端往客户端发送数据的服务端会话密钥

3、加密通讯

默认情况下,Alamofire会收到与URLSession相同的自动TLS证书和证书链,同默认情况下,Alamofire会自动帮我们处理身份验证

证书编写: 可以多个证书

public func trustSession() -> Session {///PinnedCertificatesTrustEvaluator 证书默认 Bundle.main.af.certificateslet certificates = AlamofireManager.certitificates()let keys = AlamofireManager.publicKeys()let evaluators: [String: ServerTrustEvaluating] = ["cert.xxxxx.com": PinnedCertificatesTrustEvaluator(certificates: certificates),"keys.xxxxx.com": PublicKeysTrustEvaluator(keys:keys),"any": ByIpTrustEvaluator()]let manager = ServerTrustManager(evaluators: evaluators)return Session(serverTrustManager: manager)}
/// 遍历整个工程bundle获取证书public static func certitificates(in bundle: Bundle = Bundle.main) -> [SecCertificate] {var certificates: [SecCertificate] = []let paths = Set([".cer", ".CER", ".crt", ".CRT", ".der", ".DER"].map { fileExtension inbundle.paths(forResourcesOfType: fileExtension, inDirectory: nil)}.joined())for path in paths {if let certificateData = try? Data(contentsOf: URL(fileURLWithPath: path)) as CFData,let certificate = SecCertificateCreateWithData(nil, certificateData) {certificates.append(certificate)}}return certificates}/// 获取公钥keypublic static func publicKeys(in bundle: Bundle = Bundle.main) -> [SecKey] {var publicKeys: [SecKey] = []// 把证书拿到for certificate in certitificates(in: bundle) {// ios 12以上 iOS10.3 SecCertificateCopyPublicKeyif let publicKey = SecCertificateCopyKey(certificate) {publicKeys.append(publicKey)}}return publicKeys}

验证方法

try evaluator.evaluate(trust, forHost: host)

interceptor 适配器、拦截器、重试器

继承RequestInterceptor

修改request头

  func adapt(_ urlRequest: URLRequest,for session: Session,completion: @escaping (Result<URLRequest, Error>) -> Void) {var urlRequest = urlRequestif let token = TokenManager.shared.fetchAccessToken() {urlRequest.setValue("token \(token)", forHTTPHeaderField: "Authorization")}completion(.success(urlRequest))}

修改重试次数

let retryLimit = 5
let retryDelay: TimeInterval = 10func retry(_ request: Request,for session: Session,dueTo error: Error,completion: @escaping (RetryResult) -> Void) {let response = request.task?.response as? HTTPURLResponse//Retry for 5xx status codesiflet statusCode = response?.statusCode,(500...599).contains(statusCode),request.retryCount < retryLimit {completion(.retryWithDelay(retryDelay))} else {return completion(.doNotRetry)}}

eventMonitors 通知事件监测器

继承 EventMonitor

请求完成

func requestDidFinish(_ request: Request) {print(request.description)}

DataRequest成功创建序列化的DataResponse时回调

  func request<Value>(_ request: DataRequest,didParseResponse response: DataResponse<Value, AFError>) {guard let data = response.data else {return}if let json = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) {print(json)}}

cachedResponseHandler

缓存数据

/// 带缓存带重试器,事件监听器的Sessionclass func GitInstance() ->  Session {let interceptor = GitRequestInterceptor()let configuration = URLSessionConfiguration.defaultlet networkLogger = GitNetworkLogger()/// 请求timeout截止时间为50秒configuration.timeoutIntervalForRequest = 50/// 先修改数据,再缓存新的数据,参数为修改数据的闭包,改闭包返回可选的新缓存数据let responseCacher = ResponseCacher(behavior: .modify { _, response inlet userInfo = ["date": Date()]return CachedURLResponse(/// 与实例关联的URL响应对象。response: response.response,/// 缓存响应的数据data: response.data,/// 缓存响应的用户信息字典userInfo: userInfo,/// 硬盘和内存没有限制storagePolicy: .allowed)})return Session(configuration: configuration,interceptor: interceptor,cachedResponseHandler: responseCacher,eventMonitors: [networkLogger])}
// 用request的缓存处理器处理缓存handler.dataTask(dataTask, willCacheResponse: proposedResponse, completion: completionHandler)

如果是缓存数据.cache

直接返回 completion(response)

Alamofire源码分析相关推荐

  1. Alamofire源码分析 - POST请求

    本文基于Alamofire v5.6.1,主要通过一个POST请求看看Alamofire源码是如何实现该过程的.请求代码如下: let url = "xxxxxxxxxxxxxxxxxxxx ...

  2. Alamofire源码解读系列(十一)之多表单(MultipartFormData)

    本篇讲解跟上传数据相关的多表单 前言 我相信应该有不少的开发者不明白多表单是怎么一回事,然而事实上,多表单确实很简单.试想一下,如果有多个不同类型的文件(png/txt/mp3/pdf等等)需要上传给 ...

  3. 开源项目源码分析(Kickstarter-iOS )(一)

    开源项目源码分析(Kickstarter-iOS )(一) 1.Kickstarter开源项目简介 2. Kickstarter项目结构 2.1 Makefile 文件 2.2 Git submodu ...

  4. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  5. SpringBoot-web开发(四): SpringMVC的拓展、接管(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) SpringBoot-web开发(二): 页面和图标定制(源码分析) SpringBo ...

  6. SpringBoot-web开发(二): 页面和图标定制(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) 目录 一.首页 1. 源码分析 2. 访问首页测试 二.动态页面 1. 动态资源目录t ...

  7. SpringBoot-web开发(一): 静态资源的导入(源码分析)

    目录 方式一:通过WebJars 1. 什么是webjars? 2. webjars的使用 3. webjars结构 4. 解析源码 5. 测试访问 方式二:放入静态资源目录 1. 源码分析 2. 测 ...

  8. Yolov3Yolov4网络结构与源码分析

    Yolov3&Yolov4网络结构与源码分析 从2018年Yolov3年提出的两年后,在原作者声名放弃更新Yolo算法后,俄罗斯的Alexey大神扛起了Yolov4的大旗. 文章目录 论文汇总 ...

  9. ViewGroup的Touch事件分发(源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,View的touch事件分发相对比较简单,可参考 View的Touch事件分发(一.初步了解) View的Touch事 ...

最新文章

  1. JAVA String之contains方法使用
  2. 组合计数 ---- 2020 icpc 上海 The Journey of Geor Autumn(思维划分问题计数+预处理优化)
  3. python27-资源|全机器学习和Python的27个速查表(完整版)
  4. TCP 和 UDP 绑定同一端口通信的解释
  5. 外部定义了一个数组,在函数内用指针对其操作
  6. php 访问 sharepoint列表,SharePoint—用REST方式访问列表
  7. (99)FPGA最大延迟与最小延迟基础
  8. 何使用ultraiso软碟通制作u盘启动盘(转载)
  9. vue-scroller的使用 开发自己的 scroll 插件
  10. source insight 使用技巧
  11. php 判断设备是手机还是平板还是pc
  12. 为程序界面添加滑动条
  13. 魅族手机Root之后如何恢复
  14. 低端电流检测电路实测
  15. Linux lvm(pv、vg、lv)操作命令收集
  16. 如何处理多重共线性问题
  17. 青龙面板之滴滴果园撸水果(2022.6.21更新库)
  18. ubuntu设置pac代理
  19. 报错:HTTP 401 Unauthorized
  20. 六、添加艺术字到图片

热门文章

  1. 每日一句,每日10词【2】(26天)
  2. threejs学习--vue-cli集成threejs后,加载gltf模型FPS很低问题解决(四)
  3. 中秋国庆两节临近,知行爱智告诉你机场广告该怎么投放
  4. 【虹科白皮书】高级分析对NetOps的重要性
  5. CodeForcesRound665div2
  6. 如何左右并排显示窗口_电脑任务栏多个窗口怎么并排显示
  7. toolbar实现设置
  8. Web学习 HTML5,CSS3新增内容
  9. DataPipeline完成B+轮1.2亿元人民币融资,定义基于DataOps理念的下一代数据基础设施
  10. 游览器加载渲染网页过程分析