Moya使用

關(guān)于Moya的官方可參考:?點(diǎn)擊查看

Moya官方使用下圖來對(duì)比直接使用Alamofire和用Moya的區(qū)別(左:Alamofire,右:Moya)

moya

Moya包含模塊:

Moya模塊

Moya流程圖:

Moya流程圖

Moya使用

在項(xiàng)目中可通過Pod,Carthage等方式引入Moya

CocoaPods

pod 'Moya','~> 14.0'

# or?

pod 'Moya/RxSwift' , '~> 14.0'

# or

pod 'Moya/ReactiveSwift' , '~> 14.0'

Carthage

github "Moya/Moya" -> 14.0

Moya使用介紹:

Targets

使用Moya,首先需要定義一個(gè)target(通常是繼承TargetType協(xié)議的枚舉變量),接下來,只需要處理這些targets(即:希望調(diào)用API完成的操作)

Targets必須繼承TargetType

TargetType協(xié)議要求在枚舉中定義一個(gè)baseURL屬性。注意:baseURL的值不會(huì)取決于self的值,而是返回一個(gè)固定值(如果有多個(gè)API baseURL,需要使用多個(gè)枚舉和Moya providers)

例如:

public enum GitHub {

? ? case zen

? ? case userProfile(String)

? ? case userRepositories(String)

}

extension GitHub: TargetType {

? ? public var baseURL: URL { return URL(string: "https://api.github.com")! }

? ? //path可以用來拼接相對(duì)路徑:(使用了String的擴(kuò)展方法urlEscaped)

? ? public var path: String {

? ? ? ? switch self{

? ? ? ? case .zen:

? ? ? ? ? ? return "/zen"

? ? ? ? case .userProfile(let name):

? ? ? ? ? ? return "/users/\(name.urlEscaped)"?

? ? ? ? case .userRepositories(let name):

? ? ? ? ? ? return "/users/\(name.urlEscaped)/repos"

? ? ? ? }

? ? }

? ? //設(shè)置method,這里使用的是GET方法;如果請(qǐng)求需要POST或別的方法,可以通過switch self來返回合適的值

? ? public var method: Moya.Method {

? ? ? ? return .get

? ? }

? ? //請(qǐng)求任務(wù)事件(這里附帶上參數(shù))

? ? public var task:Task{

? ? ? ? switch self{

? ? ? ? case .userRepositories:

? ? ? ? ? ? return .requestParameters(parameters: ["sort":"pushed"], encoding: URLEncoding.default)

? ? ? ? default:

? ? ? ? ? ? return .requestPlain

? ? ? ? }

? ? }

? ? //是否執(zhí)行Alamofire驗(yàn)證

? ? public var validationType : ValidationType{

? ? ? ? switch self{

? ? ? ? case .zen:

? ? ? ? ? ? return .successCodes

? ? ? ? default:

? ? ? ? ? ? return .none

? ? ? ? }

? ? }

? ? //sampleData屬性,這是TargetType協(xié)議所必須的。任何想要調(diào)用target必須提供一些非空的NSData類型的返回值。

? ? //就是做單元測(cè)試模擬的數(shù)據(jù),只會(huì)在單元測(cè)試文件中有作用

? ? public var sampleData:Data{

? ? ? ? switch self{

? ? ? ? case .zen:

? ? ? ? ? ? return "Half measures are as bad as nothing at all.".data(using: String.Encoding.utf8)!

? ? ? ? case .userProfile(letname):

? ? ? ? ? ? return "{\"login\": \"\(name)\", \"id\": 100}".data(using: String.Encoding.utf8)!

? ? ? ? case .userRepositories(let name):

? ? ? ? ? ? return "[{\"name\": \"\(name)\"}]".data(using: String.Encoding.utf8)!

? ? ? ? }

? ? }

? ? //指定headers,可通過switch self來返回不同的header

? ? public var headers: [String:String]? {

? ? ? ? return nil

? ? }

}

做完上面的TargetType之后,構(gòu)造Provider就很簡(jiǎn)單啦

Provider

provider是網(wǎng)絡(luò)請(qǐng)求的提供者,所有的網(wǎng)絡(luò)請(qǐng)求都通過provider來調(diào)用。

通過枚舉來指定要訪問的具體API

provider最簡(jiǎn)單的創(chuàng)建方法:

let provider = MoyaProvider<GitHub>() //GitHub就是遵循TargetType協(xié)議的枚舉

通過Moya源碼可知MoyaProvider是一個(gè)實(shí)現(xiàn)了MoyaProviderType協(xié)議的公開類,需要傳入一個(gè)遵循TargetType協(xié)議的對(duì)象名,這是泛型的常規(guī)用法

open class MoyaProvider<Target: TargetType>: MoyaProviderType {

? ? ......

}

簡(jiǎn)單配置后就可以使用

provier.request(.zen){ result in

? ? //......

}

request方法返回一個(gè)Cancellable,它有一個(gè)可以取消request的公共的方法。

MoyaProvider的構(gòu)造方法如下:

/// Initializes a provider.

public init(endpointClosure: @escaping EndpointClosure = MoyaProvider.defaultEndpointMapping,

? ? ? ? ? ? ? ? requestClosure:@escaping RequestClosure = MoyaProvider.defaultRequestMapping,

? ? ? ? ? ? ? ? stubClosure:@escaping StubClosure = MoyaProvider.neverStub,

? ? ? ? ? ? ? ? callbackQueue:DispatchQueue? =nil,

? ? ? ? ? ? ? ? session:Session= MoyaProvider.defaultAlamofireSession(),

? ? ? ? ? ? ? ? plugins: [PluginType] = [],

? ? ? ? ? ? ? ? trackInflights:Bool=false) {

? ? ? ? self.endpointClosure= endpointClosure

? ? ? ? self.requestClosure= requestClosure

? ? ? ? self.stubClosure= stubClosure

? ? ? ? self.session = session

? ? ? ? self.plugins= plugins

? ? ? ? self.trackInflights= trackInflights

? ? ? ? self.callbackQueue= callbackQueue

? ? }

了解Moya的高級(jí)用法,需要先了解清晰MoyaProvider構(gòu)造方法的所有參數(shù)

1、endpointClosure 一個(gè)endpoints閉包,它可以將target轉(zhuǎn)換成具體的EndPoint實(shí)例

let endpointClosure: MoyaProvider<HTTPBin>.EndpointClosure = { target in

? ? ? ? ? ? ? ? let task:Task

? ? ? ? ? ? ? ? switch target.task {

? ? ? ? ? ? ? ? case let .uploadMultipart(multipartFormData):

? ? ? ? ? ? ? ? ? ? let additional = Moya.MultipartFormData(provider: .data("test2".data(using: .utf8)!), name:"test2")

? ? ? ? ? ? ? ? ? ? var newMultipartFormData = multipartFormData

? ? ? ? ? ? ? ? ? ? newMultipartFormData.append(additional)

? ? ? ? ? ? ? ? ? ? task = .uploadMultipart(newMultipartFormData)

? ? ? ? ? ? ? ? default:

? ? ? ? ? ? ? ? ? ? task = target.task

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? return Endpoint(url: URL(target: target).absoluteString, sampleResponseClosure: {.networkResponse(200, target.sampleData)}, method: target.method, task: task, httpHeaderFields: target.headers)

? ? ? ? ? ? }

let provider = MoyaProvider(endpointClosure:endpointClosure )

這樣初始化MoyaProvider的時(shí)候,不需要在說明target的具體類型,Swit會(huì)根據(jù)endpointClosure推斷。

MoyaProvider.defaultEndpointMapping 為默認(rèn)實(shí)現(xiàn)

2、requestClosure

可以將Endpoint轉(zhuǎn)換為NSURLRequest

let requestClosure = {(endpoint:Endpoint,done:MoyaProvider.RequestResultClosure) in

? ? ? ? do{

? ? ? ? ? ? var request=try endpoint.urlRequest()

? ? ? ? ? ? done(.success(request))

? ? ? ? }catch{

? ? ? ? ? ? done(.failure(MoyaError.underlying(error)))

? ? ? ? }

? ? }

? ? let provider= MoyaProvider(requestClosure:requestClosure)

3、stubClosure

它返回一個(gè).Never(默認(rèn)),或.Immediate,或.Delayed(seconds),可以延遲這個(gè)stub模擬請(qǐng)求(具體n秒)。例如:.Delayed(1),會(huì)把每個(gè)請(qǐng)求延遲1秒

這樣的好處就是當(dāng)需要模擬不同于其他的特殊請(qǐng)求是,可以編寫自己的閉包:

let provider = MoyaProvider<MyTarget>(stubClosure: { target: MyTarget -> Moya.StubBehavior in

????switch target {

????????/* Return something different based on the target. */ }

})

4、callbackQueue

可指定callback的Queue

5、session

可針對(duì)URLSessionConfiguration進(jìn)行配置

final class func defaultAlamofireSession() ->Session{

? ? ? ? let configuration = URLSessionConfiguration.default

? ? ? ? configuration.headers = .default

? ? ? ? returnSession(configuration: configuration, startRequestsImmediately:false)

}

6、plugins

插件數(shù)組,它們?cè)诎l(fā)起請(qǐng)求之前和收到返回之后調(diào)用,比如:開始網(wǎng)絡(luò)請(qǐng)求之前的NetworkActivityPlugin, 結(jié)束網(wǎng)絡(luò)之后的日志NetworkLoggerPlugin

NetworkLoggerPlugin

使用方式:

static var plugins:[PluginType{

? ?let activityPlugin = NewNetworkActivityPlugin{(state,targetType) in ????

????????switch state{

????????????case .began:

? ? ? ? ? ? ? ? //顯示loading

????????????case .ended:

? ? ? ? ? ? ? ? //關(guān)閉loading

????????}

????}

????return [activityPlugin,myLoggorPlugin]

}

let userModuleProvider = MoyaProvider<UserModule>(plugins:plugins)

7、trackInflights

是否要跟蹤重復(fù)網(wǎng)絡(luò)請(qǐng)求


MultiTarget

正常情況下,都是一個(gè)target對(duì)應(yīng)一個(gè)Provider

?有時(shí)候程序會(huì)根據(jù)業(yè)務(wù)邏輯拆分成多個(gè)target,這樣target可能就會(huì)有多個(gè),如果有多個(gè)target我們就創(chuàng)建多少個(gè)Provider,會(huì)讓程序的邏輯復(fù)雜化。

?特別是當(dāng)它們使用同樣的plugings或closures時(shí),又要做一些額外的工作去維護(hù)。

?那么借助MultiTarget,可以讓多個(gè)target都使用相同的Provider

例子:

定義Target

public enum OPKeyConfigTarget {

? ? case config

}

extension OPKeyConfigTarget: TargetType {

? ? //服務(wù)器地址

? ? public var baseURL: URL {

? ? ? ? switch self{

? ? ? ? case .config:

? ? ? ? ? ? return URL(string: "http://xxx.com")!

? ? ? ? }

? ? }

? ? //各個(gè)請(qǐng)求的具體路徑

? ? public var path: String {

? ? ? ? switch self{

? ? ? ? case .config:

? ? ? ? ? ? return "/path/xxx"

? ? ? ? }

? ? }

? ? //請(qǐng)求類型

? ? public var method: Moya.Method {

? ? ? ? return.post

? ? }

? ? //做單元測(cè)試模擬的數(shù)據(jù),只會(huì)在單元測(cè)試文件中有作用

? ? public var sampleData: Data {

? ? ? ? return "{}".data(using: String.Encoding.utf8)!

? ? }

? ? //請(qǐng)求任務(wù)事件(這里附帶上參數(shù))

? ? public var task: Task {

? ? ? ? switch self{

? ? ? ? case .config:

? ? ? ? ? ? var params : [String:Any] = [:]

? ? ? ? ? ? return .requestParameters(parameters: params, encoding:URLEncoding.default)

? ? ? ? }

? ? }

? ? //請(qǐng)求頭

? ? public var headers: [String : String]? {

? ? ? ? return nil

? ? }


}

public enum OPMarketsTarget{

? ? case market

}

extension OPMarketsTarget: TargetType{

? ? //服務(wù)器地址

? ? public var baseURL: URL {

? ? ? ? return URL(string: "http://xxx.com")!

? ? }

? ? //各個(gè)請(qǐng)求的具體路徑

? ? public var path: String {

? ? ? ? return "/path/xxx"

? ? }

? ? //請(qǐng)求類型

? ? public var method: Moya.Method {

? ? ? ? return .get

? ? }

? ? //做單元測(cè)試模擬的數(shù)據(jù),只會(huì)在單元測(cè)試文件中有作用

? ? public var sampleData: Data {

? ? ? ? return "{}".data(using: String.Encoding.utf8)!

? ? }

? ? //請(qǐng)求任務(wù)事件(這里附帶上參數(shù))

? ? public var task: Task {

? ? ? ? return .requestPlain

? ? }

? ? //請(qǐng)求頭

? ? public var headers: [String : String]? {

? ? ? ? return nil

? ? }

}

Provider定義

1對(duì)1 使用方式

let keyConfigProvider = MoyaProvider<OPKeyConfigTarget>()

let marketsProvider = MoyaProvider<OPMarketsTarget>()

?使用多個(gè)target的Provider可以如下方式定義

let provider = MoyaProvider<MultiTarget>()

Provider使用

一對(duì)一 使用方式

? ? keyConfigProvider.request(.config) { result in

? ? ? ? //...

? ? }


? ? marketsProvider.request(.market) { result in

? ? ? ? //...

? ? }

?使用多個(gè)target的Provider,如下方式

? ? provider.request(MultiTarget(OPKeyConfigTarget.config)) { result in

? ? ? ? //...

? ? }

? ? provider.request(MultiTarget(OPMarketsTarget.market)) { result in

? ? ? ? //...

? ? }

Moya針對(duì)返回結(jié)果的處理:

Moya 會(huì)將 Alamofire 成功或失敗的響應(yīng)包裹在 Result 枚舉中返回,具體值如下:

?? ? ? ? .success(Moya.Response):成功的情況。我們可以從 Moya.Response 中得到返回?cái)?shù)據(jù)(data)和狀態(tài)(status )

?? ? ? ? .failure(MoyaError):失敗的情況。這里的失敗指的是服務(wù)器沒有收到請(qǐng)求(例如可達(dá)性/連接性錯(cuò)誤)或者沒有發(fā)送響應(yīng)(例如請(qǐng)求超時(shí))。我們可以在這里設(shè)置個(gè)延遲請(qǐng)求,過段時(shí)間重新發(fā)送請(qǐng)求。

provider.request(MultiTarget(OPKeyConfigTarget.config)) { result in

? ? ? ? ? ? switch result{

? ? ? ? ? ? case let .success(response):

? ? ? ? ? ? ? ? //方式一:

? ? ? ? ? ? ? ? let statusCode = response.statusCode? ? //響應(yīng)狀態(tài)碼

? ? ? ? ? ? ? ? let data = response.data? ? //響應(yīng)數(shù)據(jù)

? ? ? ? ? ? ? ? //方式二:過濾正確的狀態(tài)碼

? ? ? ? ? ? ? ? do{

? ? ? ? ? ? ? ? ? ? try response.filterSuccessfulStatusCodes()

? ? ? ? ? ? ? ? ? ? let data =try response.mapJSON()

? ? ? ? ? ? ? ? ? ? print("data===\(data)")

? ? ? ? ? ? ? ? ? ? //繼續(xù)做一些其他事情

? ? ? ? ? ? ? ? }catch{

? ? ? ? ? ? ? ? ? ? //處理錯(cuò)誤狀態(tài)碼的響應(yīng)

? ? ? ? ? ? ? ? }

? ? ? ? ? ? case let .failure(_):

? ? ? ? ? ? ? ? //錯(cuò)誤處理

? ? ? ? ? ? ? ? break

? ? ? ? ? ? }

? ? ? ? }

針對(duì)錯(cuò)誤的類型,可以通過switch語(yǔ)句判斷具體的MoyaError錯(cuò)誤類型:

case let .failure(error):

?? ? ? ? ? ? switch error {

?? ? ? ? ? ? case .imageMapping(let response):

?? ? ? ? ? ? ? ? print("錯(cuò)誤原因:\(error.errorDescription ?? "")")

?? ? ? ? ? ? ? ? print(response)

?? ? ? ? ? ? case .jsonMapping(let response):

?? ? ? ? ? ? ? ? print("錯(cuò)誤原因:\(error.errorDescription ?? "")")

?? ? ? ? ? ? ? ? print(response)

?? ? ? ? ? ? case .statusCode(let response):

?? ? ? ? ? ? ? ? print("錯(cuò)誤原因:\(error.errorDescription ?? "")")

?? ? ? ? ? ? ? ? print(response)

?? ? ? ? ? ? case .stringMapping(let response):

?? ? ? ? ? ? ? ? print("錯(cuò)誤原因:\(error.errorDescription ?? "")")

?? ? ? ? ? ? ? ? print(response)

?? ? ? ? ? ? case .underlying(let error1, let response):

?? ? ? ? ? ? ? ? print("錯(cuò)誤原因:\(error.errorDescription ?? "")")

?? ? ? ? ? ? ? ? print(error1)

?? ? ? ? ? ? ? ? print(response as Any)

?? ? ? ? ? ? case .requestMapping:

?? ? ? ? ? ? ? ? print("錯(cuò)誤原因:\(error.errorDescription ?? "")")

?? ? ? ? ? ? ? ? print("nil")

? ? ? ? ? ? case .objectMapping(let error1, let response):

? ??????????????print(error1)

? ??????????????print("錯(cuò)誤原因:\(error.errorDescription ?? "")")

? ??????????????print(response)

? ? ? ? ? ??case .encodableMapping(let response):

? ??????????????print("錯(cuò)誤原因:\(error.errorDescription ?? "")")

? ??????????????print(response)

? ??????????case .parameterEncoding(let response):

? ??????????????print("錯(cuò)誤原因:\(error.errorDescription ?? "")")

? ??????????????print(response)

}

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

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

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