Moya
是一个高度抽象的网络库,他的理念是让你不用关心网络请求的底层的实现细节,只用定义你关心的业务。且Moya
采用桥接和组合来进行封装(默认桥接了Alamofire
),使得Moya
非常好扩展,让你不用修改Moya
源码就可以轻易定制。官方给出几个Moya
主要优点:API endpoint
权限Target
, endpoints
stubs
当做一等公民对待,因此测试超级简单。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)
}
Provider
Task (是一个枚举,定义了任务的形式,是传参呢还是啥都不传了,是上传了还是下载任务)
Cancellable (只是标明了请求是否可以被取消,和一个取消请求的方法)
TargetType
MultiTarget (MultiTarget
用于使MoyaProvider
能够处理多个TargetType
)
Result
MoyaError (是一个枚举,定义了 Moya
可能抛出的各种错误,包括上面说的 Response
三个map
方法出错,状态码出错,解码出错,和参数编码错误等,同时有两个get
方法,得出错误的Response
返回以及错误描述返回)
Plugins
Alamofire
这里有一个iOS交流圈:891 488 181 分享BAT,阿里面试题、面试经验,讨论技术, 感兴趣的话大家一起交流学习!
Moya
和Alamofire
的桥接文件,桥接模式保证了最少知道原则,如果进行替换Alamofire
,主要修改这个文件)Alamofire
的桥接,通过 append
等方法把moya
形式的MultipartFormData
添加到Alamofire
里去)URL
URL+Moya(通过获取TargetType
的baseURL
和path
初始化一个URL
)
URLRequest+Encoding(对URLRequest
的两个encoded
方法)
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.StubBehavior
public enum StubBehavior {
/// 不使用Stub返回数据.
case never
/// 立即使用Stub返回数据
case immediate
/// 一段时间间隔后使用Stub返回的数据.
case delayed(seconds: TimeInterval)
}
下面用个例子来总结一下这三个闭包的用法
var sampleData: Data {
return "{'code': 0,'Token':'3764837egfdg8dfg8e93hr93'}".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
的SessionManager
public typealias Manager = Alamofire.SessionManager
Manager
是真正用来网络请求的类,Moya
自己并不提供Manage
r类,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
。在返回数据的时候,如果需要跟踪了重复请求,那么就将一次实际发送请求返回的数据,多次返回。
在 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
MoyaProvider+Defaults
里面就是 3 个默认方法,前面已经提到过,就不多做赘述了
defaultEndpointMapping
返回 Endpoint
的默认方法defaultRequestMapping
本质是返回URLRequest
的默认方法defaultAlamofireManager
返回网络库的manager
的默认方法(默认是Alamofire)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)
}
}
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 = httpHeaderFields
adding
用于创建一个新的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
方法差不多,只是更换了Task
urlRequest()
方法 转换Endpoint
成URLRequest
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 }
}
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)
}
}
Moya Plugin
接收回调(调用时机都是在MoyaProvider+Internal
里),以在发送或接收请求时执行。例如,一个插件可以用于
1.记录网络请求
2.隐藏和显示网络活动指示器
3.在请求中注入附加信息
prepare
可以用来修改发送前的请求。(在 stub 测试之前)
func prepare(_ request: URLRequest, target: TargetType) -> URLRequest
willSend
在网络请求发送前调用(在 stub 测试之后)
func willSend(_ request: RequestType, target: TargetType)
didReceive
在收到响应后,但在MoyaProvider
调用其完成处理程序之前调用。
func didReceive(_ result: Result<Moya.Response, MoyaError>, target: TargetType)
process
在 completion
前调用用来修改result
func 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
}
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
}
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)
}
}
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)
}
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/6899362830114357256