1.Moya的定义
Moya是一个高度抽象的网络库,他的理念是让你不用关心网络请求的底层的实现细节,只用定义你关心的业务。且Moya采用桥接和组合来进行封装(默认桥接了Alamofire),使得Moya非常好扩展,让你不用修改Moya源码就可以轻易定制。官方给出几个Moya主要优点:- 编译时检查
API endpoint权限 - 让你使用枚举定义各种不同
Target,endpoints - 把
stubs当做一等公民对待,因此测试超级简单。
- 编译时检查
2.Moya的使用
Moya的使用分成几步,首先需要先自定义一个枚举类型。
enum SHChannelViewModelApiManager{ case getChannelList(Bool) case getItemList(String) }
- 然后遵循开闭原则,让枚举的分类遵循
Moya的TargetType,按需实现定义的各种get方法
public protocol TargetType { /// The target's base `URL`. var baseURL: URL { get } /// The path to be appended to `baseURL` to form the full `URL`. var path: String { get } /// The HTTP method used in the request. var method: Moya.Method { get } /// Provides stub data for use in testing. var sampleData: Data { get } /// The type of HTTP task to be performed. var task: Task { get } /// A Boolean value determining whether the embedded target performs Alamofire validation. Defaults to `false`. var validate: Bool { get } /// The headers to be used in the request. var headers: [String: String]? { get } }
extension SHChannelViewModelApiManager:TargetType { var baseURL: URL { return URL(string: baseUrl)! } var task: Task { switch self { case .getChannelList: return .requestPlain case .getItemList(let pipe): return .requestParameters(parameters: ["pipe":pipe], encoding: URLEncoding.queryString) } } var method: Moya.Method { return .get } var path: String { switch self { case .getChannelList(let isToday): if isToday{ return "/Youmeng/chaxunservletall" }else{ return "/Youmeng/chaxunservletallhistory" } case .getItemList: return itemListUrl } } }
- 最后创建
MoyaProvider对象,泛型允许传入任何你定义的遵循TargetType协议的枚举,
let provider = MoyaProvider<SHChannelViewModelApiManager>()
- 使用
MoyaProvider对象发起请求
provider.rx.request(.getItemList(pipe)).mapArr([SHChannelItemTopModel].self).subscribe(onSuccess: { [weak self](model) in self?.topModels = model self?.itemOutput.onNext(true) }) { [weak self](error) in self?.itemOutput.onNext(false) }.disposed(by: bag) }
3.Moya的所有文件解析

- Provider
- MoyaProvider
- MoyaProvider+Defaults
- MoyaProvider+Internal
- Endpoint
- Task (是一个枚举,定义了任务的形式,是传参呢还是啥都不传了,是上传了还是下载任务)
- Cancellable (只是标明了请求是否可以被取消,和一个取消请求的方法)
- TargetType
- TargetType
- MultiTarget (
MultiTarget用于使MoyaProvider能够处理多个TargetType)
- Result
- Response
- MoyaError (是一个枚举,定义了
Moya可能抛出的各种错误,包括上面说的Response三个map方法出错,状态码出错,解码出错,和参数编码错误等,同时有两个get方法,得出错误的Response返回以及错误描述返回)
- Plugins
- Alamofire
这里有一个iOS交流圈:891 488 181 分享BAT,阿里面试题、面试经验,讨论技术, 感兴趣的话大家一起交流学习!- Moya+Alamofire (
Moya和Alamofire的桥接文件,桥接模式保证了最少知道原则,如果进行替换Alamofire,主要修改这个文件) - MultipartFormData (多文件上传与
Alamofire的桥接,通过append等方法把moya形式的MultipartFormData添加到Alamofire里去)
- Moya+Alamofire (
- URL
- URL+Moya(通过获取
TargetType的baseURL和path初始化一个URL) - URLRequest+Encoding(对
URLRequest的两个encoded方法)
- URL+Moya(通过获取
3.1.MoyaProvider
MoyaProvider 初始化
MoyaProvider是请求提供者类。只能通过该类发起请求,类的初始化如下
public init(endpointClosure: @escaping EndpointClosure = MoyaProvider.defaultEndpointMapping, requestClosure: @escaping RequestClosure = MoyaProvider.defaultRequestMapping, stubClosure: @escaping StubClosure = MoyaProvider.neverStub, callbackQueue: DispatchQueue? = nil, manager: Manager = MoyaProvider<Target>.defaultAlamofireManager(), plugins: [PluginType] = [], trackInflights: Bool = false) { self.endpointClosure = endpointClosure self.requestClosure = requestClosure self.stubClosure = stubClosure self.manager = manager self.plugins = plugins self.trackInflights = trackInflights self.callbackQueue = callbackQueue }
由以上代码可以得知,初始化可传入的参数。
EndpointClosure是一个把传入的Target转化为Endpoint对象的闭包,public typealias EndpointClosure = (Target) -> Endpoint<Target>既然如此,我们就顺腾摸瓜,看一下
Endpoint对象里面是什么open class Endpoint<Target> { public typealias SampleResponseClosure = () -> EndpointSampleResponse /// 请求的URL的字符串. open let url: String /// stub数据的 response(测试用的) open let sampleResponseClosure: SampleResponseClosure /// 请求方式. open let method: Moya.Method /// 请求任务. open let task: Task /// 请求头. open let httpHeaderFields: [String: String]? public init(url: String, sampleResponseClosure: @escaping SampleResponseClosure, method: Moya.Method, task: Task, httpHeaderFields: [String: String]?) { self.url = url self.sampleResponseClosure = sampleResponseClosure self.method = method self.task = task self.httpHeaderFields = httpHeaderFields } ... }可以看出,
Endpoint的属性,基本对应TargetType协议对应的get方法,所以才能进行转化,EndpointClosure的作用在于,可以根据业务需求在这里重新定制网络请求,还可以通过stub进行数据测试,可以看看官方默认的闭包实现public final class func defaultEndpointMapping(for target: Target) -> Endpoint<Target> { return Endpoint( url: URL(target: target).absoluteString, sampleResponseClosure: { .networkResponse(200, target.sampleData) }, method: target.method, task: target.task, httpHeaderFields: target.headers ) }RequestClosure这个闭包实现就是将Endpoint转化成真正的请求对象URLRequest//RequestClosure public typealias RequestClosure = (Endpoint<Target>, @escaping RequestResultClosure) -> Void // 上面的RequestResultClosure public typealias RequestResultClosure = (Result<URLRequest, MoyaError>) -> Void看看
Moya提供的默认实现public final class func defaultRequestMapping(for endpoint: Endpoint<Target>, closure: RequestResultClosure) { do { //urlRequest请求 let urlRequest = try endpoint.urlRequest() closure(.success(urlRequest)) // MoyaError类型的错误,稍后讲解 } catch MoyaError.requestMapping(let url) { closure(.failure(MoyaError.requestMapping(url))) } catch MoyaError.parameterEncoding(let error) { closure(.failure(MoyaError.parameterEncoding(error))) } catch { closure(.failure(MoyaError.underlying(error, nil))) } }上面代码通过
endpoint.urlRequest()获得urlRequest,那我们就看一下urlRequest()的具体实现public func urlRequest() throws -> URLRequest { guard let requestURL = Foundation.URL(string: url) else { throw MoyaError.requestMapping(url) } var request = URLRequest(url: requestURL) request.httpMethod = method.rawValue request.allHTTPHeaderFields = httpHeaderFields switch task { case .requestPlain, .uploadFile, .uploadMultipart, .downloadDestination: return request case .requestData(let data): request.httpBody = data return request //.................此处省略一点代码 } }现在我们清晰的发现确实是转成
URLRequest了,事实上,这也是Moya给你最后的机会了,举个例子,你想设置超时时间,let requestClosure = { (endpoint:Endpoint<SHChannelViewModelApiManager>,closure:RequestResultClosure){ do { var urlRequest = try endpoint.urlRequest() //设置超时时间,urlRequest的可配置的东西都可以在这配置 urlRequest.timeoutInterval = 60 closure(.success(urlRequest)) } catch MoyaError.requestMapping(let url) { closure(.failure(MoyaError.requestMapping(url))) } catch MoyaError.parameterEncoding(let error) { closure(.failure(MoyaError.parameterEncoding(error))) } catch { closure(.failure(MoyaError.underlying(error, nil))) } } }StubClosure返回了一个StubBehavior的枚举值,它就是让你告诉Moya你是否使用Stub返回数据或者怎样使用Stub返回数据,默认是不返回public typealias StubClosure = (Target) -> Moya.StubBehaviorpublic enum StubBehavior { /// 不使用Stub返回数据. case never /// 立即使用Stub返回数据 case immediate /// 一段时间间隔后使用Stub返回的数据. case delayed(seconds: TimeInterval) }下面用个例子来总结一下这三个闭包的用法
var sampleData: Data { return "{'code': 0,'Token':'egfdg8dfg8e93hr93'}".data(using: String.Encoding.utf8)! } //定义在SHChannelViewModelApiManager外头 let endPointAction = {(target: SHChannelViewModelApiManager) -> Endpoint<SHChannelViewModelApiManager> in return Endpoint( url: URL(target: target).absoluteString, sampleResponseClosure: { .networkResponse(400, target.sampleData) }, method: target.method, task: target.task, httpHeaderFields: target.headers ) } //3秒后返回 let stubAction: (_ type: SHChannelViewModelApiManager) -> Moya.StubBehavior = { type in return Moya.StubBehavior.delayed(seconds: 3) } //创建moyaProvider let moyaProvider = MoyaProvider<SHChannelViewModelApiManager>(endpointClosure: endPointAction, stubClosure: stubAction) //使用 moyaProvider.request(SHChannelViewModelApiManager.getChannelList)........Manager就没什么好说的了,就是Alamofire的SessionManagerpublic typealias Manager = Alamofire.SessionManagerManager是真正用来网络请求的类,Moya自己并不提供Manager类,Moya只是对其他网络请求类进行了简单的桥接(桥接模式)。这么做是为了让调用方可以轻易地定制、更换网络请求的库。比如你不想用Alamofire,可以十分简单的换成其他库,callbackQueue作为回调队列传给Alamofire,如果为nil– 将使用Alamofire默认的,下面是找到的一个用途例子if let callbackQueue = callbackQueue { callbackQueue.async(execute: sendProgress) } else { sendProgress() }plugins–Moya提供了一个插件机制,使我们可以建立自己的插件类来做一些额外的事情。比如写Log,显示“菊花”等。抽离出Plugin层的目的,就是让Provider职责单一,满足开闭原则。把和自己网络无关的行为抽离。避免各种业务揉在一起不利于扩展,(其实更像一个请求的生命周期,在该插入的地方调用)trackInflights,根据代码逻辑可以看出来,这是是否对重复请求情况的处理。其中有一个解释是:是否要跟踪重复网络请求。if trackInflights { objc_sync_enter(self)//递归锁 var inflightCompletionBlocks = self.inflightRequests[endpoint] inflightCompletionBlocks?.append(pluginsWithCompletion) self.inflightRequests[endpoint] = inflightCompletionBlocks objc_sync_exit(self) if inflightCompletionBlocks != nil { // 如果存在,就是说明已经有一个已经重复的请求了,就把这个取消了 return cancellableToken } else { objc_sync_enter(self) // 如果不存在 key 为 endpoint 的值,则初始化一个 self.inflightRequests[endpoint] = [pluginsWithCompletion] objc_sync_exit(self) } }一个请求在
init的时候将trackInflights设置为true,那么在Moya中就会存储这个请求的endpoint。在返回数据的时候,如果需要跟踪了重复请求,那么就将一次实际发送请求返回的数据,多次返回。
MoyaProvider 发送请求
- 在
Moya中request方法是一个统一的请求入口。只需要在方法中配置需要的参数,包括需要对应生成的请求地址,请求参数等通过枚举类型,十分清晰的分类和管理。利用.语法生成对应的枚举,然后依次生成endpoint,URLRequest等@discardableResult open func request(_ target: Target, callbackQueue: DispatchQueue? = .none, progress: ProgressBlock? = .none, completion: @escaping Completion) -> Cancellable { let callbackQueue = callbackQueue ?? self.callbackQueue return requestNormal(target, callbackQueue: callbackQueue, progress: progress, completion: completion) } target就是传入的自定义枚举。callbackQueue同上progress代表请求任务完成进度的回调,默认不使用public typealias ProgressBlock = (_ progress: ProgressResponse) -> Void再点进去
ProgressResponse看看public struct ProgressResponse { /// The optional response of the request. public let response: Response? /// An object that conveys ongoing progress for a given request. public let progressObject: Progress? /// Initializes a `ProgressResponse`. public init(progress: Progress? = nil, response: Response? = nil) { self.progressObject = progress self.response = response } /// The fraction of the overall work completed by the progress object. public var progress: Double { return progressObject?.fractionCompleted ?? 1.0 } /// A Boolean value stating whether the request is completed. public var completed: Bool { return progress == 1.0 && response != nil } }由上可知,
progressObject是一个Foundation框架的Progress对象,这是iOS 7加入的专门用于监控任务进度的类completion是请求完成后返回的回调public typealias Completion = (_ result: Result<Moya.Response, MoyaError>) -> Void
3.2.MoyaProvider+Defaults
MoyaProvider+Defaults里面就是 3 个默认方法,前面已经提到过,就不多做赘述了
defaultEndpointMapping返回Endpoint的默认方法defaultRequestMapping本质是返回URLRequest的默认方法defaultAlamofireManager返回网络库的manager的默认方法(默认是Alamofire)
3.3.MoyaProvider+Internal
Method是对Alamofire.HTTPMethod的拓展,添加supportsMultipart方法来判断,请求方式支不支持多种请求方式一起出现extension Method { /// A Boolean value determining whether the request supports multipart. public var supportsMultipart: Bool { switch self { case .post, .put, .patch, .connect: return true case .get, .delete, .head, .options, .trace: return false } } }requestNormal是MoyaProvider里request调的方法,方法里说明了,Moya在请求的时候到底做了什么func requestNormal(_ target: Target, callbackQueue: DispatchQueue?, progress: Moya.ProgressBlock?, completion: @escaping Moya.Completion) -> Cancellable { //获取endpoint、stubBehavior和初始化cancellableToken let endpoint = self.endpoint(target) let stubBehavior = self.stubClosure(target) //这个类控制是否取消请求任务 let cancellableToken = CancellableWrapper() // 允许插件修改 response let pluginsWithCompletion: Moya.Completion = { result in let processedResult = self.plugins.reduce(result) { $1.process($0, target: target) } completion(processedResult) } //是否追踪重复请求 if trackInflights { objc_sync_enter(self)//递归锁 var inflightCompletionBlocks = self.inflightRequests[endpoint] inflightCompletionBlocks?.append(pluginsWithCompletion) self.inflightRequests[endpoint] = inflightCompletionBlocks objc_sync_exit(self) if inflightCompletionBlocks != nil { // 如果存在,就是说明已经有一个已经重复的请求了,就把这个取消了 return cancellableToken } else { objc_sync_enter(self) // 如果不存在 key 为 endpoint 的值,则初始化一个 self.inflightRequests[endpoint] = [pluginsWithCompletion] objc_sync_exit(self) } } //字面上理解,就是真正执行请求的下一步了。这个闭包,是在 endpoint → URLRequest 方法执行完成后的闭包 let performNetworking = { (requestResult: Result<URLRequest, MoyaError>) in // 先判断这个请求是否取消,是则返回错误类型为 cancel 的错误提示数据 if cancellableToken.isCancelled { self.cancelCompletion(pluginsWithCompletion, target: target) return } var request: URLRequest! switch requestResult { case .success(let urlRequest): request = urlRequest case .failure(let error): pluginsWithCompletion(.failure(error)) return } // 允许插件修改 request let preparedRequest = self.plugins.reduce(request) { $1.prepare($0, target: target) } // 定义返回结果闭包,这里返回的是请求返回的数据映射成了 Result let networkCompletion: Moya.Completion = { result in if self.trackInflights { self.inflightRequests[endpoint]?.forEach { $0(result) } objc_sync_enter(self) self.inflightRequests.removeValue(forKey: endpoint) objc_sync_exit(self) } else { // 使用上面的闭包,通知所有插件,且返回结果 pluginsWithCompletion(result) } } // 这一步就是执行请求的下一步了,将所有参数继续传递 cancellableToken.innerCancellable = self.performRequest(target, request: preparedRequest, callbackQueue: callbackQueue, progress: progress, completion: networkCompletion, endpoint: endpoint, stubBehavior: stubBehavior) } // 接下去的就是将上面定义好的两个闭包,传入到 requestClosure 闭包中 requestClosure(endpoint, performNetworking) return cancellableToken }performRequest是上面执行请求的下一步,这个方法的内部实现,根据switch stubBehavior和endpoint.task来分别执行对应的请求方式。
private func performRequest(_ target: Target, request: URLRequest, callbackQueue: DispatchQueue?, progress: Moya.ProgressBlock?, completion: @escaping Moya.Completion, endpoint: Endpoint<Target>, stubBehavior: Moya.StubBehavior) -> Cancellable { switch stubBehavior { case .never: switch endpoint.task { case .requestPlain, .requestData, .requestJSONEncodable, .requestParameters, .requestCompositeData, .requestCompositeParameters: return self.sendRequest(target, request: request, callbackQueue: callbackQueue, progress: progress, completion: completion) case .uploadFile(let file): return self.sendUploadFile(target, request: request, callbackQueue: callbackQueue, file: file, progress: progress, completion: completion) case .uploadMultipart(let multipartBody), .uploadCompositeMultipart(let multipartBody, _): guard !multipartBody.isEmpty && endpoint.method.supportsMultipart else { fatalError("\(target) is not a multipart upload target.") } return self.sendUploadMultipart(target, request: request, callbackQueue: callbackQueue, multipartBody: multipartBody, progress: progress, completion: completion) case .downloadDestination(let destination), .downloadParameters(_, _, let destination): return self.sendDownloadRequest(target, request: request, callbackQueue: callbackQueue, destination: destination, progress: progress, completion: completion) } default: return self.stubRequest(target, request: request, callbackQueue: callbackQueue, completion: completion, endpoint: endpoint, stubBehavior: stubBehavior) } }
3.4.Endpoint
Endpoint的初始化方法public init(url: String, sampleResponseClosure: @escaping SampleResponseClosure, method: Moya.Method, task: Task, httpHeaderFields: [String: String]?) { self.url = url self.sampleResponseClosure = sampleResponseClosure self.method = method self.task = task self.httpHeaderFields = httpHeaderFieldsadding用于创建一个新的Endpoint的便利方法,其属性与接收方相同,但增加了HTTP请求头。open func adding(newHTTPHeaderFields: [String: String]) -> Endpoint<Target> { return Endpoint(url: url, sampleResponseClosure: sampleResponseClosure, method: method, task: task, httpHeaderFields: add(httpHeaderFields: newHTTPHeaderFields)) }replacing方法差不多,只是更换了TaskurlRequest()方法 转换Endpoint成URLRequest
3.5.TargetType
TargetType就是用于定义MoyaProvider的协议,自定义枚举需要签订的协议
public protocol TargetType { /// The target's base `URL`. var baseURL: URL { get } /// The path to be appended to `baseURL` to form the full `URL`. var path: String { get } /// The HTTP method used in the request. var method: Moya.Method { get } /// Provides stub data for use in testing. var sampleData: Data { get } /// The type of HTTP task to be performed. var task: Task { get } /// A Boolean value determining whether the embedded target performs Alamofire validation. Defaults to `false`. var validate: Bool { get } /// The headers to be used in the request. var headers: [String: String]? { get } }
3.6.Response
Response就是对对MoyaProvider.request的响应
Response的初始化public init(statusCode: Int, data: Data, request: URLRequest? = nil, response: HTTPURLResponse? = nil) { self.statusCode = statusCode //状态码 self.data = data //返回的二进制数据 self.request = request // URL 请求 self.response = response// http 的 response }除此之外,还有自带的
mapJSON和mapString用于将data转成JSON或者字符串,map<D: Decodable>自带转模型,可以讲数据转换为签订Decodable类的对象,mapImage在返回data数据为一个图片二进制数据时使用,直接转换成图片对象返回func mapImage() throws -> Image { guard let image = Image(data: data) else { throw MoyaError.imageMapping(self) } return image } func mapJSON(failsOnEmptyData: Bool = true) throws -> Any { do { return try JSONSerialization.jsonObject(with: data, options: .allowFragments) } catch { if data.count < 1 && !failsOnEmptyData { return NSNull() } throw MoyaError.jsonMapping(self) } } public func mapString(atKeyPath keyPath: String? = nil) throws -> String { if let keyPath = keyPath { // Key path was provided, try to parse string at key path guard let jsonDictionary = try mapJSON() as? NSDictionary, let string = jsonDictionary.value(forKeyPath: keyPath) as? String else { throw MoyaError.stringMapping(self) } return string } else { // Key path was not provided, parse entire response as string guard let string = String(data: data, encoding: .utf8) else { throw MoyaError.stringMapping(self) } return string } } func map<D: Decodable>(_ type: D.Type, atKeyPath keyPath: String? = nil, using decoder: JSONDecoder = JSONDecoder()) throws -> D { let serializeToData: (Any) throws -> Data? = { (jsonObject) in guard JSONSerialization.isValidJSONObject(jsonObject) else { return nil } do { return try JSONSerialization.data(withJSONObject: jsonObject) } catch { throw MoyaError.jsonMapping(self) } } let jsonData: Data if let keyPath = keyPath { guard let jsonObject = (try mapJSON() as? NSDictionary)?.value(forKeyPath: keyPath) else { throw MoyaError.jsonMapping(self) } if let data = try serializeToData(jsonObject) { jsonData = data } else { let wrappedJsonObject = ["value": jsonObject] let wrappedJsonData: Data if let data = try serializeToData(wrappedJsonObject) { wrappedJsonData = data } else { throw MoyaError.jsonMapping(self) } do { return try decoder.decode(DecodableWrapper<D>.self, from: wrappedJsonData).value } catch let error { throw MoyaError.objectMapping(error, self) } } } else { jsonData = data } do { return try decoder.decode(D.self, from: jsonData) } catch let error { throw MoyaError.objectMapping(error, self) } }
3.7.Plugin
Moya Plugin接收回调(调用时机都是在MoyaProvider+Internal里),以在发送或接收请求时执行。例如,一个插件可以用于
1.记录网络请求 2.隐藏和显示网络活动指示器 3.在请求中注入附加信息

prepare可以用来修改发送前的请求。(在 stub 测试之前)func prepare(_ request: URLRequest, target: TargetType) -> URLRequestwillSend在网络请求发送前调用(在 stub 测试之后)func willSend(_ request: RequestType, target: TargetType)didReceive在收到响应后,但在MoyaProvider调用其完成处理程序之前调用。func didReceive(_ result: Result<Moya.Response, MoyaError>, target: TargetType)process在completion前调用用来修改resultfunc process(_ result: Result<Moya.Response, MoyaError>, target: TargetType) -> Result<Moya.Response, MoyaError>RequestType是willSend需要传入的参数,它的设计遵循迪米特法则(最少知道),我们使用这个协议来代替Alamofire请求,以避免泄露这个抽象概念。Plugin应该是完全不知道Alamofire的public protocol RequestType { /// Retrieve an `NSURLRequest` representation. var request: URLRequest? { get } /// Authenticates the request with a username and password. func authenticate(user: String, password: String, persistence: URLCredential.Persistence) -> Self /// Authenticates the request with an `NSURLCredential` instance. func authenticate(usingCredential credential: URLCredential) -> Self }
3.8.AccessTokenPlugin
AccessTokenPlugin可用于做JWT的 Bearer 认证 和 Basic 认证,也可以做OAuth认证,不过比较麻烦
- 必要的时候通过
prepare添加授权请求头来验证public func prepare(_ request: URLRequest, target: TargetType) -> URLRequest { guard let authorizable = target as? AccessTokenAuthorizable else { return request } let authorizationType = authorizable.authorizationType var request = request switch authorizationType { case .basic, .bearer: //添加Authorization,tokenClosure返回一个可应用请求头上的access token let authValue = authorizationType.rawValue + " " + tokenClosure() request.addValue(authValue, forHTTPHeaderField: "Authorization") case .none: break } return request }
3.9.CredentialsPlugin
AccessTokenPlugin是做 HTTP 身份验证的,在 willSend里验证
public func willSend(_ request: RequestType, target: TargetType) { //credentialsClosure返回一个 URLCredential对象,用于身份验证的系统 api if let credentials = credentialsClosure(target) { //通过Moya+Alamofire的“extension Request: RequestType { }” 可知,这个authenticate方法最后还是调用的 Alamofire的的认证方法 _ = request.authenticate(usingCredential: credentials) } }
3.10.NetworkActivityPlugin
NetworkActivityPlugin还是比较简单的,就是单纯的抽离willSend和didReceive转变成NetworkActivityChangeType的began和ended,可以添加菊花的显示和隐藏
public func willSend(_ request: RequestType, target: TargetType) { networkActivityClosure(.began, target) } /// Called by the provider as soon as a response arrives, even if the request is canceled. public func didReceive(_ result: Result<Moya.Response, MoyaError>, target: TargetType) { networkActivityClosure(.ended, target) }
3.11.NetworkLoggerPlugin
NetworkLoggerPlugin是网络日志的打印,还是在willSend和didReceive打印了网络状态
public func willSend(_ request: RequestType, target: TargetType) { if let request = request as? CustomDebugStringConvertible, cURL { output(separator, terminator, request.debugDescription) return } outputItems(logNetworkRequest(request.request as URLRequest?)) } public func didReceive(_ result: Result<Moya.Response, MoyaError>, target: TargetType) { if case .success(let response) = result { outputItems(logNetworkResponse(response.response, data: response.data, target: target)) } else { outputItems(logNetworkResponse(nil, data: nil, target: target)) } }
文章链接:https://juejin.cn/post/
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/232467.html原文链接:https://javaforall.net
