Moya是一個高度抽象的網絡庫,他的理念是讓你不用關心網絡請求的底層的實現細節,只用定義你關心的業務。且Moya采用橋接和組合來進行封裝(默認橋接了Alamofire),使得Moya非常好擴展,讓你不用修改Moya源碼就可以輕易定制。官方給出幾個Moya主要優點:
- 編譯時檢查API endpoint權限
- 讓你使用枚舉定義各種不同Target, endpoints
- 把stubs當做一等公民對待,因此測試超級簡單。
Target
開始Moya之旅的第一步便是,建立一個Enum的Target,這個Target便是你網絡請求相關行為的定義。Target必須實現TargetType協議。
public protocol TargetType { var baseURL: NSURL { get } var path: String { get } var method: Moya.Method { get } var parameters: [String: AnyObject]? { get } var sampleData: NSData { get } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
例如有一個AccountAPI模塊,模塊實現注冊登錄的功能。所以第一件事情,我們需要定義一個Target
enum AccountAPI {
case Login(userName: String, passwd: String) case Register(userName: String, passwd: String) } extension AccountAPI: TargetType { var baseURL: NSURL { return NSURL(string: "https://www.myapp.com")! } var path: String { switch self { case .Login: return "/login" case .Register: return "/register" } } var method: Moya.Method { return .GET } var parameters: [String: AnyObject]? { switch self { case .Login: return nil case .Register(let userName, let passwd): return ["username": userName, "password": passwd] } } var sampleData: NSData { switch self { case .Login: return "{'code': 1,6'Token':'123455'}".dataUsingEncoding(NSUTF8StringEncoding)! case .Register(let userName, let passwd): return "找不到數據" } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
主要是實現了TargetType協議,里面的網址和內容,是隨便寫的,可能不make sence(不合理), 但 僅僅是做一個例子而已。
Providers
Providers是Moya中的核心,Moya中所有的API請求都是通過Provider來發起的。因此大多數時候,你的代碼請求像這樣:
let provider = MoyaProvider<AccountAPI>()
provider.request(.Login) { result in // `result` is either .Success(response) or .Failure(error) }
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
我們初始化了一個AccountAPI的Provider,並且調用了Login請求。怎么樣?干凈簡單吧!
從Provider的構造函數說起
Provider真正做的事情可以用一個流來表示:Target -> Endpoint -> Request 。在這個例子中,它將AccountAPI轉換成Endpoint, 再將其轉換成為NSRURLRequest。最后將這個NSRURLRequest交給Alamofire去進行網絡請求。
我們從Provider的構造函數開始切入,一步一步地扒開它。
//Moya.swift public init(endpointClosure: EndpointClosure = MoyaProvider.DefaultEndpointMapping, requestClosure: RequestClosure = MoyaProvider.DefaultRequestMapping, stubClosure: StubClosure = MoyaProvider.NeverStub, manager: Manager = MoyaProvider<Target>.DefaultAlamofireManager(), plugins: [PluginType] = [])
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
-
首先我們發現的是3個Closure:endpointClosure、requestClosure、stubClosure。這3個Closure是讓我們定制請求和進行測試時用的。非常有用,后面細說。
-
然后是一個Manager,Manager是真正用來網絡請求的類,Moya自己並不提供Manager類,Moya只是對其他網絡請求類進行了簡單的橋接。這么做是為了讓調用方可以輕易地定制、更換網絡請求的庫。比如你不想用Alamofire,可以十分簡單的換成其他庫
-
最后是一個類型為PluginType的數組。Moya提供了一個插件機制,使我們可以建立自己的插件類來做一些額外的事情。比如寫Log,顯示“菊花”等。抽離出Plugin層的目的,就是讓Provider職責單一,滿足開閉原則。把和自己網絡無關的行為抽離。避免各種業務揉在一起不利於擴展。
先來看看第一個EndpointClosure
EndpointClosure
//Moya.swift public typealias EndpointClosure = Target -> Endpoint<Target>
- 1
- 2
- 3
- 1
- 2
- 3
EndpointClosure這個閉包,輸入是一個Target,返回Endpoint。這就是我們前面說的Target -> Endpoint的轉換,那么Endpoint是個什么鬼?
Endpoint 是Moya最終進行網絡請求前的一種數據結構,它保存了這些數據:
- URL
- HTTP請求方式 (GET, POST, etc).
- 本次請求的參數
- 參數的編碼方式 (URL, JSON, custom, etc).
- stub數據的 response(測試用的)
//Endpoint.swift public class Endpoint<Target> { public typealias SampleResponseClosure = () -> EndpointSampleResponse public let URL: String public let method: Moya.Method public let sampleResponseClosure: SampleResponseClosure public let parameters: [String: AnyObject]? public let parameterEncoding: Moya.ParameterEncoding ... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
Moya提供一個默認EndpointClosure的函數,來實現這個Target到Endpoint的轉換:
//Moya.swift public final class func DefaultEndpointMapping(target: Target) -> Endpoint<Target> { let url = target.baseURL.URLByAppendingPathComponent(target.path).absoluteString return Endpoint(URL: url, sampleResponseClosure: {.NetworkResponse(200, target.sampleData)}, method: target.method, parameters: target.parameters) }
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
上面的代碼只是單純地創建並返回一個Endpoint實例。然而在很多時候,我們需要自定義這個閉包來做更多額外的事情。后面在stub小節,你會看到,我們用stub模擬API請求失敗的場景,給客戶端返回一個非200的狀態碼。為了實現這個功能,在這個閉包里處理相關的邏輯,再合適不過了!或者說這個閉包就是讓我們根據業務需求定制網絡請求的。
RequestClosure
//Moya.swift public typealias RequestClosure = (Endpoint<Target>, NSURLRequest -> Void) -> Void
- 1
- 2
- 3
- 1
- 2
- 3
RequestClosure這個閉包就是實現將Endpoint -> NSURLRequest,Moya也提供了一個默認實現:
//Moya.swift public final class func DefaultRequestMapping(endpoint: Endpoint<Target>, closure: NSURLRequest -> Void) { return closure(endpoint.urlRequest) }
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
默認實現也只是簡單地調用endpoint.urlRequest取得一個NSURLRequest實例。然后調用了closure。然而,你可以在這里修改這個請求Request, 事實上這也是Moya給你的最后的機會。舉個例子, 你想禁用所有的cookie,並且設置超時時間等。那么你可以實現這樣的閉包:
let requestClosure = { (endpoint: Endpoint<GitHub>, done: NSURLRequest -> Void) in //可以在這里修改request let request: NSMutableURLRequest = endpoint.urlRequest.mutableCopy() as NSMutableURLRequest request.HTTPShouldHandleCookies = false request.timeoutInterval = 20 done(request) } provider = MoyaProvider(requestClosure: requestClosure)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
從上面可以清晰地看出,EndpointClosure 和 RequestClosure 實現了 Target -> Endpoint -> NSRequest的轉換流
StubClosure
//Moya.swift public typealias StubClosure = Target -> Moya.StubBehavior
- 1
- 2
- 3
- 1
- 2
- 3
StubClosure這個閉包比較簡單,返回一個StubBehavior的枚舉值。它就是讓你告訴Moya你是否使用Stub返回數據或者怎樣使用Stub返回數據
//Moya.swift public enum StubBehavior { case Never //不使用Stub返回數據 case Immediate //立即使用Stub返回數據 case Delayed(seconds: NSTimeInterval) //一段時間間隔后使用Stub返回的數據 }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
Never表明不使用Stub來返回模擬的網絡數據, Immediate表示馬上返回Stub的數據, Delayed是在幾秒后返回。Moya默認是不使用Stub來測試。
在Target那一節我們定義了一個AccountAPI, API中我們實現了接口sampleData, 這個屬性是返回Stub數據的。
extension AccountAPI: TargetType { ... var sampleData: NSData { switch self { case .Login: return "{'code': 1,6'Token':'123455'}".dataUsingEncoding(NSUTF8StringEncoding)! case .Register(let userName, let passwd): return "找不到數據" } } } let endPointAction = { (target: TargetType) -> Endpoint<AccountAPI> in let url = target.baseURL.URLByAppendingPathComponent(target.path).absoluteString switch target { case .Login: return Endpoint(URL: url, sampleResponseClosure: {.NetworkResponse(200, target.sampleData)}, method: target.method, parameters: target.parameters) case .Register: return Endpoint(URL: url, sampleResponseClosure: {.NetworkResponse(404, target.sampleData)}, method: target.method, parameters: target.parameters) } } let stubAction: (type: AccountAPI) -> Moya.StubBehavior = { type in switch type { case .Login: return Moya.StubBehavior.Immediate case .Register: return Moya.StubBehavior.Delayed(seconds: 3) } } let loginAPIProvider = MoyaProvider<AccountAPI>( endpointClosure: endPointAction, stubClosure: stubAction ) self.netProvider = loginAPIProvider loginAPIProvider.request(AccountAPI.Login(userName: "user", passwd: "123456")) { (result) in switch result { case .Success(let respones) : print(respones) case .Failure(_) : print("We got an error") } print(result) }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
就這樣我們就實現了一個Stub! Login和Register都使用了Stub返回的數據。
注意:Moya中Provider對象在銷毀的時候會去Cancel網絡請求。為了得到正確的結果,你必須保證在網絡請求的時候你的Provider不會被釋放。否者你會得到下面的錯誤 “But don’t forget to keep a reference for it in property. If it gets deallocated you’ll see -999 “cancelled” error on response” 。通常為了避免這種情況,你可以將Provider實例設置為類成員變量,或者shared實例
Moya中Stub的實現
大多iOS的Http的Stub框架本質都是實現一個HTTP網絡請求的代理類,去Hook系統Http請求。 如OHHTTPStub就是這么做的。在iOS中,HTTP代理類需要繼承NSURLProtocol類,重載一些父類的方法,然后將這個代理類注冊到系統中去。
class MyHttpProxy : NSURLProtocol { //重載一些父類的方法 override class func canInitWithRequest(request: NSURLRequest) -> Bool { return true } override class func canonicalRequestForRequest(request: NSURLRequest) -> NSURLRequest { return super.canonicalRequestForRequest(request) } .... } //注冊 NSURLProtocol.registerClass(MyHttpProxy.self)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
之后我們APP中所有的網絡請求,都會去經過我們MyHttpProxy的代理類。
然而Moya的Stub不是這樣的,Moya的Stub的實現原理也超級無敵簡單!它不是系統級別的,非入侵式的。它只是簡單的加了一個判斷而已!還是在Moya的Request方法里面
//Moya.swift public func request(target: Target, queue:dispatch_queue_t?, completion: Moya.Completion) -> Cancellable { let endpoint = self.endpoint(target) let stubBehavior = self.stubClosure(target) var cancellableToken = CancellableWrapper() let performNetworking = { (request: NSURLRequest) in if cancellableToken.isCancelled { return } switch stubBehavior { case .Never: cancellableToken.innerCancellable = self.sendRequest(target, request: request, queue: queue, completion: completion) default: cancellableToken.innerCancellable = self.stubRequest(target, request: request, completion: completion, endpoint: endpoint, stubBehavior: stubBehavior) } } requestClosure(endpoint, performNetworking) return cancellableToken }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
Moya先調用我們在構造函數中傳入的stubClosure閉包,如果stubBehavior是Never就真正的發起網絡請求,否
者就調用self.stubRequest
//Moya.swift
internal func stubRequest(target: Target, request: NSURLRequest, completion: Moya.Completion, endpoint: Endpoint<Target>, stubBehavior: Moya.StubBehavior) -> CancellableToken {
... let stub: () -> () = createStubFunction(cancellableToken, forTarget: target, withCompletion: completion, endpoint: endpoint, plugins: plugins) switch stubBehavior { case .Immediate: stub() case .Delayed(let delay): let killTimeOffset = Int64(CDouble(delay) * CDouble(NSEC_PER_SEC)) let killTime = dispatch_time(DISPATCH_TIME_NOW, killTimeOffset) dispatch_after(killTime, dispatch_get_main_queue()) { stub() } case .Never: fatalError("Method called to stub request when stubbing is disabled.") } ... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
如果Immediate,就馬上調用stub返回,是Delayed的話就Dispatch after延遲調用。
Manager
我們知道,Moya並不是一個網絡請求的三方庫,它只是一個抽象的網絡層。它對其他網絡庫的進行了橋接,真正進行網絡請求是別人的網絡庫(比如默認的Alamofire.Manager)
為了達到這個目的Moya做了幾件事情:
首先抽象了一個RequestType協議,利用這個協議將Alamofire隱藏了起來,讓Provider類依賴於這個協議,而不是具體細節。
//Plugin.swift public protocol RequestType { var request: NSURLRequest? { get } func authenticate(user user: String, password: String, persistence: NSURLCredentialPersistence) -> Self func authenticate(usingCredential credential: NSURLCredential) -> Self }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
然后讓Moya.Manager == Alamofire.Manager,並且讓Alamofire.Manager也實現RequestType協議
Moya+Alamofire.swift
public typealias Manager = Alamofire.Manager /// Choice of parameter encoding. public typealias ParameterEncoding = Alamofire.ParameterEncoding //讓Alamofire.Manager也實現 RequestType協議 extension Request: RequestType { }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
上面幾步,就完成了Alamofire的封裝、橋接。正因為橋接封裝了Alamofire, 因此Moya的request,最終一定會調用Alamofire的request。簡單的跟蹤下Moya的Request方法就可以發現sendRequest調用了Alamofire。
//Moya.swift
func sendRequest(target: Target, request: NSURLRequest, queue: dispatch_queue_t?, completion: Moya.Completion) -> CancellableToken {
//調用Alamofire發起網絡請求
let alamoRequest = manager.request(request)
... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
如果你想自定義你自己的Manager, 你可以傳入你自己的Manager到Privoder。之后所有的請求都會經過你的這個Manager
let policies: [String: ServerTrustPolicy] = [ "example.com": .PinPublicKeys( publicKeys: ServerTrustPolicy.publicKeysInBundle(), validateCertificateChain: true, validateHost: true ) ] let manager = Manager( configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), serverTrustPolicyManager: ServerTrustPolicyManager(policies: policies) ) let provider = MoyaProvider<MyTarget>(manager: manager)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
Plugin
Moya提供還提供插件機制,你可以自定義各種插件,所有插件必須滿足PluginType協議
//Plugin.swift public protocol PluginType { /// Called immediately before a request is sent over the network (or stubbed). func willSendRequest(request: RequestType, target: TargetType) // Called after a response has been received, but before the MoyaProvider has invoked its completion handler. func didReceiveResponse(result: Result<Moya.Response, Moya.Error>, target: TargetType) }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
協議里只有兩個方法,willSendRequest和didReceiveResponse。在進行網絡請求之前和收到請求后,Moya會遍歷所有的插件。分別去調用插件各自的willSendRequest和didReceiveResponse方法。
個人覺得這個插件更像是一個網絡回調的Delegate,只是取了一個高大上的名字而已。不過將網絡回調抽取出來確實能更好地將無關業務隔離,讓Privoder更加專心的做自己的事情。而且以后也非常好擴展。
Moya默認提供了三個插件:
- Authentication插件 (CredentialsPlugin.swift)。 HTTP認證的插件。
- Logging插件(NetworkLoggerPlugin.swift)。在調試是,輸入網絡請求的調試信息到控制台
- Network Activity Indicator插件(NetworkActivityPlugin.swift)。可以用這個插件來顯示網絡菊花
Network Activity Indicator插件用法示例,在網絡進行請求開始請求時添加一個Spinner, 請求結束隱藏Spinner。這里用的是SwiftSpinner
let spinerPlugin = NetworkActivityPlugin { state in if state == .Began { SwiftSpinner.show("Connecting...") } else { SwiftSpinner.show("request finish...") SwiftSpinner.hide() } let loginAPIProvider = MoyaProvider<AccountAPI>( plugins: [spinerPlugin] ) loginAPIProvider.request(.Login) { _ in }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
插件實現代碼
插件的源碼實現也超級簡單。在進行網絡請求之前和收到請求后,遍歷所有的插件,調用其相關的接口。只是要分別處理下Stub和真正進行網絡請求的兩種情況
//Moya.swift func sendRequest(target: Target, request: NSURLRequest, queue: dispatch_queue_t?, completion: Moya.Completion) -> CancellableToken { let alamoRequest = manager.request(request) let plugins = self.plugins // 遍歷插件,通知開始請求 plugins.forEach { $0.willSendRequest(alamoRequest, target: target) } // Perform the actual request alamoRequest.response(queue: queue) { (_, response: NSHTTPURLResponse?, data: NSData?, error: NSError?) -> () in let result = convertResponseToResult(response, data: data, error: error) // 遍歷插件,通知收到請求 plugins.forEach { $0.didReceiveResponse(result, target: target) } completion(result: result) } alamoRequest.resume() return CancellableToken(request: alamoRequest) } //在測試時,Stub分支的也要,遍歷調用一次插件 internal final func createStubFunction(token: CancellableToken, forTarget target: Target, withCompletion completion: Moya.Completion, endpoint: Endpoint<Target>, plugins: [PluginType]) -> (() -> ()) { return { if (token.canceled) { let error = Moya.Error.Underlying(NSError(domain: NSURLErrorDomain, code: NSURLErrorCancelled, userInfo: nil)) //調用插件 plugins.forEach { $0.didReceiveResponse(.Failure(error), target: target) } completion(result: .Failure(error)) return } switch endpoint.sampleResponseClosure() { case .NetworkResponse(let statusCode, let data): let response = Moya.Response(statusCode: statusCode, data: data, response: nil) //成功情況,調用插件 plugins.forEach { $0.didReceiveResponse(.Success(response), target: target) } completion(result: .Success(response)) case .NetworkError(let error): let error = Moya.Error.Underlying(error) //失敗情況,調用插件 plugins.forEach { $0.didReceiveResponse(.Failure(error), target: target) } completion(result: .Failure(error)) } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
總結
總的來說Moya的實現比較簡單,但是基於作者這種橋接、封裝的思路,使得Moya擴展十分靈活,所以Moya有各種Provider, 能和RxSwift, RAC等等輕松的結合。 而Moya用起來也非常的干凈。你不用關心Request具體實現。只用專注於你自己的Target設計就行。再加上Moya的Stub特性,的確使得它十分易於測試。
自己的思考
成也蕭何敗也蕭何。然而我自己的感受,Moya讓我們把所有的業務都放到Target中去,也會導致另外一些問題:
(以下僅是個人觀點,僅供參考)
-
枚舉無法重載,代碼未必簡潔
比如,現在要添加一個新接口,還是要求實現Login功能,除了支持已有的用戶名/密碼登錄,還要支持指紋登錄。那么我們想定義可能想這樣:Login(fingerPrint: String)。這兩種登錄情況實際上只是參數不一樣。但在因為枚舉中不能重載,所以為了添加這個case,我們不得不重新取一個名字,而不能利用函數重載。enum AccountAPI { case Login(userName: String, passwd: String) case Register(userName: String, passwd: String) //case Login(fingerPrint: String) //error: 不能這樣添加錯的,不支持重載 case LoginWithPrint(fingerPrint: String) //正確. 只能改名 }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
我個人覺得這樣做,似乎並沒有重載簡潔。相比修改名字,我更喜歡重載。
-
Target碎片化,后期維護困難
隨着業務的增加,Target會變得很復雜。TargetType協議它是利用多個屬性:method屬性、parameters屬性等。將一次API請求的實現的分割到多個了函數(屬性)中去實現。這就導致實現碎片化了。添加一個API請求,你需要修改幾個函數(屬性), 改幾個switch語句。如果文件很長,修改起來真的很煩,根本不好歸類整理。 -
不利於多人協作開發
因為大家每次添加新功能,修改的都是這幾個相同的函數(屬性),所以非常容易導致文件沖突。