The definition of 1. Moya
Moya
Is a highly abstract network library, the idea is that you don’t have to worry about the low-level implementation details of network requests, just define the business you care about. andMoya
Use bridging and composition for encapsulation (bridging by defaultAlamofire
),Moya
It’s very extensible and you don’t have to modify itMoya
The source code can be easily customized. The official list is how manyMoya
Main advantages:- Compile time check
API endpoint
permissions - Let you define various differences using enumerations
Target
.endpoints
- the
stubs
Treat them like first-class citizens, so the test is super easy.
- Compile time check
2. Moya’s use
Moya
The use of an enumeration type is divided into several steps, starting with a custom enumeration type.
enum SHChannelViewModelApiManager{
case getChannelList(Bool)
case getItemList(String)
}
Copy the code
- Then follow the open close principle and let the classification of enumerations follow
Moya
theTargetType
, as defined by the implementation on demandget
methods
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 }
}
Copy the code
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
}
}
}
Copy the code
- Finally create
MoyaProvider
Object generics allow you to pass in whatever you define to followTargetType
Enumeration of protocols,
let provider = MoyaProvider<SHChannelViewModelApiManager>()
Copy the code
- use
MoyaProvider
Object initiates a request
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) }Copy the code
3. All file parsing of Moya
-
Provider
-
MoyaProvider
-
MoyaProvider+Defaults
-
MoyaProvider+Internal
-
Endpoint
-
Task (is an enumeration that defines the form of the Task, whether it is passed or not, whether it is uploaded or downloaded)
-
Cancellable (simply indicates whether the request can be cancelled, and a method to cancel the request)
-
-
TargetType
-
TargetType
-
MultiTarget (used to enable MoyaProvider to handle multiple targetTypes)
-
-
Result
-
Response
-
MoyaError (is an enumeration that defines all kinds of errors that Moya can throw, including the above mentioned three map methods error, status code error, decoding error, parameter encoding error, etc., and two get methods to return the error Response and error description return)
-
-
Plugins
-
Plugin
-
AccessTokenPlugin
-
NetworkActivityPlugin
-
NetworkLoggerPlugin
-
-
Alamofire
- Moya+Alamofire (
Moya
andAlamofire
Of the bridge files, the bridge mode guarantees the least known principle if replacement is madeAlamofire
, mainly modify this file) - MultipartFormData (multifile upload with
Alamofire
Bridge, throughappend
Methods such as themoya
In the form ofMultipartFormData
Added to theAlamofire
To)
- Moya+Alamofire (
-
URL
-
URL+Moya(initialize a URL by getting the baseURL and path of TargetType)
-
URLRequest+Encoding(two encoded methods for URLRequest)
-
3.1. MoyaProvider
MoyaProvider initialization
MoyaProvider is the request provider class. Requests can only be made through this class, which is initialized as follows
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
}
Copy the code
As you can see from the above code, initialization can pass in parameters.
-
EndpointClosure is a closure that converts the passed Target into an Endpoint object,
public typealias EndpointClosure = (Target) -> Endpoint<Target> Copy the code
In that case, let’s take a quick look at what’s inside the Endpoint object
Open Class Endpoint<Target> {public TypeAlias SampleResponseClosure = () -> EndpointSampleResponse Specifies the requested URL Let URL: String /// Stub data response(test) open let sampleResponseClosure: SampleResponseClosure // Open let method: Moya. 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 } ... }Copy the code
The EndpointClosure function allows you to re-customize network requests based on business requirements and test data using stubs. Take a look at the official default closure implementation
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 ) } Copy the code
-
The RequestClosure implementation converts the Endpoint to the actual request object URLRequest
//RequestClosure public typealias RequestClosure = (Endpoint<Target>, @escaping RequestResultClosure) -> Void // Above RequestResultClosure Public TypeAlias RequestResultClosure = (Result<URLRequest, MoyaError>) -> VoidCopy the code
Take a look at the default implementation provided by Moya
public final class func defaultRequestMapping(for endpoint: Endpoint<Target>, closure: RequestResultClosure) {do {//urlRequest let urlRequest = try endpoint.urlRequest() (.success(urlRequest)) // Closure (.success(urlRequest)) {//urlRequest request let urlRequest = try endpoint.urlRequest()) // Error of type 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))) } }Copy the code
This code gets urlRequest via endpoint.urlRequest(), so let’s look at the implementation of 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 //................. A little code omitted here}}Copy the code
Now it’s clear that it did turn into URLRequest. In fact, this is Moya’s last chance for you, for example, if you want to set a timeout,
let requestClosure = { (endpoint:Endpoint<SHChannelViewModelApiManager>,closure:RequestResultClosure){ do { var UrlRequest = try endpoint.urlRequest() // Set timeout, UrlRequest configurable items can be in this configuration urlRequest timeoutInterval = 60 closure (. Success (urlRequest)} the 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))) } } }Copy the code
-
StubClosure returns an enumeration of StubBehaviors that lets you tell Moya whether or how to use stubs to return data. The default is none
public typealias StubClosure = (Target) -> Moya.StubBehavior Copy the code
Public enum StubBehavior {/// Data returned without Stub case Never /// Data returned with Stub case immediate /// Data returned with Stub after a period of time Case delayed(seconds: TimeInterval) }Copy the code
Here’s an example to summarize the use of these three closures
var sampleData: Data { return "{'code': 0,'Token':'3764837egfdg8dfg8e93hr93'}".data(using: String.Encoding.utf8)! } / / defined in SHChannelViewModelApiManager let outside 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)} SHChannelViewModelApiManager) -> Moya.StubBehavior = { type in return Moya.StubBehavior.delayed(seconds: 3)} / / create moyaProvider let moyaProvider = moyaProvider < SHChannelViewModelApiManager > (endpointClosure: EndPointAction stubClosure: stubAction) / / use moyaProvider. Request (SHChannelViewModelApiManager. GetChannelList)......Copy the code
-
Manager is nothing to say, is Alamofire SessionManager
public typealias Manager = Alamofire.SessionManager Copy the code
Manager is the real class for network requests. Moya does not provide the Manager class itself. Moya simply Bridges other network request classes (bridge mode). This is done so that callers can easily customize and replace the library of network requests. If you don’t want to use Alamofire, you can simply switch to another library,
-
CallbackQueue is passed to Alamofire as a callbackQueue, if nil – Alamofire’s default will be used. Here is an example of the use found
if let callbackQueue = callbackQueue { callbackQueue.async(execute: sendProgress) } else { sendProgress() } Copy the code
-
Plugins-moya provides a plug-in mechanism that allows us to build our own plug-in classes to do additional things. For example, write Log, display “chrysanthemum” and so on. The purpose of pulling out the Plugin layer is to keep the Provider accountable and open and closed. Disconnect from behaviors that don’t relate to your network. Avoiding all sorts of business clumping together is bad for scaling (more like the lifecycle of a request, called at the insertion point)
-
TrackInflights, according to the code logic can be seen, this is whether to handle repeated requests. One explanation is whether to track repeated network requests.
If trackInflights {objc_sync_enter(self)// Recursive lock var inflightCompletionBlocks = self.inflightrequests [endpoint] inflightCompletionBlocks? .append(pluginsWithCompletion) self.inflightRequests[endpoint] = inflightCompletionBlocks objc_sync_exit(self) if inflightCompletionBlocks ! = nil {// If it exists, Return cancellableToken} else {objc_sync_enter(self) // If there is no value where key is an endpoint, Initializing a self.inflightrequests [endpoint] = [pluginsWithCompletion] objc_sync_exit(self)}}Copy the code
A request with trackInflights set to true when init will store the endpoint of the request in Moya. When the data is returned, if a duplicate request needs to be traced, the data returned by the request is actually sent once and returned multiple times.
MoyaProvider sends requests
-
The request method in Moya is a unified request entry. Only need to configure the required parameters in the method, including the need to generate the request address, request parameters through enumeration type, very clear classification and management. Use the. Syntax to generate the corresponding enumeration, and then generate the endpoint, URLRequest, and so on
@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) } Copy the code
-
Target is the custom enumeration passed in.
-
CallbackQueue ditto
-
Progress represents a callback to request the completion progress of the task. It is not used by default
public typealias ProgressBlock = (_ progress: ProgressResponse) -> Void Copy the code
So let’s go back to 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 whether the request is completed. Public var completed: Bool {return progress == 1.0&&response! = nil } }Copy the code
ProgressObject is a Progress object from the Foundation framework. It is a class added to iOS 7 specifically for monitoring the Progress of tasks
-
Completion is the callback that is returned after the request is completed
public typealias Completion = (_ result: Result<Moya.Response, MoyaError>) -> Void Copy the code
3.2. MoyaProvider + Defaults
There are three default methods in MoyaProvider+Defaults
defaultEndpointMapping
returnEndpoint
Default method ofdefaultRequestMapping
The essence is to returnURLRequest
Default method ofdefaultAlamofireManager
Return to the network librarymanager
Default method (default is Alamofire)
3.3. MoyaProvider + Internal
Method
Is theAlamofire.HTTPMethod
Extension, addsupportsMultipart
Method to determine whether multiple request modes are supported togetherextension 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 } } }Copy the code
requestNormal
isMoyaProvider
In therequest
The method of tuning, it says in the method,Moya
What exactly was done at the time of the requestfunc requestNormal(_ target: Target, callbackQueue: DispatchQueue? , progress: Moya.ProgressBlock? , completion: @escaping Moya.Completion) -> Cancellable {// Get the endpoint, stubBehavior, and initialize cancellableToken let endpoint = Self. endpoint(target) let stubBehavior = self.stubClosure(target) // This class controls whether to cancel the request task let cancellableToken = CancellableWrapper() // Allow plugins to modify 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)// Recursive lock var inflightCompletionBlocks = self.inflightRequests[endpoint] inflightCompletionBlocks? .append(pluginsWithCompletion) self.inflightRequests[endpoint] = inflightCompletionBlocks objc_sync_exit(self) if inflightCompletionBlocks ! = nil {// If it exists, Return cancellableToken} else {objc_sync_enter(self) // If there is no value where key is an endpoint, Initializing self.inflightrequests [endpoint] = [pluginsWithCompletion] objc_sync_exit(self)}} Let performNetworking = {(requestResult: Result<URLRequest, MoyaError>) in // Is return the wrong type for the cancel the error data of the if cancellableToken. IsCancelled {self. CancelCompletion (pluginsWithCompletion, target: target) return } var request: URLRequest! switch requestResult { case .success(let urlRequest): request = urlRequest case .failure(let error): Request let preparedRequest = self.plugins.reduce(request) {pluginsWithCompletion(.failure(error)) return} $1. Prepare ($0, target: target)} // Define a closure that maps the data to Result let networkCompletion: Moya.Completion = { result in if self.trackInflights { self.inflightRequests[endpoint]? .forEach { $0(result) } objc_sync_enter(self) self.inflightRequests.removeValue(forKey: PluginsWithCompletion (result)}} This step is the next step in executing the request. Will continue to pass all the parameters cancellableToken. InnerCancellable = self. PerformRequest (target, request: preparedRequest, callbackQueue: callbackQueue, progress: progress, completion: networkCompletion, endpoint: endpoint, stubBehavior: } // The next step is to use the two closures defined above, RequestClosure (endpoint, performNetworking) return cancellableToken}Copy the code
performRequest
Is the next step in executing the request above, the internal implementation of this method, according toswitch stubBehavior
和endpoint.task
To perform the corresponding request mode separately.
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) } }Copy the code
3.4. The Endpoint
Endpoint
The initialization method ofpublic 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 Copy the code
adding
Use to create a new oneEndpoint
Is a convenience method that has the same properties as the recipient, but adds an HTTP request header.open func adding(newHTTPHeaderFields: [String: String]) -> Endpoint<Target> { return Endpoint(url: url, sampleResponseClosure: sampleResponseClosure, method: method, task: task, httpHeaderFields: add(httpHeaderFields: newHTTPHeaderFields)) } Copy the code
replacing
The method is the same, but changedTask
urlRequest()
Methods conversionEndpoint
intoURLRequest
3.5. TargetType
TargetType is the protocol used to define the MoyaProvider. Custom enumerations need to be signed
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 }
}
Copy the code
3.6. The Response
A Response is a Response to MoyaProvider. Request
Response
The initializationpublic init(statusCode: Int, data: Data, request: URLRequest? = nil, response: HTTPURLResponse? = nil) {self.statuscode = statusCode // statusCode self.data = data // binary data returned self.request = request // URL request self.response = Response // HTTP response}Copy the code
In addition, there is a built-in
mapJSON
andmapString
Is used todata
intoJSON
Or a string,map<D: Decodable>
With the model, you can convert data to signDecodable
Class object,mapImage
On his return todata
Data is used when a picture binary data is returned directly into a picture objectfunc 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) } }Copy the code
3.7. The Plugin
The Moya Plugin receives callbacks (all within MoyaProvider+Internal) to be executed when a request is sent or received. For example, a plug-in can be used for
1. Log network requests 2. Hide and display network activity indicators 3Copy the code
-
Prepare can be used to modify the request before it is sent. (Before stub test)
func prepare(_ request: URLRequest, target: TargetType) -> URLRequest Copy the code
-
WillSend is called before a network request is sent (after a stub test)
func willSend(_ request: RequestType, target: TargetType) Copy the code
-
DidReceive is called after receiving the response, but before MoyaProvider calls its completion handler.
func didReceive(_ result: Result<Moya.Response, MoyaError>, target: TargetType) Copy the code
-
Process is called before Completion to modify the result
func process(_ result: Result<Moya.Response, MoyaError>, target: TargetType) -> Result<Moya.Response, MoyaError> Copy the code
-
RequestType is the parameter that willSend needs to pass in. It is designed to follow Demeter’s rule (least known), and we use this protocol instead of Alamofire requests to avoid giving away this abstraction. Plugin should be completely unaware of 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 } Copy the code
3.8. AccessTokenPlugin
AccessTokenPlugin can be used for Bearer and Basic authentication of JWT as well as OAuth authentication, but it is more difficult
- Pass when necessary
prepare
Add authorization request headers to verifypublic 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: // Add Authorization, TokenClosure returns a head can apply request access token let authValue = authorizationType. RawValue + "" + tokenClosure () request.addValue(authValue, forHTTPHeaderField: "Authorization") case .none: break } return request }Copy the code
3.9. CredentialsPlugin
AccessTokenPlugin does HTTP authentication in willSend
public func willSend(_ request: RequestType, target: TargetType) {//credentialsClosure returns an URLCredential object, If let credentials = credentialsClosure(target) {// Through Moya+Alamofire "Extension Request: RequestType {} "Authenticate (usingCredential: credentials)}}Copy the code
3.10.Net workActivityPlugin
NetworkActivityPlugin is relatively simple, it is a simple willSend and didReceive into NetworkActivityChangeType began and ended, can add the chrysanthemum show and hide
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)
}
Copy the code
3.11.Net workLoggerPlugin
NetworkLoggerPlugin prints network logs or network state in willSend and 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))
}
}
Copy the code