Alamofire(4.0.0是基于Swift3.0語法)是Swift中最常用的網(wǎng)絡(luò)框架载荔。
1. AFError
Swift中的枚舉比較特殊和敬,可以在枚舉中定義方法凹炸,但是不能定義變量。
enum CompassPoint{
case North
case Sourth
case East
case West
//枚舉中 可以定義方法
func show(){
print(self)
}
}
// 定義枚舉變量
var p = CompassPoint.North
// 類型標(biāo)注之后 可以使用點來獲取枚舉值
var p2 : CompassPoint = .Sourth
p.show()
p2.show()
在Alamofire中的枚舉更加有個性昼弟,可以在枚舉中定義枚舉啤它,并且枚舉的case可以傳遞參數(shù)。異常類型可以直接通過throw拋出舱痘。
public enum AFError: Error {
public enum ParameterEncodingFailureReason {
case missingURL
case jsonEncodingFailed(error: Error)
case propertyListEncodingFailed(error: Error)
}
public enum MultipartEncodingFailureReason {
...
}
public enum ResponseValidationFailureReason {
...
}
public enum ResponseSerializationFailureReason {
...
}
case invalidURL(url: URLConvertible)
case parameterEncodingFailed(reason: ParameterEncodingFailureReason)
case multipartEncodingFailed(reason: MultipartEncodingFailureReason)
case responseValidationFailed(reason: ResponseValidationFailureReason)
case responseSerializationFailed(reason: ResponseSerializationFailureReason)
}
Swift中的枚舉非常強(qiáng)大变骡,可以對枚舉進(jìn)行擴(kuò)展,在擴(kuò)展中可以定義屬性芭逝。如下代碼所示塌碌,Alamofire中可以在AFError的擴(kuò)展中定義方法來判斷當(dāng)前的錯誤屬于哪一類錯誤。
extension AFError {
public var isInvalidURLError: Bool {
if case .invalidURL = self { return true }
return false
}
......
public var isResponseSerializationError: Bool {
if case .responseSerializationFailed = self { return true }
return false
}
}
由于擴(kuò)展可以定義多個旬盯,所以便可以將一類功能歸類到同一個擴(kuò)展中台妆,如Alamofire中定義了一個便捷屬性擴(kuò)展翎猛。
// MARK: - Convenience Properties
extension AFError {
public var urlConvertible: URLConvertible? {
switch self {
case .invalidURL(let url):
return url
default:
return nil
}
}
......
public var failedStringEncoding: String.Encoding? {
switch self {
case .responseSerializationFailed(let reason):
return reason.failedStringEncoding
default:
return nil
}
}
}
Alamofire中枚舉使用的最牛逼的就是為AFError內(nèi)部枚舉添加相應(yīng)擴(kuò)展。
extension AFError.ParameterEncodingFailureReason {
var underlyingError: Error? {
switch self {
case .jsonEncodingFailed(let error), .propertyListEncodingFailed(let error):
return error
default:
return nil
}
}
}
......
然后最上層直接調(diào)用underlyingError得到error
- Summary
1.枚舉添加訪問修飾符接剩,并且可以實現(xiàn)協(xié)議办成。比如。public enum AFError: Error搂漠。這里的Error其實是一個協(xié)議public protocol Error迂卢。
2.枚舉內(nèi)部可以再定義枚舉。相當(dāng)于聲明枚舉桐汤,后面還是通過case的方式使用而克。并且可以傳遞參數(shù)。
3.通過擴(kuò)展給枚舉挺添加便捷屬性怔毛。
4.按照不同功能給擴(kuò)展分組员萍。讓代碼更便于閱讀。
2. Notifications
定義的通知是一件比較簡單的事情拣度。一般情況下碎绎,在OC中我們會直接定義一個字符串來表示某種通知。通常情況下也沒怎么把通知管理起來抗果。
在Alamofire中定義的通知就感覺很正式了筋帖。首先是成了一個擴(kuò)展的形式,把相關(guān)的通知都寫在里面冤馏。然后使用結(jié)構(gòu)體來包裝通知日麸。代碼如下:
extension Notification.Name {
public struct Task {
public static let DidResume = Notification.Name(rawValue: "org.alamofire.notification.name.task.didResume")
public static let DidSuspend = Notification.Name(rawValue: "org.alamofire.notification.name.task.didSuspend")
public static let DidCancel = Notification.Name(rawValue: "org.alamofire.notification.name.task.didCancel")
public static let DidComplete = Notification.Name(rawValue: "org.alamofire.notification.name.task.didComplete")
}
}
- 注意定義的都是靜態(tài)常量
相當(dāng)于擴(kuò)展了系統(tǒng)通知name。把通知名稱都定義在里面逮光。然后通過不同的結(jié)構(gòu)體定義不同用途的通知代箭。其實在OC中也可以這樣做。只是平時很少這樣寫涕刚,這樣寫之后代碼組織就更加優(yōu)雅了嗡综。
Swift中Notification的API如下:
public struct Notification : ReferenceConvertible, Equatable, Hashable {
public typealias ReferenceType = NSNotification
public var name: Notification.Name
public var object: Any?
public var userInfo: [AnyHashable : Any]?
public init(name: Notification.Name, object: Any? = default, userInfo: [AnyHashable : Any]? = default)
public var hashValue: Int { get }
public var description: String { get }
public var debugDescription: String { get }
public typealias Name = NSNotification.Name
public static func ==(lhs: Notification, rhs: Notification) -> Bool
}
- Summary :
1.通過擴(kuò)展Notification.Name來定義通知名稱。讓代碼組織更加優(yōu)雅杜漠。
2.使用結(jié)構(gòu)體來區(qū)分不同功能的通知极景。在結(jié)構(gòu)體下定義靜態(tài)常量定義通知名稱。
3. ParameterEncoding
詳細(xì)代碼見ParameterEncoding.swift類碑幅。
(1)枚舉繼承的類就是case所對應(yīng)的類型戴陡。比如
public enum HTTPMethod: String {
case options = "OPTIONS"
case get = "GET"
case head = "HEAD"
case post = "POST"
case put = "PUT"
case patch = "PATCH"
case delete = "DELETE"
case trace = "TRACE"
case connect = "CONNECT"
}
(2)typealias可以給一個類型取一個別名
public typealias Parameters = [String: Any]
Parameters就是一個字典,key為字符串沟涨,value可以是任意類型
4. Request
Request.swift這個類中有三個協(xié)議(RequestAdapter、RequestRetrier异吻、TaskConvertible)裹赴、五個類喜庞、六個擴(kuò)展。
四個類及之間的繼承關(guān)系如下:
open class Request
open class DataRequest: Request
open class DownloadRequest: Request
open class UploadRequest: DataRequest
open class StreamRequest: Request
一開始先定義一套協(xié)議棋返,和類型別名(類型別名)延都。如下:
public protocol RequestAdapter {
func adapt(_ urlRequest: URLRequest) throws -> URLRequest
}
// 類似于block
public typealias RequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval) -> Void
public protocol RequestRetrier {
func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion)
}
protocol TaskConvertible {
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask
}
defer關(guān)鍵字:表示在執(zhí)行完方法最后的時候調(diào)用。比如文件打開后最后需要關(guān)閉睛竣。
internal(set):表示set方法只有在內(nèi)部模塊才能訪問晰房。get方法是都能訪問的
open internal(set) var delegate: TaskDelegate {
get {
taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
return taskDelegate
}
set {
taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
taskDelegate = newValue
}
}
Swift 3.0 中方法的返回值必須有接收否則會報警告,當(dāng)然其實主要目的是為了避免開發(fā)人員忘記接收返回值的情況射沟,但是有些情況下確實不需要使用返回值可以使用"_"接收來忽略返回值殊者。當(dāng)然你也可以增加@discardableResult聲明,告訴編譯器此方法可以不用接收返回值验夯。如Request.Swift類的第187行:
@discardableResult
open func authenticate(usingCredential credential: URLCredential) -> Self {
delegate.credential = credential
return self
}
@noescape: 用來標(biāo)記一個閉包, 用法如下func hostFunc(@noescape closure: () -> ()) -> Void
@noescape字面意思是無法逃脫. closure 被@noescape修飾, 則聲明 closure 的生命周期不能超過 hostFunc, 并且, closure不能被hostFunc中的其他閉包捕獲(也就是強(qiáng)持有)
func hostFunc(@noescape closure: () -> ()) -> Void {
//以下編譯出錯, closure 被修飾后, 不能被其他異步線程捕獲
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
closure()
}
}
5. Response
5.1 結(jié)構(gòu)體
在OC中猖吴,我們在寫代碼的時候很少用到結(jié)構(gòu)體。但是在Swift的中挥转,結(jié)構(gòu)體出現(xiàn)的頻率相當(dāng)高海蔽。在文件Response.swift中沒有定義一個類,全是結(jié)構(gòu)體绑谣。
先來回顧一下Swift中結(jié)構(gòu)體和類的關(guān)系:
a.都可以有屬性和方法党窜;
b.都有構(gòu)造器;
c.都支持附屬腳本借宵;
d.都支持?jǐn)U展刑然;
e.都支持協(xié)議。
然后我們來看看他們的不同之處:
a.類有繼承暇务;
b.結(jié)構(gòu)體有一個自動生成的逐一初始化構(gòu)造器泼掠;
c.在做賦值操作時,結(jié)構(gòu)體總是被拷貝(Array有特殊處理)垦细;
d.結(jié)構(gòu)體可以聲明靜態(tài)的屬性和方法择镇;
e.從設(shè)計模式的角度來分析,類的設(shè)計更側(cè)重于對功能的封裝括改,而結(jié)構(gòu)體的設(shè)計更側(cè)重于對數(shù)據(jù)的封裝腻豌。
類的設(shè)計更側(cè)重于對功能的封裝,而結(jié)構(gòu)體的設(shè)計更側(cè)重于對數(shù)據(jù)的封裝嘱能。為了便于代碼組織吝梅,一般在結(jié)構(gòu)體的擴(kuò)展里面添加方法,比如在Response.swift中:
public struct DefaultDataResponse {
public let request: URLRequest?
public let response: HTTPURLResponse?
public let data: Data?
public let error: Error?
public let timeline: Timeline
var _metrics: AnyObject?
public init(
request: URLRequest?,
response: HTTPURLResponse?,
data: Data?,
error: Error?,
timeline: Timeline = Timeline(),
metrics: AnyObject? = nil)
{
self.request = request
self.response = response
self.data = data
self.error = error
self.timeline = timeline
}
}
結(jié)構(gòu)體DefaultDataResponse
完全滿足對數(shù)據(jù)的封裝惹骂,當(dāng)然這里用類來封裝這些數(shù)據(jù)其實也可以苏携,但是就感覺沒有那么完美。
同樣在文件中出現(xiàn)的DataResponse
也是結(jié)構(gòu)體对粪。然后通過擴(kuò)展給結(jié)構(gòu)體添加方法右冻,或者應(yīng)該算是屬性装蓬。
5.2 Response協(xié)議
protocol Response {
var _metrics: AnyObject? { get set }
mutating func add(_ metrics: AnyObject?)
}
mutating:修飾方法是為了能在該方法中修改struct
或是 enum
的變量,在設(shè)計接口的時候纱扭,也要考慮到使用者程序的擴(kuò)展性牍帚。所以要多考慮使用mutating
來修飾方法。如果將Response中修飾方法的mutating去掉乳蛾,編譯器會報錯說沒有實現(xiàn)protocol暗赶。如果將struct中的mutating去掉,則會報錯不能改變結(jié)構(gòu)體的成員肃叶。
通過這樣定義之后蹂随,就可以讓結(jié)構(gòu)體實現(xiàn)這個協(xié)議,然后修改結(jié)構(gòu)體里面的變量了被环。
6. Result
這個類是一個泛型枚舉糙及,通過對結(jié)果的封裝可以直接獲取到更加詳細(xì)的信息。來看代碼:
public enum Result<Value> {
case success(Value)
case failure(Error)
// 對結(jié)果信息進(jìn)一步處理筛欢,可以馬上返回成功或者失敗浸锨。
public var isSuccess: Bool {
switch self {
case .success:
return true
case .failure:
return false
}
}
public var isFailure: Bool {
return !isSuccess
}
/// 對結(jié)果信息進(jìn)一步處理,還可以直接返回成功的值版姑。
public var value: Value? {
switch self {
case .success(let value):
return value
case .failure:
return nil
}
}
public var error: Error? {
switch self {
case .success:
return nil
case .failure(let error):
return error
}
}
}
- CustomStringConvertible柱搜,CustomDebugStringConvertible接口:這兩個接口都是自定義輸出的。之前如果要達(dá)到同樣的效果就重寫toString剥险。
7. SessionDelegate
SessionDelegate看名字以為它是一個代理聪蘸,其實它是一個類,前面說過類一般是對功能的封裝表制。這個類的作用是用閉包(也就是OC中的block)來替代系統(tǒng)中的代理回調(diào)健爬。大致分三個部分:
(1)聲明替代系統(tǒng)代理回調(diào)方法的閉包
注意這里定的閉包在下面的擴(kuò)展里面將對系統(tǒng)的代理進(jìn)行包裝一次,然后外面通過定義的閉包使用么介。
(2)定義需要的屬性及方法娜遵。比如lock,sessionManager.
var retrier: RequestRetrier?
weak var sessionManager: SessionManager?
private var requests: [Int: Request] = [:]
private let lock = NSLock()
/// Access the task delegate for the specified task in a thread-safe manner.
open subscript(task: URLSessionTask) -> Request? {
get {
lock.lock() ; defer { lock.unlock() }
return requests[task.taskIdentifier]
}
set {
lock.lock() ; defer { lock.unlock() }
requests[task.taskIdentifier] = newValue
}
}
(3)在類的擴(kuò)展里面實現(xiàn)系統(tǒng)代理,實現(xiàn)自定義閉包代替系統(tǒng)回調(diào)代理壤短。
這部分比較簡單设拟,基本思路是,實現(xiàn)代理久脯,在代理方法中調(diào)用定義好的閉包纳胧,傳遞參數(shù)。通過對系統(tǒng)的代理方法包裝一層帘撰,然后外部通過定義的閉包來調(diào)用跑慕。OC中的第三方BlockKit的底層原理和此類似。
8. SessionManager
這個類非常重要骡和,包含以下8個方面:
(1)調(diào)用結(jié)果枚舉定義相赁。Helper
這個類似于在OC中定義成功回調(diào)和失敗回調(diào)相寇。只是現(xiàn)在把回調(diào)放到了枚舉里面慰于,這樣更加合理钮科。這種方式得益于case可以傳遞參數(shù)。
public enum MultipartFormDataEncodingResult {
case success(request: UploadRequest, streamingFromDisk: Bool, streamFileURL: URL?)
case failure(Error)
}
調(diào)用方式:
let encodingResult = MultipartFormDataEncodingResult.success(
request: self.upload(data, with: urlRequestWithContentType),
streamingFromDisk: false,
streamFileURL: nil
)
DispatchQueue.main.async { encodingCompletion?(encodingResult) }
(2)屬性定義婆赠。Properties
屬性分為:計算屬性绵脯、存儲屬性、靜態(tài)屬性和實例屬性
靜態(tài)計算屬性:
open static let `default`: SessionManager = {
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
return SessionManager(configuration: configuration)
}()
屬性的get/set方法
open var retrier: RequestRetrier? {
get { return delegate.retrier }
set { delegate.retrier = newValue }
}
這種方式類似于OC中重寫屬性的get/set方法休里。最常見的將model改變和UI綁定在一起蛆挫。
(3)生命周期。Lifecycle
Swift中屬性在初始化之后必須有值妙黍。
關(guān)于初始方法悴侵,Swift3中有init 和 init?,前者代碼一定會走的拭嫁,后者代表可能會走的初始化方法可免。
(4)數(shù)據(jù)請求。Data Request
這部分在定義方法上可以學(xué)習(xí)一下做粤,具體的內(nèi)容就是先定義一個最為基礎(chǔ)的方法浇借,參數(shù)比較多但是一定要有默認(rèn)值,然后后續(xù)的方法在參數(shù)上做減法怕品,最終都是調(diào)用最為基本的方法
(5)下載請求妇垢。Download Request
(6)上傳請求。Upload Request
(7)流式請求肉康。Stream Request
(8)重試闯估。Retry Request