IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> Alamofire源码分析 -> 正文阅读

[开发测试]Alamofire源码分析

源码分析

(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的方法转换为URL
        AF.request("https://www.baidu.com").responseString { resp in
            switch 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.
                //直接调用performSetupOperations
                case let r as DataRequest: self.performDataRequest(r)
                //如果是新建下载, 直接调用performSetupOperations方法
                //如果是断点续传, 在rootQueue调用didReceiveResumeData方法, 详见下方断点续传部分
                case let r as DownloadRequest: self.performDownloadRequest(r)
                ///直接调用performSetupOperations
                case let r as DataStreamRequest: self.performDataStreamRequest(r)
                default: fatalError("Attempted to perform unsupported Request subclass: \(type(of: request))")
                }
            }
        }
    }

调用了performDataRequest

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

第四个方法

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

/// 处理成功后, 会调用didCreateURLRequest方法来更新状态
    func performDataRequest(_ request: DataRequest) {
        //当前在requestQueue
        dispatchPrecondition(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 })
    {
        //当前在RequestQueue
        dispatchPrecondition(condition: .onQueue(requestQueue))

        //URLRequestConvertible生成的URLRequest
        let initialRequest: URLRequest

        do {
            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 in
            do {
                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
        // 线程安全保存task
        request.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.
    @discardableResult
    public 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.
    @discardableResult
    public 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错误并转换成AFError
            let result: AFResult<Serializer.SerializedObject> = Result {
                try responseSerializer.serialize(request: self.request,
                                                 response: self.response,
                                                 data: self.data,
                                                 error: self.error)
            }.mapError { error in
                error.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初始化result
                        let result: AFResult<Serializer.SerializedObject> = .failure(retryError.asAFError(orFailWith: "Received retryError was not already AFError"))
                        // 封装新的Response
                        let 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 = 50
            session = 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 data
    class 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 name
    class 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 function
    fileprivate func mapProperty(_ json: Any) {
        if json is NSNull { return }
        
        // mapping setting
        let 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] ?? key
            let jsonValue = (json as AnyObject).value(forKey: jsonKey)
            
            guard !ignorePropertyNames.contains(key) else {continue}  // ignore property
            guard let value = jsonValue else {continue}
            if value is NSNull {  // ignore null porperty
                debugPrint("Reflect error: The key \(jsonKey) value is \(value)")
                continue
            }

            setPropertyValue(value as AnyObject, forKey: key)
            // map sub object
            if let trackObject = mapSubObject(key, jsonKey: jsonKey, mappingObjectClass: mappingObjectClass, value: value as AnyObject){
                setValue(trackObject, forKey: key)
            }
            
            // map sub array
            if 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 type
        var transFlag: Bool?
        
        var transValue: AnyObject?
        let valueTuple = (self.value(forKey: key), value)
        switch valueTuple {
        case let (objValue as NSNumber, jsonValue as NSString):
            transFlag = false
            if objValue.isBool { // string -> bool
                if jsonValue == "true" {
                    transValue = true as AnyObject?
                    transFlag = true
                }
            } else { // string -> number
                if let res = NumberFormatter().number(from: jsonValue as String) {
                    transValue = res
                    transFlag = true
                }
            }
        case let (_ as NSString, jsonValue as NSNumber):
            transValue = "\(jsonValue)" as AnyObject?
            transFlag = true
        default:
            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 7
        var keys = [String]()
        var propNum: UInt32 = 0
        let 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! T
        return 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/" + url
        let p = params
        debugPrint(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"].stringValue
                        callback?(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.isFailure
                if 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: 默认post
    class 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 in
            if 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 = bean
                    print(beanModel?.result)
                    // 直接对原始数据使用JSONDecoder解析
                    // model继承codable
                    print(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 in
            self.model = model
            print(model)
        } failue: { error in
            print(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 in
            if 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/" + path
            AF.session.getAllTasks { sessionTask in
                sessionTask.forEach { task in
                    if task.currentRequest?.url?.path == urlpath {
                        task.cancel()
                    }
                }
            }
        }
    }

设置cookies

/// 设置所有的cookies
    class 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

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

下载

    /// 下载file
    /// - Parameters:
    ///   - file: 真实的url下载链接
    ///   - fileName: 下载后的名称
    func downLoadFile(file: String, fileName: String) {
        var fileStr = file
        if fileStr.isIncludeChinese() {
            fileStr = file.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? ""
        }
        var url = URL(string: fileStr)
        let destination: DownloadRequest.Destination = { _, response in
            let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
            let fileURL = documentsURL.appendingPathComponent("file1/\(fileName)")
            // 两个参数表示如果又同名文件则会覆盖,如果路径中文文件夹不存在则会自动创建
            url = fileURL
            return (fileURL,[.removePreviousFile,.createIntermediateDirectories])
        }
        AlamofireManager.shareInstance().download(fileStr,to: destination).downloadProgress { progress in
            print(progress)
        }.responseData { response in
            if 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\n
            case initial
            /// 中间:\r\n --分隔符\r\n
            case encapsulated
            /// 结束:\r\n --分隔符 -- \r\n
            case final
        }
/// 生成分隔符Data,拼接数据用
        static func boundaryData(forBoundaryType boundaryType: BoundaryType, boundary: String) -> Data {
            let boundaryText: String

            switch 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.Uploadable
        if multipartFormData.contentLength < encodingMemoryThreshold {
          // 把保存多表单的数据encode(编码)
            let data = try multipartFormData.encode()

            uploadable = .data(data)
        } else {
            let tempDirectoryURL = fileManager.temporaryDirectory
            let directoryURL = tempDirectoryURL.appendingPathComponent("org.alamofire.manager/multipart.form.data")
            let fileName = UUID().uuidString
            let 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.certificates
        let 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 in
                bundle.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
    }
    
    /// 获取公钥key
    public static func publicKeys(in bundle: Bundle = Bundle.main) -> [SecKey] {
        var publicKeys: [SecKey] = []
            
        // 把证书拿到
        for certificate in certitificates(in: bundle) {
            // ios 12以上 iOS10.3 SecCertificateCopyPublicKey
            if 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 = urlRequest
    if let token = TokenManager.shared.fetchAccessToken() {
      urlRequest.setValue("token \(token)", forHTTPHeaderField: "Authorization")
    }
    completion(.success(urlRequest))
  }

修改重试次数

let retryLimit = 5
let retryDelay: TimeInterval = 10
	func retry(
    _ request: Request,
    for session: Session,
    dueTo error: Error,
    completion: @escaping (RetryResult) -> Void
  ) {
    let response = request.task?.response as? HTTPURLResponse
    //Retry for 5xx status codes
    if
      let 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

缓存数据

/// 带缓存带重试器,事件监听器的Session
    class func GitInstance() ->  Session {
        let interceptor = GitRequestInterceptor()
        let configuration = URLSessionConfiguration.default
        let networkLogger = GitNetworkLogger()
        /// 请求timeout截止时间为50秒
        configuration.timeoutIntervalForRequest = 50
        /// 先修改数据,再缓存新的数据,参数为修改数据的闭包,改闭包返回可选的新缓存数据
        let responseCacher = ResponseCacher(behavior: .modify { _, response in
          let 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)

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2022-03-12 17:52:26  更:2022-03-12 17:52:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/18 0:44:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码