😊😊😊Alamofire thematic directory. Welcome timely feedback and exchange

  • Alamofire (1) – URLSession prerequisite skill
  • Alamofire (2) — Background download
  • Alamofire (3) — Request
  • Alamofire (4) — Details you need to know
  • Alamofire (5) — Response
  • Alamofire (6) — Multiple form uploads
  • Alamofire (7) — Safety certification
  • Alamofire (8) — Final Chapter (Network Monitoring & Notifications & Downloader Packaging)

Alamofire Directory through train — Harmonious learning, not impatient!


In the last chapter, we explained that SessionDelegate is the total responder of the event. Depending on the requirements (DataTaskDelegate, DownloadTaskDelegate, UploadTaskDelegate, TaskDelegate), Respond to the general agent and then give professional people to do professional things according to different needs. The coupling is greatly reduced and the architecture is more layered! In this chapter I’m going to introduce Alamofire to some very useful little details that will help you in your future development

SessionDelegate external closure

Our SessionDelegate is not only a generic name for the agent, but also a powerful outlet for our external logic, providing a plethora of closures ~😬 for our agent responses

// Accept the challenge callback
open var sessionDidReceiveChallengeWithCompletion: ((URLSession.URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition.URLCredential?). ->Void) - >Void)?
// The callback closure for the completion of the background event
open var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) - >Void)?
// Task completion closure
open var taskDidComplete: ((URLSession.URLSessionTask.Error?). ->Void)?
// Download the read/write progress closure
open var downloadTaskDidWriteData: ((URLSession.URLSessionDownloadTask.Int64.Int64.Int64) - >Void)?
// The closure that received the event task data
open var dataTaskDidReceiveData: ((URLSession.URLSessionDataTask.Data) - >Void)?
// The closure that received the response
open var dataTaskDidReceiveResponse: ((URLSession.URLSessionDataTask.URLResponse) - >URLSession.ResponseDisposition)?
Copy the code
  • The closures listed above are just a few, but you can get a good sense of what they do by looking at their names
  • Here’s an example 🌰
/ / create request
SessionManager.default.request(urlStr)
// Listen for task callback completion status
SessionManager.default.delegate.taskDidComplete = { (session,task,error) in
    print("Mission accomplished.")}// The logic behind it
open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
    /// Executed after it is determined that the request is not going to be retried
    let completeTask: (URLSession.URLSessionTask.Error?). ->Void= {[weak self] session, task, error in
        guard let strongSelf = self else { return }
        // The callback completes the closurestrongSelf.taskDidComplete? (session, task, error) }// Other logic omit...
}
Copy the code
  • You can see that we can go straight fromSessionManager.default.delegateDirectly on thetaskDidCompleteClosure declaration of
  • You can actually see it in theSessionDelegateIs executed in the proxy responsetaskDidCompleteclosure
  • The essence of this external closure is to provide external capabilities to make developers more comfortable and convenient

Dynamic adaptation capability – RequestAdapter

This feature is particularly useful and provides two capabilities.

  • 1: the request processing
  • 2: Redirects request

So let’s start playing with this adaptation

class LGAdapter: RequestAdapter{
    func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
        // token
        // 1: request processing
        // 2: Request redirection
        var request = urlRequest
        request.setValue("lgcoociToken", forHTTPHeaderField: "lgtoken")
        let newUrlRequest = URLRequest.init(url: URL(string: "http://www.douban.com/j/app/radio/channels")!return newUrlRequest
    }
}
Copy the code
  • implementationRequestAdapterOf the agreementadaptmethods
  • To provide theurlRequestFor example, unified configurationtoken
  • righturlRequestRedirect and get a new onerequestThe request.
  • Remember to configure:SessionManager.default.adapter = LGAdapter()

3. Custom validation

We request network habitual response status code more than 200 is correct, in fact, we can according to the characteristics of our own company customized processing verification operation, more in line with the actual development

SessionManager.default.request(urlStr, method: .get, parameters: ["username":"Kody"."password":"888888"])
    .response { (response) in
        debugPrint(response)
    }.validate { (request, response, data) -> Request.ValidationResult in
        guard let _ = data else{
            return .failure(NSError.init(domain: "lgcooci", code: 10089, userInfo: nil))}let code = response.statusCode
        if code == 404 {
            return .failure(NSError.init(domain: "lgcooci", code: 100800, userInfo: nil))}return .success
}
Copy the code
  • So in this code we’re going to add in the chainvalidatemethods
  • Add your own validation method to the closure
  • Such as noData from the dataI will return10089error
  • Status code == 404When we return100800error
  • Other returns succeeded. Custom validation is handled according to your specific needs, againAlamofireThe flexible

Retry the request

  • The operation of retry request may not be used much in development, but I think it is also a requirement scenario. And as an analogy, this is a very good place to talk about it
if let retrier = retrier, let error = error {
    retrier.should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, timeDelay in
        guard shouldRetry else { completeTask(session, task, error) ; return }

        DispatchQueue.utility.after(timeDelay) { [weak self] in
            guard let strongSelf = self else { return }

            letretrySucceeded = strongSelf.sessionManager? .retry(request) ??false

            if retrySucceeded, let task = request.task {
                strongSelf[task] = request
                return
            } else {
                completeTask(session, task, error)
            }
        }
    }
}
Copy the code
  • whenSessionDelegateWhen completing the request, determine whether the retry closure exists and note that it must be an error, without which there is no need to reconnect. It’s also revealed hereretriercollocationvalidateMore beautiful oh
  • retrierAlso is the inheritance protocol processing mode, operation referenceadapter
extension LGAdapter: RequestRetrier{
    func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
        print("manager = \(manager)")
        print("request = \(request)")
        print("error = \(error)")
        completion(true.1)
        // There must be an exit, otherwise continuous recursion will have serious effects
        completion(false.0)}}Copy the code
  • implementationRequestRetrierOf the agreementshould
  • Use:SessionManager.default.retrier = LGAdapter().

Fifth, the Result

Alamofire will have a Response when requesting data, but this is not our final result, we still need to go through a layer of serialization.

public func response<T: DataResponseSerializerProtocol>(
    queue: DispatchQueue? = nil,
    responseSerializer: T,
    completionHandler: @escaping (DataResponse<T.SerializedObject>) -> Void) - >Self
{
    delegate.queue.addOperation {
        let result = responseSerializer.serializeResponse(
            self.request,
            self.response,
            self.delegate.data,
            self.delegate.error
        )

        var dataResponse = DataResponse<T.SerializedObject>(
            request: self.request,
            response: self.response,
            data: self.delegate.data,
            result: result,
            timeline: self.timeline
        )
    }
    return self
}
Copy the code
  • resultIs passedresponseSerializer.serializeResponseThe result of the serialization process
  • resultThe result of is finally passed indataResponse
public enum Result<Value> {
    case success(Value)
    case failure(Error)
    
   // Provide success and failure verification
    public var isSuccess: Bool {... }
    public var isFailure: Bool{... }public var value: Value? {... }public var error: Error? {... }}Copy the code
  • The result is only successes and failures, designed as enumerations,SwiftThe enumeration is very powerful πŸ€™πŸ€™πŸ€™, and this place is also represented
  • Of course, Result is implemented in order to print more detailed informationCustomStringConvertibleandCustomDebugStringConvertibleProtocol:
extension Result: CustomStringConvertible {
    public var description: String {
       // return "SUCCESS" and "FAILURE"}}extension Result: CustomDebugStringConvertible {
    public var debugDescription: String {
        // The identity is returned along with the specific content}}Copy the code
  • There are many other methods of extension below, not the focus of everyone to see OK

Timeline

In order to facilitate our development, the powerful AlamofireπŸ‘πŸ‘πŸ‘ also provides us with the Timeline. We can quickly get the time data of this request through the Timeline, so as to judge whether the request is reasonable and whether we need to optimize….

timeline: Timeline: { 
"Request Start Time": 588099247.070."Initial Response Time": 588099272.474."Request Completed Time": 588099272.475."Serialization Completed Time": 588099272.475."Latency": 25.404 secs, 
"Request Duration": 25.405 secs, 
"Serialization Duration": 0.000 secs, 
"Total Duration": 25.405 secs 
 }
Copy the code
  • The time line data comes out, and here we haveAlamofireA queue is designed
self.queue = {
    let operationQueue = OperationQueue()

    operationQueue.maxConcurrentOperationCount = 1
    operationQueue.isSuspended = true
    operationQueue.qualityOfService = .utility

    return operationQueue
}()
Copy the code
  • Synchronize queues to allow processes to execute sequentially.
  • The current queue is suspended at initial initializationoperationQueue.isSuspended = true
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
   // omit extraneous code for easy reading
   // The request is completed
   queue.isSuspended = false
}
Copy the code
  • Request completed, queueresume
  • See here also shows that the task added to the queue must be done after the request completes

1: request start time

open func resume(a) {
    if startTime == nil { startTime = CFAbsoluteTimeGetCurrent()}}Copy the code

2: Adds the request completion time record

init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
    self.session = session
       // omit extraneous code for easy reading
    delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent()}}Copy the code

3: initialize the response time

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
    if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent()}}Copy the code

4: Timeline Settings

var timeline: Timeline {
    let requestStartTime = self.startTime ?? CFAbsoluteTimeGetCurrent(a)let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent(a)let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime

    return Timeline(
        requestStartTime: requestStartTime,
        initialResponseTime: initialResponseTime,
        requestCompletedTime: requestCompletedTime,
        serializationCompletedTime: CFAbsoluteTimeGetCurrent()}Copy the code

5: Initializes the recording time and calculation time

public init(
    requestStartTime: CFAbsoluteTime = 0.0,
    initialResponseTime: CFAbsoluteTime = 0.0,
    requestCompletedTime: CFAbsoluteTime = 0.0,
    serializationCompletedTime: CFAbsoluteTime = 0.0)
{
    self.requestStartTime = requestStartTime
    self.initialResponseTime = initialResponseTime
    self.requestCompletedTime = requestCompletedTime
    self.serializationCompletedTime = serializationCompletedTime

    self.latency = initialResponseTime - requestStartTime
    self.requestDuration = requestCompletedTime - requestStartTime
    self.serializationDuration = serializationCompletedTime - requestCompletedTime
    self.totalDuration = serializationCompletedTime - requestStartTime
}
Copy the code
  • You can see why these times can be recorded here, because the current time is kept at some core point through queue synchronization control! Such as:endTime
  • There are also some key core times like:startTime , initialResponseTimeIs set in the related proxy!
  • If no value is set, the current time is reset at the time of the call
  • TimelineThe rest of the time is through knowninitialResponseTime ε’Œ requestStartTime,requestCompletedTime 、serializationCompletedTimeCalculate it!

That’s all for this chapter. Accidentally 01:40 again! Although this point is sent out, few people will see πŸ™πŸ™πŸ™. However, I hope to support my friend πŸ‘¬, and wake up naturally to receive article push notifications from Cooci! Everything is so good 😸😸😸, tonight to sleep, looking forward to the dream after the struggle, add ~~~~~~, listen, sing

Just ask who else is there right now? 45 degrees up in the sky, damn it! My charm with no place to put it!