Alamofire3.4.1的學(xué)習(xí)過程

Alamofire 是目前github上Swift版star最多的一個(gè)網(wǎng)絡(luò)庫。網(wǎng)絡(luò)幾乎是任何一個(gè)有實(shí)用價(jià)值的APP都繞不過的話題。Object-C寫網(wǎng)絡(luò)庫,看AFNetworking是必須的;同樣的,用Swift寫網(wǎng)絡(luò)庫,學(xué)Alamofire也是必須的。這兩者雖然是一個(gè)人寫的,不過由于語言的差異,兩者的實(shí)現(xiàn)思路差異很大。

Alamofire.swift

  • 是否對AFNetworking.h還有印象?只要包含這個(gè)頭文件,AF中的一切都隨便用。這個(gè)文件的目的同樣也是為了方便使用,大多數(shù)情況下,只要看這個(gè)文件,用這里提供的函數(shù),就能完成任務(wù)了。

  • 這個(gè)文件只是一個(gè)透傳薄層,具體的事情由其他類完成。提供了“全局函數(shù)”作為調(diào)用接口。由于是封裝成framework的,所以調(diào)用的時(shí)候以Alamofire.request()的樣式出現(xiàn),看上去像類方法,但是實(shí)際上不是,而是“全局函數(shù)”。在實(shí)際使用中,一般要import Alamofire,那么上面的調(diào)用可以簡單地以request()出現(xiàn),這就是“赤裸裸的全局函數(shù)了”。這種處理方式,確實(shí)讓人腦洞大開,可以省去創(chuàng)建對象的步驟,也可以省去具體工作的類名。當(dāng)然,為了體現(xiàn)“看上去的面向?qū)ο蟆保總€(gè)函數(shù)錢都可以帶上公共的Alamofire,也不用擔(dān)心“命名沖突”這個(gè)在Object-C中令人頭疼的問題。只要能夠克服,“對全局函數(shù)的恐懼”,這種做法還是有一定的借鑒意義。當(dāng)然,如果處理不好,還是老老實(shí)實(shí)地把屬性和方法包在一個(gè)類中,再讓別人調(diào)用比較好。

  • 對于普通的Https數(shù)據(jù)通訊,只要用下面這個(gè)函數(shù)接口就可以了

/**
    Creates a request using the shared manager instance for the specified method, URL string, parameters, and
    parameter encoding.

    - parameter method:     The HTTP method.
    - parameter URLString:  The URL string.
    - parameter parameters: The parameters. `nil` by default.
    - parameter encoding:   The parameter encoding. `.URL` by default.
    - parameter headers:    The HTTP headers. `nil` by default.

    - returns: The created request.
*/
public func request(
    method: Method,
    _ URLString: URLStringConvertible,
    parameters: [String: AnyObject]? = nil,
    encoding: ParameterEncoding = .URL,
    headers: [String: String]? = nil)
    -> Request
{
    return Manager.sharedInstance.request(
        method,
        URLString,
        parameters: parameters,
        encoding: encoding,
        headers: headers
    )
}
  • 文檔說明中的一個(gè)使用例子,看上去就是鏈?zhǔn)秸{(diào)用,一直“點(diǎn)”下去。能做到這樣,是因?yàn)閞equest、validate、responseJSON等函數(shù)的返回值都是Request類,而他們本身又是這個(gè)類的成員函數(shù)。成員函數(shù)(比如validate)的返回值是自己所屬的類(比如Request),這種思路也是比較巧妙的,值得借鑒。
Alamofire.request(.GET, "https://httpbin.org/get") .validate() .responseJSON { response in debugPrint(response) }
  • request函數(shù)定義中有5個(gè)參數(shù),但是例子中只提供了2個(gè)。原因是parameters、encoding、headers等參數(shù)提供了默認(rèn)值。對于不常用的參數(shù)提供默認(rèn)值,這也是值得學(xué)習(xí)的一種做法。

  • URLString參數(shù)的類型是URLStringConvertible,這是一個(gè)協(xié)議,并且通過擴(kuò)展系統(tǒng)默認(rèn)類型,提供了一些默認(rèn)實(shí)現(xiàn)。這樣就實(shí)現(xiàn)了參數(shù)類型的多樣化,這種方式值得效仿。

// MARK: - URLStringConvertible

/**
    Types adopting the `URLStringConvertible` protocol can be used to construct URL strings, which are then used to 
    construct URL requests.
*/
public protocol URLStringConvertible {
    /**
        A URL that conforms to RFC 2396.

        Methods accepting a `URLStringConvertible` type parameter parse it according to RFCs 1738 and 1808.

        See https://tools.ietf.org/html/rfc2396
        See https://tools.ietf.org/html/rfc1738
        See https://tools.ietf.org/html/rfc1808
    */
    var URLString: String { get }
}

extension String: URLStringConvertible {
    public var URLString: String {
        return self
    }
}

extension NSURL: URLStringConvertible {
    public var URLString: String {
        return absoluteString
    }
}

extension NSURLComponents: URLStringConvertible {
    public var URLString: String {
        return URL!.URLString
    }
}

extension NSURLRequest: URLStringConvertible {
    public var URLString: String {
        return URL!.URLString
    }
}

Manager.swift

  • 頂層管理類,提供單例模式。Alamofire.swift中的方便函數(shù)都是用的這個(gè)類的單例形態(tài)。使用靜態(tài)常量屬性的方式來做單例,比較簡單。利用{}()結(jié)構(gòu)還能執(zhí)行一些額外的代碼。這個(gè)方式值得借鑒。
    /**
        A shared instance of `Manager`, used by top-level Alamofire request methods, and suitable for use directly 
        for any ad hoc requests.
    */
    public static let sharedInstance: Manager = {
        let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
        configuration.HTTPAdditionalHeaders = Manager.defaultHTTPHeaders

        return Manager(configuration: configuration)
    }()
  • 構(gòu)造函數(shù),都提供了默認(rèn)參數(shù)。NSURLSessionConfiguration使用了系統(tǒng)默認(rèn)的,ServerTrustPolicyManager參數(shù)是nil。如果不需要修改超時(shí)時(shí)間,不需要使用自定義的證書文件,那么使用默認(rèn)的單例就可以了,不需要用到這個(gè)構(gòu)造函數(shù)。由于manager是單例,那么NSURLSession就只有一個(gè),整個(gè)數(shù)據(jù)通訊都用這一個(gè),效果就相當(dāng)于用了NSURLSession sharedSession單例一樣。這種處理方法是值得借鑒的。占網(wǎng)絡(luò)80%左右的數(shù)據(jù)通訊,用這種單例的方式就可以了。長時(shí)間的通訊,歸類到Upload或者download,這兩種就不要用單例了,用這個(gè)構(gòu)造函數(shù),創(chuàng)建manager的實(shí)例,自己在外部再寫一個(gè)管理類,進(jìn)行統(tǒng)一處理。
    /**
        Initializes the `Manager` instance with the specified configuration, delegate and server trust policy.

        - parameter configuration:            The configuration used to construct the managed session. 
                                              `NSURLSessionConfiguration.defaultSessionConfiguration()` by default.
        - parameter delegate:                 The delegate used when initializing the session. `SessionDelegate()` by
                                              default.
        - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust 
                                              challenges. `nil` by default.

        - returns: The new `Manager` instance.
    */
    public init(
        configuration: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration(),
        delegate: SessionDelegate = SessionDelegate(),
        serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
    {
        self.delegate = delegate
        self.session = NSURLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)

        commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
    }
  • 數(shù)據(jù)業(yè)務(wù)主要是下面的函數(shù)完成的。由session和task創(chuàng)建一個(gè)自定義的Request對象(這個(gè)和NSURLRequest差異很大),來管理具體的事務(wù)。
    /**
        Creates a request for the specified method, URL string, parameters, parameter encoding and headers.

        - parameter method:     The HTTP method.
        - parameter URLString:  The URL string.
        - parameter parameters: The parameters. `nil` by default.
        - parameter encoding:   The parameter encoding. `.URL` by default.
        - parameter headers:    The HTTP headers. `nil` by default.

        - returns: The created request.
    */
    public func request(
        method: Method,
        _ URLString: URLStringConvertible,
        parameters: [String: AnyObject]? = nil,
        encoding: ParameterEncoding = .URL,
        headers: [String: String]? = nil)
        -> Request
    {
        let mutableURLRequest = URLRequest(method, URLString, headers: headers)
        let encodedURLRequest = encoding.encode(mutableURLRequest, parameters: parameters).0
        return request(encodedURLRequest)
    }

    /**
        Creates a request for the specified URL request.

        If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.

        - parameter URLRequest: The URL request

        - returns: The created request.
    */
    public func request(URLRequest: URLRequestConvertible) -> Request {
        var dataTask: NSURLSessionDataTask!
        dispatch_sync(queue) { dataTask = self.session.dataTaskWithRequest(URLRequest.URLRequest) }

        let request = Request(session: session, task: dataTask)
        delegate[request.delegate.task] = request.delegate

        if startRequestsImmediately {
            request.resume()
        }

        return request
    }
  • SessionDelegate是Manager的一個(gè)內(nèi)部類,是一個(gè)代理,實(shí)現(xiàn)了NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate。這是一個(gè)比較大的類,數(shù)據(jù)、上傳、下載三項(xiàng)業(yè)務(wù)的代理實(shí)現(xiàn)都是它。這也是一個(gè)大的容器類,具體工作由Reques得內(nèi)部類TaskDelegate來做。在實(shí)際使用中,最好不要這樣做,要求高了點(diǎn),耦合度過大。

  • 一個(gè)Session可以包含多個(gè)Request,一個(gè)Request可以包含多個(gè)task。task的類型可以是數(shù)據(jù)、上傳、下載中的任意一種。

Request

  • 這是一個(gè)請求、響應(yīng)、時(shí)間、進(jìn)度等綜合事務(wù)管理的一個(gè)綜合體,功能很全面。也是實(shí)現(xiàn)函數(shù)鏈?zhǔn)秸{(diào)用的一個(gè)中間體。

    // MARK: - Properties

    /// The delegate for the underlying task.
    public let delegate: TaskDelegate

    /// The underlying task.
    public var task: NSURLSessionTask { return delegate.task }

    /// The session belonging to the underlying task.
    public let session: NSURLSession

    /// The request sent or to be sent to the server.
    public var request: NSURLRequest? { return task.originalRequest }

    /// The response received from the server, if any.
    public var response: NSHTTPURLResponse? { return task.response as? NSHTTPURLResponse }

    /// The progress of the request lifecycle.
    public var progress: NSProgress { return delegate.progress }

    var startTime: CFAbsoluteTime?
    var endTime: CFAbsoluteTime?
  • 從構(gòu)造函數(shù)可以看出,task可以是數(shù)據(jù)、上傳、下載中的任意一種
init(session: NSURLSession, task: NSURLSessionTask) {
        self.session = session

        switch task {
        case is NSURLSessionUploadTask:
            delegate = UploadTaskDelegate(task: task)
        case is NSURLSessionDataTask:
            delegate = DataTaskDelegate(task: task)
        case is NSURLSessionDownloadTask:
            delegate = DownloadTaskDelegate(task: task)
        default:
            delegate = TaskDelegate(task: task)
        }

        delegate.queue.addOperationWithBlock { self.endTime = CFAbsoluteTimeGetCurrent() }
    }
  • 任務(wù)的啟動(dòng),暫停,取消,是通過其包含的task來完成的。可以把Request看作是對task又包了一層的一個(gè)自定義類。不同的Request是用不同的task.taskIdentifier來區(qū)分的。

  • 代理TaskDelegate,以及3個(gè)子類都是內(nèi)部類,做具體的代理工作。

Error.swift

  • 這是一個(gè)結(jié)構(gòu)體struct

  • 封裝了自定義的domain和code

  • 通過提供靜態(tài)函數(shù),構(gòu)造NSError對象

Result.swift

  • 這是一個(gè)枚舉enum,這個(gè)案例是enum的經(jīng)典寫法,值得借鑒

  • 分為成功、失敗兩種case

  • 成功的數(shù)據(jù)value,失敗的error,以附屬變量的形式跟在相應(yīng)的case后面

  • 數(shù)據(jù)和出錯(cuò)信息采用了泛型

  • 用一些計(jì)算屬性,方便判斷是否成功,獲取數(shù)據(jù)或者失敗信息

  • 通過擴(kuò)展,提供描述信息

/**
    Used to represent whether a request was successful or encountered an error.

    - Success: The request and all post processing operations were successful resulting in the serialization of the 
               provided associated value.
    - Failure: The request encountered an error resulting in a failure. The associated values are the original data 
               provided by the server as well as the error that caused the failure.
*/
public enum Result<Value, Error: ErrorType> {
    case Success(Value)
    case Failure(Error)

    /// Returns `true` if the result is a success, `false` otherwise.
    public var isSuccess: Bool {
        switch self {
        case .Success:
            return true
        case .Failure:
            return false
        }
    }

    /// Returns `true` if the result is a failure, `false` otherwise.
    public var isFailure: Bool {
        return !isSuccess
    }

    /// Returns the associated value if the result is a success, `nil` otherwise.
    public var value: Value? {
        switch self {
        case .Success(let value):
            return value
        case .Failure:
            return nil
        }
    }

    /// Returns the associated error value if the result is a failure, `nil` otherwise.
    public var error: Error? {
        switch self {
        case .Success:
            return nil
        case .Failure(let error):
            return error
        }
    }
}

// MARK: - CustomStringConvertible

extension Result: CustomStringConvertible {
    /// The textual representation used when written to an output stream, which includes whether the result was a 
    /// success or failure.
    public var description: String {
        switch self {
        case .Success:
            return "SUCCESS"
        case .Failure:
            return "FAILURE"
        }
    }
}

// MARK: - CustomDebugStringConvertible

extension Result: CustomDebugStringConvertible {
    /// The debug textual representation used when written to an output stream, which includes whether the result was a
    /// success or failure in addition to the value or error.
    public var debugDescription: String {
        switch self {
        case .Success(let value):
            return "SUCCESS: \(value)"
        case .Failure(let error):
            return "FAILURE: \(error)"
        }
    }
}

Response.swift

  • 這是一個(gè)結(jié)構(gòu)體struct

  • 將NSURLRequest、NSHTTPURLResponse、數(shù)據(jù)、結(jié)果、時(shí)間等組合到在一起。相當(dāng)于一個(gè)容器。

Notifications.swift

  • 這是一個(gè)結(jié)構(gòu)體struct

  • 作用相當(dāng)于與宏定義 #define,比宏方便好用。這種方式值得借鑒

/// Contains all the `NSNotification` names posted by Alamofire with descriptions of each notification's payload.
public struct Notifications {
    /// Used as a namespace for all `NSURLSessionTask` related notifications.
    public struct Task {
        /// Notification posted when an `NSURLSessionTask` is resumed. The notification `object` contains the resumed
        /// `NSURLSessionTask`.
        public static let DidResume = "com.alamofire.notifications.task.didResume"

        /// Notification posted when an `NSURLSessionTask` is suspended. The notification `object` contains the 
        /// suspended `NSURLSessionTask`.
        public static let DidSuspend = "com.alamofire.notifications.task.didSuspend"

        /// Notification posted when an `NSURLSessionTask` is cancelled. The notification `object` contains the
        /// cancelled `NSURLSessionTask`.
        public static let DidCancel = "com.alamofire.notifications.task.didCancel"

        /// Notification posted when an `NSURLSessionTask` is completed. The notification `object` contains the
        /// completed `NSURLSessionTask`.
        public static let DidComplete = "com.alamofire.notifications.task.didComplete"
    }
}

ParameterEncoding.swift

  • 這是一個(gè)枚舉enum

  • 輸入?yún)?shù)編碼方式

ResponseSerialization.swift

  • 這是返回結(jié)果處理的地方

  • 主要是對Request類的擴(kuò)展;這種寫法在Swift中比較普遍;可以借鑒(文件名和類名毫不相關(guān))

// MARK: - JSON

extension Request {

    /**
        Creates a response serializer that returns a JSON object constructed from the response data using 
        `NSJSONSerialization` with the specified reading options.

        - parameter options: The JSON serialization reading options. `.AllowFragments` by default.

        - returns: A JSON object response serializer.
    */
    public static func JSONResponseSerializer(
        options options: NSJSONReadingOptions = .AllowFragments)
        -> ResponseSerializer<AnyObject, NSError>
    {
        return ResponseSerializer { _, response, data, error in
            guard error == nil else { return .Failure(error!) }

            if let response = response where response.statusCode == 204 { return .Success(NSNull()) }

            guard let validData = data where validData.length > 0 else {
                let failureReason = "JSON could not be serialized. Input data was nil or zero length."
                let error = Error.error(code: .JSONSerializationFailed, failureReason: failureReason)
                return .Failure(error)
            }

            do {
                let JSON = try NSJSONSerialization.JSONObjectWithData(validData, options: options)
                return .Success(JSON)
            } catch {
                return .Failure(error as NSError)
            }
        }
    }

    /**
        Adds a handler to be called once the request has finished.

        - parameter options:           The JSON serialization reading options. `.AllowFragments` by default.
        - parameter completionHandler: A closure to be executed once the request has finished.

        - returns: The request.
    */
    public func responseJSON(
        queue queue: dispatch_queue_t? = nil,
        options: NSJSONReadingOptions = .AllowFragments,
        completionHandler: Response<AnyObject, NSError> -> Void)
        -> Self
    {
        return response(
            queue: queue,
            responseSerializer: Request.JSONResponseSerializer(options: options),
            completionHandler: completionHandler
        )
    }
}

Validation.swift

  • 對Request類的功能擴(kuò)展

  • 函數(shù)又返回Request類本身,實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用

  • 驗(yàn)證網(wǎng)絡(luò)傳輸本身是否正常

// MARK: - Automatic

    /**
        Validates that the response has a status code in the default acceptable range of 200...299, and that the content 
        type matches any specified in the Accept HTTP header field.

        If validation fails, subsequent calls to response handlers will have an associated error.

        - returns: The request.
    */
    public func validate() -> Self {
        let acceptableStatusCodes: Range<Int> = 200..<300
        let acceptableContentTypes: [String] = {
            if let accept = request?.valueForHTTPHeaderField("Accept") {
                return accept.componentsSeparatedByString(",")
            }

            return ["*/*"]
        }()

        return validate(statusCode: acceptableStatusCodes).validate(contentType: acceptableContentTypes)
    }

其他文件

  • MultipartFormData.swift 多表單數(shù)據(jù)拼接后通過POST上傳

  • Download.swift下載

  • Upload.swift 上傳

  • NetworkReachabilityManager.swift檢查是否有網(wǎng)絡(luò)

  • ServerTrustPolicy.swift 網(wǎng)絡(luò)安全策略

  • Stream.swift流的方式

  • 基本上是通過擴(kuò)展Manager和Request這兩個(gè)類完成相應(yīng)功能

類圖

Alamofire.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,318評論 4 61
  • “眼前的食物,可能來自遙遠(yuǎn)的高山和大海。” 在東北,3月的夜晚,零下15攝氏度。這是制作凍豆腐最適宜的溫度,低溫讓...
    好書君閱讀 1,064評論 0 3
  • 少年時(shí)代饑不擇食的閱讀造成了現(xiàn)在的我。 我眼睛暗淡,思想茫然,與瞎子有關(guān)。 必須清算,帶著批判性的清算。 我可以成...
    文森林木閱讀 259評論 0 0

友情鏈接更多精彩內(nèi)容