美文网首页
【Alamofire源码解析】08 - Request

【Alamofire源码解析】08 - Request

作者: Lebron_James | 来源:发表于2017-12-21 22:46 被阅读79次

    这个文件里面主要定义了各种请求类型。

    1. RequestAdapter协议

    RequestAdapter协议允许SessionManagerRequest在创建的时候被适配。

    public protocol RequestAdapter {
        func adapt(_ urlRequest: URLRequest) throws -> URLRequest
    }
    

    举个例子,大家就知道RequestAdapter用来干嘛的了。例如我们在请求的时候需要把accessToken拼接到请求头:

    class AccessTokenAdapter: RequestAdapter {
        private let accessToken: String
    
        init(accessToken: String) {
            self.accessToken = accessToken
        }
    
        func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
            var urlRequest = urlRequest
    
            if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix("https://httpbin.org") {
                urlRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization")
            }
    
            return urlRequest
        }
    }
    
    // 使用
    let sessionManager = SessionManager()
    sessionManager.adapter = AccessTokenAdapter(accessToken: "1234")
    
    // 在创建请求的时候就会把accessToken加进去
    sessionManager.request("https://httpbin.org/get")
    

    2. 重试

    // 一个closure类型,给`RequestRetrier`判断是否需要重试
    public typealias RequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval) -> Void
    
    // 请求重试器协议
    public protocol RequestRetrier {
        // 决定请求是否需要重试
        func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion)
    }
    

    3. TaskConvertible协议

    这个协议的主要目的是,让遵循这个协议的类型,通过实现协议的方法来创建URLSessionTask

    protocol TaskConvertible {
        func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask
    }
    

    4. Request

    1)辅助类型

    // 监测上传或者下载进度的closure类型
    public typealias ProgressHandler = (Progress) -> Void
    
    // 列举了请求任务的类型,并携带了相关的关联值
    enum RequestTask {
        case data(TaskConvertible?, URLSessionTask?)
        case download(TaskConvertible?, URLSessionTask?)
        case upload(TaskConvertible?, URLSessionTask?)
        case stream(TaskConvertible?, URLSessionTask?)
    }
    

    2)属性

    // TaskDelegate,用于处理URLSessionTask的所有callback
    // internal(set): 这个指的是只能在内部赋值
    // get和set都加了锁,防止多线程同时get和set
    open internal(set) var delegate: TaskDelegate {
        get {
            taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
            return taskDelegate
        }
        set {
            taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
            taskDelegate = newValue
        }
    }
    
    open var task: URLSessionTask? { return delegate.task }
    open let session: URLSession
    open var request: URLRequest? { return task?.originalRequest }
    open var response: HTTPURLResponse? { return task?.response as? HTTPURLResponse }
    open internal(set) var retryCount: UInt = 0
    let originalTask: TaskConvertible?
    
    // 用于记录请求的时间
    var startTime: CFAbsoluteTime?
    var endTime: CFAbsoluteTime?
    
    var validations: [() -> Void] = []
    
    private var taskDelegate: TaskDelegate
    private var taskDelegateLock = NSLock()
    

    3)初始化方法

    init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
        self.session = session
    
        // 根据请求任务的类型,创建taskDelegate
        switch requestTask {
        case .data(let originalTask, let task):
            taskDelegate = DataTaskDelegate(task: task)
            self.originalTask = originalTask
        case .download(let originalTask, let task):
            taskDelegate = DownloadTaskDelegate(task: task)
            self.originalTask = originalTask
        case .upload(let originalTask, let task):
            taskDelegate = UploadTaskDelegate(task: task)
            self.originalTask = originalTask
        case .stream(let originalTask, let task):
            taskDelegate = TaskDelegate(task: task)
            self.originalTask = originalTask
        }
    
        delegate.error = error
        
        把请求结束的时间用队列记录下来,在请求完成后会执行这个队列
        delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
    }
    

    4)验证

    下面的前两个方法把Self作为返回值,方便我们把同一个类的两个方法链接起来调用,例如:

    let user = "user"
    let password = "password"
    
    Alamofire.request("https://httpbin.org/basic-auth/\(user)/\(password)")
        .authenticate(user: user, password: password)
        .responseJSON { response in
            debugPrint(response)
    }
    
    // 可以通过这个方法来关联一个HTTP基础证书
    @discardableResult
    open func authenticate(
        user: String,
        password: String,
        persistence: URLCredential.Persistence = .forSession)
        -> Self
    {
        let credential = URLCredential(user: user, password: password, persistence: persistence)
        return authenticate(usingCredential: credential)
    }
    
    // 可以通过这个方法来关联一个指定的证书
    @discardableResult
    open func authenticate(usingCredential credential: URLCredential) -> Self {
        delegate.credential = credential
        return self
    }
    
    // 返回一个base64编码的验证头
    open static func authorizationHeader(user: String, password: String) -> (key: String, value: String)? {
        guard let data = "\(user):\(password)".data(using: .utf8) else { return nil }
    
        let credential = data.base64EncodedString(options: [])
    
        return (key: "Authorization", value: "Basic \(credential)")
    }
    

    5)状态

    /// 开始请求
    open func resume() {
        guard let task = task else { delegate.queue.isSuspended = false ; return }
    
        if startTime == nil { startTime = CFAbsoluteTimeGetCurrent() }
    
        task.resume()
    
        NotificationCenter.default.post(
            name: Notification.Name.Task.DidResume,
            object: self,
            userInfo: [Notification.Key.Task: task]
        )
    }
    
    /// 暂停请求
    open func suspend() {
        guard let task = task else { return }
    
        task.suspend()
    
        NotificationCenter.default.post(
            name: Notification.Name.Task.DidSuspend,
            object: self,
            userInfo: [Notification.Key.Task: task]
        )
    }
    
    /// 取消请求
    open func cancel() {
        guard let task = task else { return }
    
        task.cancel()
    
        NotificationCenter.default.post(
            name: Notification.Name.Task.DidCancel,
            object: self,
            userInfo: [Notification.Key.Task: task]
        )
    }
    

    另外还实现了CustomStringConvertibleCustomDebugStringConvertible,方便调试使用。

    5. DataRequest

    继承于Request

    1) Requestable

    内置了Requestable,并实现TaskConvertible协议。目的是用urlRequest创建一个dataTask。

    struct Requestable: TaskConvertible {
        let urlRequest: URLRequest
    
        func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
            do {
                let urlRequest = try self.urlRequest.adapt(using: adapter)
                return queue.sync { session.dataTask(with: urlRequest) }
            } catch {
                throw AdaptError(error: error)
            }
        }
    }
    

    2) 属性

    // DataRequest对应的请求
    open override var request: URLRequest? {
        if let request = super.request { return request }
        if let requestable = originalTask as? Requestable { return requestable.urlRequest }
    
        return nil
    }
    
    // 从服务器获取数据的进度
    open var progress: Progress { return dataDelegate.progress }
    
    // DataTask对应的delegate,负责处理与data task相关的所有回调。在父类的初始化方法已经初始化过,这里直接返回,并且强转为DataTaskDelegate类型
    // 在使用强转的时候要非常小心,如果不能确定能强转成功的,请务必使用`if let`,否则如果强转不成功,程序crash
    var dataDelegate: DataTaskDelegate { return delegate as! DataTaskDelegate }
    
    // 调用这个方法,设置一个closure,这个closure会在数据从服务器返回过程中周期性的调用;
    // closure里面的data只包含最近从服务器获取的数据,不包含之前获取的数据;
    // 如果调用了这个方法设置closure,那么下载的数据只能从这里访问,不会在其他地方存储,
    // 并且`Response`对象中的data为`nil`
    @discardableResult
    open func stream(closure: ((Data) -> Void)? = nil) -> Self {
        dataDelegate.dataStream = closure
        return self
    }
    
    // 调用这个方法,设置一个closure,这个closure会在数据从服务器返回过程中周期性的调用,用于跟踪下载进度
    @discardableResult
    open func downloadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
        dataDelegate.progressHandler = (closure, queue)
        return self
    }
    

    6. DownloadRequest

    继承于Request

    1) DownloadOptions

    是一个OptionSet类型,用于指定一些下载选项:

    public struct DownloadOptions: OptionSet {
        public let rawValue: UInt
    
        // 创建中间目录
        public static let createIntermediateDirectories = DownloadOptions(rawValue: 1 << 0)
    
        // 删除之前的文件
        public static let removePreviousFile = DownloadOptions(rawValue: 1 << 1)
    
        public init(rawValue: UInt) {
            self.rawValue = rawValue
        }
    }
    

    2) DownloadFileDestination

    下载完成后,会执行这个closure,程序会把下载完成后把文件临时存放在temporaryURL,然后再移动到destinationURL

    public typealias DownloadFileDestination = (
        _ temporaryURL: URL,
        _ response: HTTPURLResponse)
        -> (destinationURL: URL, options: DownloadOptions)
    

    3) Downloadable

    内置了Downloadable,并实现TaskConvertible协议。目的是用urlRequest创建一个downloadTask。创建下载任务的时候有两种情况:1)全新的下载;2)之前下载好了部分数据,接着继续下载。

    enum Downloadable: TaskConvertible {
        case request(URLRequest)
        case resumeData(Data)
    
        func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
            do {
                let task: URLSessionTask
    
                switch self {
                case let .request(urlRequest):
                    let urlRequest = try urlRequest.adapt(using: adapter)
                    task = queue.sync { session.downloadTask(with: urlRequest) }
                case let .resumeData(resumeData):
                    task = queue.sync { session.downloadTask(withResumeData: resumeData) }
                }
    
                return task
            } catch {
                throw AdaptError(error: error)
            }
        }
    }
    

    4) 属性

    // DownloadRequest对应的请求
    open override var request: URLRequest? {
        if let request = super.request { return request }
    
        if let downloadable = originalTask as? Downloadable, case let .request(urlRequest) = downloadable {
            return urlRequest
        }
    
        return nil
    }
    
    // 已经下载好的数据,用于继续下载
    open var resumeData: Data? { return downloadDelegate.resumeData }
    
    // 下载进度
    open var progress: Progress { return downloadDelegate.progress }
    
    // // DownloadTask对应的delegate,负责处理URLSessionDownloadDelegate的所有回调。在父类的初始化方法已经初始化过,这里直接返回,并且强转为DownloadTaskDelegate类型
    var downloadDelegate: DownloadTaskDelegate { return delegate as! DownloadTaskDelegate }
    

    5) 方法

    
    /// 重写父类的cancel方法
    open override func cancel() {
      // 取消下载的时候要把resumeData记录起来,方便后续继续下载
        downloadDelegate.downloadTask.cancel { self.downloadDelegate.resumeData = $0 }
    
        NotificationCenter.default.post(
            name: Notification.Name.Task.DidCancel,
            object: self,
            userInfo: [Notification.Key.Task: task as Any]
        )
    }
    
    // 调用这个方法,设置一个closure,这个closure会在数据从服务器返回过程中周期性的调用,用于跟踪下载进度
    @discardableResult
    open func downloadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
        downloadDelegate.progressHandler = (closure, queue)
        return self
    }
    
    // 提供一个建议的DownloadFileDestination,最终会把下载好的文件移动到用户的documents目录
    open class func suggestedDownloadDestination(
        for directory: FileManager.SearchPathDirectory = .documentDirectory,
        in domain: FileManager.SearchPathDomainMask = .userDomainMask)
        -> DownloadFileDestination
    {
        return { temporaryURL, response in
            let directoryURLs = FileManager.default.urls(for: directory, in: domain)
    
            if !directoryURLs.isEmpty {
                return (directoryURLs[0].appendingPathComponent(response.suggestedFilename!), [])
            }
    
            return (temporaryURL, [])
        }
    }
    

    7. UploadRequest

    继承于DataRequest

    1) Uploadable

    内置了Uploadable,并实现TaskConvertible协议。目的是用urlRequest创建一个uploadTask。创建上传任务的时候有三种情况:1)上传数据;2)上传文件;3)上传流。

    enum Uploadable: TaskConvertible {
        case data(Data, URLRequest)
        case file(URL, URLRequest)
        case stream(InputStream, URLRequest)
        
        func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
            do {
                let task: URLSessionTask
                
                switch self {
                case let .data(data, urlRequest):
                    let urlRequest = try urlRequest.adapt(using: adapter)
                    task = queue.sync { session.uploadTask(with: urlRequest, from: data) }
                case let .file(url, urlRequest):
                    let urlRequest = try urlRequest.adapt(using: adapter)
                    task = queue.sync { session.uploadTask(with: urlRequest, fromFile: url) }
                case let .stream(_, urlRequest):
                    let urlRequest = try urlRequest.adapt(using: adapter)
                    task = queue.sync { session.uploadTask(withStreamedRequest: urlRequest) }
                }
                
                return task
            } catch {
                throw AdaptError(error: error)
            }
        }
    }
    

    2) 属性

    // UploadRequest对应的请求
    open override var request: URLRequest? {
        if let request = super.request { return request }
        
        guard let uploadable = originalTask as? Uploadable else { return nil }
        
        switch uploadable {
        case .data(_, let urlRequest), .file(_, let urlRequest), .stream(_, let urlRequest):
            return urlRequest
        }
    }
    
    // 上传进度
    open var uploadProgress: Progress { return uploadDelegate.uploadProgress }
    
    // // UploadTask对应的delegate,负责处理与上传相关的回调。在父类的初始化方法已经初始化过,这里直接返回,并且强转为UploadTaskDelegate类型
    var uploadDelegate: UploadTaskDelegate { return delegate as! UploadTaskDelegate }
    
    // 调用这个方法,设置一个closure,这个closure会在数据上传过程中周期性的调用,用于跟踪上传进度
    @discardableResult
    open func uploadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
        uploadDelegate.uploadProgressHandler = (closure, queue)
        return self
    }
    

    8. StreamRequest

    继承于Request,只能在iOS、macOS和tvOS使用。同样地,内置了Streamable,并实现TaskConvertible协议。目的是用host和端口或者NetService创建一个streamTask。

    @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
    open class StreamRequest: Request {
        enum Streamable: TaskConvertible {
            case stream(hostName: String, port: Int)
            case netService(NetService)
            
            func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
                let task: URLSessionTask
                
                switch self {
                case let .stream(hostName, port):
                    task = queue.sync { session.streamTask(withHostName: hostName, port: port) }
                case let .netService(netService):
                    task = queue.sync { session.streamTask(with: netService) }
                }
                
                return task
            }
    }
    

    有任何问题,欢迎大家留言!

    欢迎加入我管理的Swift开发群:536353151,本群只讨论Swift相关内容。

    原创文章,转载请注明出处。谢谢!

    相关文章

      网友评论

          本文标题:【Alamofire源码解析】08 - Request

          本文链接:https://www.haomeiwen.com/subject/itmzwxtx.html