Alamofire3.4.1的學習過程

weixin_34050427發表於2016-07-12

Alamofire 是目前github上Swift版star最多的一個網路庫。網路幾乎是任何一個有實用價值的APP都繞不過的話題。Object-C寫網路庫,看AFNetworking是必須的;同樣的,用Swift寫網路庫,學Alamofire也是必須的。這兩者雖然是一個人寫的,不過由於語言的差異,兩者的實現思路差異很大。

Alamofire.swift

  • 是否對AFNetworking.h還有印象?只要包含這個標頭檔案,AF中的一切都隨便用。這個檔案的目的同樣也是為了方便使用,大多數情況下,只要看這個檔案,用這裡提供的函式,就能完成任務了。

  • 這個檔案只是一個透傳薄層,具體的事情由其他類完成。提供了“全域性函式”作為呼叫介面。由於是封裝成framework的,所以呼叫的時候以Alamofire.request()的樣式出現,看上去像類方法,但是實際上不是,而是“全域性函式”。在實際使用中,一般要import Alamofire,那麼上面的呼叫可以簡單地以request()出現,這就是“赤裸裸的全域性函式了”。這種處理方式,確實讓人腦洞大開,可以省去建立物件的步驟,也可以省去具體工作的類名。當然,為了體現“看上去的物件導向”,每個函式錢都可以帶上公共的Alamofire,也不用擔心“命名衝突”這個在Object-C中令人頭疼的問題。只要能夠克服,“對全域性函式的恐懼”,這種做法還是有一定的借鑑意義。當然,如果處理不好,還是老老實實地把屬性和方法包在一個類中,再讓別人呼叫比較好。

  • 對於普通的Https資料通訊,只要用下面這個函式介面就可以了

/**
    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
    )
}
  • 文件說明中的一個使用例子,看上去就是鏈式呼叫,一直“點”下去。能做到這樣,是因為request、validate、responseJSON等函式的返回值都是Request類,而他們本身又是這個類的成員函式。成員函式(比如validate)的返回值是自己所屬的類(比如Request),這種思路也是比較巧妙的,值得借鑑。
Alamofire.request(.GET, "https://httpbin.org/get") .validate() .responseJSON { response in debugPrint(response) }
  • request函式定義中有5個引數,但是例子中只提供了2個。原因是parameters、encoding、headers等引數提供了預設值。對於不常用的引數提供預設值,這也是值得學習的一種做法。

  • URLString引數的型別是URLStringConvertible,這是一個協議,並且通過擴充套件系統預設型別,提供了一些預設實現。這樣就實現了引數型別的多樣化,這種方式值得效仿。

// 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中的方便函式都是用的這個類的單例形態。使用靜態常量屬性的方式來做單例,比較簡單。利用{}()結構還能執行一些額外的程式碼。這個方式值得借鑑。
    /**
        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)
    }()
  • 建構函式,都提供了預設引數。NSURLSessionConfiguration使用了系統預設的,ServerTrustPolicyManager引數是nil。如果不需要修改超時時間,不需要使用自定義的證書檔案,那麼使用預設的單例就可以了,不需要用到這個建構函式。由於manager是單例,那麼NSURLSession就只有一個,整個資料通訊都用這一個,效果就相當於用了NSURLSession sharedSession單例一樣。這種處理方法是值得借鑑的。佔網路80%左右的資料通訊,用這種單例的方式就可以了。長時間的通訊,歸類到Upload或者download,這兩種就不要用單例了,用這個建構函式,建立manager的例項,自己在外部再寫一個管理類,進行統一處理。
    /**
        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)
    }
  • 資料業務主要是下面的函式完成的。由session和task建立一個自定義的Request物件(這個和NSURLRequest差異很大),來管理具體的事務。
    /**
        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的一個內部類,是一個代理,實現了NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate。這是一個比較大的類,資料、上傳、下載三項業務的代理實現都是它。這也是一個大的容器類,具體工作由Reques得內部類TaskDelegate來做。在實際使用中,最好不要這樣做,要求高了點,耦合度過大。

  • 一個Session可以包含多個Request,一個Request可以包含多個task。task的型別可以是資料、上傳、下載中的任意一種。

Request

  • 這是一個請求、響應、時間、進度等綜合事務管理的一個綜合體,功能很全面。也是實現函式鏈式呼叫的一箇中間體。

    // 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?
  • 從建構函式可以看出,task可以是資料、上傳、下載中的任意一種
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() }
    }
  • 任務的啟動,暫停,取消,是通過其包含的task來完成的。可以把Request看作是對task又包了一層的一個自定義類。不同的Request是用不同的task.taskIdentifier來區分的。

  • 代理TaskDelegate,以及3個子類都是內部類,做具體的代理工作。

Error.swift

  • 這是一個結構體struct

  • 封裝了自定義的domain和code

  • 通過提供靜態函式,構造NSError物件

Result.swift

  • 這是一個列舉enum,這個案例是enum的經典寫法,值得借鑑

  • 分為成功、失敗兩種case

  • 成功的資料value,失敗的error,以附屬變數的形式跟在相應的case後面

  • 資料和出錯資訊採用了泛型

  • 用一些計算屬性,方便判斷是否成功,獲取資料或者失敗資訊

  • 通過擴充套件,提供描述資訊

/**
    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

  • 這是一個結構體struct

  • 將NSURLRequest、NSHTTPURLResponse、資料、結果、時間等組合到在一起。相當於一個容器。

Notifications.swift

  • 這是一個結構體struct

  • 作用相當於與巨集定義 #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

  • 這是一個列舉enum

  • 輸入引數編碼方式

ResponseSerialization.swift

  • 這是返回結果處理的地方

  • 主要是對Request類的擴充套件;這種寫法在Swift中比較普遍;可以借鑑(檔名和類名毫不相關)

// 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類的功能擴充套件

  • 函式又返回Request類本身,實現鏈式呼叫

  • 驗證網路傳輸本身是否正常

// 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 多表單資料拼接後通過POST上傳

  • Download.swift下載

  • Upload.swift 上傳

  • NetworkReachabilityManager.swift檢查是否有網路

  • ServerTrustPolicy.swift 網路安全策略

  • Stream.swift流的方式

  • 基本上是通過擴充套件Manager和Request這兩個類完成相應功能

類圖

1186939-dae2275e52690c25.jpg
Alamofire.jpg

相關文章