😊😊😊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!


SesssionManager to SessionDelegate layering was introduced in the previous Alamofire- Background Download! Let me summarize and then start a very important Alamofire module, Request!

I. Summary of SessionManager

  • SesssionManager is the externally provided manager that has all the functionality of the entire Alamofire.

    • requestThe request,Download, upload, stream
    • Request header Settings and multiple form header Settings
    • sessionDirect external supply, convenient and free handling
    • Initialize exposure to facilitate free factory construction, such asURLSessionConfigurationConfiguration mode of
    • Retry and adapt requests
    • Closures provide externally:backgroundCompletionHandlerThe background download comes back and listens for the closure
    • One of the very important points is thatSesssionManager 把 SessionDelegate to a specialized class:SessionDelegate
  • SessionDelegate URLSessionDelegate, URLSessionTaskDelegate, URLSessionDataDelegate, URLSessionDownloadDelegate, Agent URLSessionStreamDelegate etc.

  • More interesting: SessionDelegate also provides external closures, meaning that all internal implementations of the delegate can be listened to externally. Of course, all external closures fall into two categories:

    • Add closure execution inside the original proxy callback.

  • Alternatively, if the proxy comes back, the lower proxy delivery is not performed and the external closure callback is performed

Conclusion:SesssionManagerResponsible for creation and managementRequestObjects and their underlying layersNSURLSession

First of all, I will post a very familiar picture to you. Understanding this picture will be very helpful for the following understanding of Request

  • As you can see from the picture above, our external module isSesssionManagerIt provides a lot of functionality to outside users
  • But the real implementers of this work are made up ofIOS, Android, front-end, back-end, testingImplementation of!
  • Which oneiOSFor module tasks, there areHomepage, Discovery, My, SDK, video....Modules need to be implemented, but our project manager may not even know what they are and how to implement them! All of them if all of themSesssionManagerTo implement, obviously coupling is too strong, and the task is messy, does not reflect the effect of a great project layered architecture. So in theiOSTask refinement andSesssionManagerIn between, there is a small manager, right: he knows the details and scheduling. Yes: He canSesssionManagerCoordinate and cooperate. That isRequest

Second, the Request

1. Request parameter encoding

  • First of all, I would like to explain that Alamofire supports coding formats. The specific format differences are easy to understand according to the name. If you really can’t understand, you can search and fill the gaps by yourself

    • URLEncoding
    • JSONEncoding
    • PropertyListEncoding
  • let encodedURLRequest = try encoding.encode(originalRequest! , with: parameters) also encode the request through this code

public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
    var urlRequest = try urlRequest.asURLRequest()

    guard let parameters = parameters else { return urlRequest }

    if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) {
        guard let url = urlRequest.url else {
            throw AFError.parameterEncodingFailed(reason: .missingURL)
        }

        if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
            let percentEncodedQuery = (urlComponents.percentEncodedQuery.map{$0 + "&"}????"") + query(parameters)
            urlComponents.percentEncodedQuery = percentEncodedQuery
            urlRequest.url = urlComponents.url
        }
    } else {
        if urlRequest.value(forHTTPHeaderField: "Content-Type") = =nil {
            urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
        }
        urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)}return urlRequest
}
Copy the code
  • Code to consult, here is a summary

  • First take out the request method, according to different request method, parameter encoding is different

    • .get, .head, .deleteThe three methods are to concatenate the parameters directly toURLbehind
    • Other requests through the request body (httpBody)
  • Since our request is ASCII encoded, the first step to percentage-coding is to percentage-code all routes for the current request

  • Parameter convenient coding, splicing out

private func query(_ parameters: [String: Any]) -> String {
    var components: [(String.String)] = []

    for key in parameters.keys.sorted(by: <) {
        let value = parameters[key]!
        components += queryComponents(fromKey: key, value: value)
    }
    return components.map { "\ [$0)=\ [$1)" }.joined(separator: "&")}Copy the code
  • throughASCIIYou sort it from small to large
  • queryComponentsThis method code is overly omitted.
    • I’m going to take recursive parameters,
    • theThe key and the valueTake it out, and then do a percent code.
    • Put into the tuple to save, forming a pair of parameters
  • Let’s add a tuple to an array
  • The map mapping($0) = \ ($1)
  • Type a separator between the elements&

Normal methods simply concatenate the URL, such as the POST method, which puts these encoded pairs into the request body. And you have to addContent-TypeThe request header

2. Request internal relationship review

After exploring the parameter encoding of one of the request’s tedious tasks, start analyzing the process inside: URL -> Request -> Task. This process also establishes the binding relationship between task and request

open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
    var originalRequest: URLRequest?
    do {
        originalRequest = try urlRequest.asURLRequest()
        let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
        let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
        let request = DataRequest(session: session, requestTask: .data(originalTask, task))

        delegate[task] = request
        if startRequestsImmediately { request.resume() }

        return request
    } catch {
        return request(originalRequest, failedWith: error)
    }
}
Copy the code
  • Internal createRequestableTo help those belowDataRequestcreateTask“, also a common saying, tasks are layered, and the architecture is clearer
  • The bindingThe task and the requestWill it be convenient for theSessionDelegateDeliver the task,taskDirect retrieval,requestConvenient direct access
  • Direct taskrequest.resume()Start the

  • RequestThe initialization takes advantage of the convenience of enumerations, constructs are created, and related information is saved

May be a lot of partners at this time will have doubts, you are not throughSessionDelegateImplementation proxy, why have one hereDataTaskDelegate

  • The first thing to understand is that the SessionDelegate is the delegate follower for all sessions, and any proxy will come and respond.

  • The DataTaskDelegate is the implementor of our specific tedious task, and all of the methods in the DataTaskDelegate are responded to by the SessionDelegate.

  • To put it bluntly, it is the relationship between the whole and the part

  • SessionDelegate is the general responder to events, but specific transactions should be assigned to specific people to execute. You can’t have all the code listed here just because the SessionDelegate gets all the responses. Obviously that would lead to confusion. 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.

  • There is also an important point about asynchrony: delegate[task] = request to give our SessionDelegate a handy ability to get in time

open func urlSession(
    _ session: URLSession,
    downloadTask: URLSessionDownloadTask,
    didFinishDownloadingTo location: URL)
{
  // Omit attribute closure callback
    if let delegate = self[downloadTask]? .delegateas? DownloadTaskDelegate {
        delegate.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location)
    }
}
Copy the code
  • This code typically passes through the binding we established earlier, through each proxy callbacktaskFind the correspondingRequestAnd then just pull it out because of the propertyRequestProperty delegate to handle related transactions
func urlSession(
    _ session: URLSession,
    downloadTask: URLSessionDownloadTask,
    didFinishDownloadingTo location: URL)
{
    temporaryURL = location

    guard
        let destination = destination,
        let response = downloadTask.response as? HTTPURLResponse
    else { return }

    let result = destination(location, response)
    let destinationURL = result.destinationURL
    let options = result.options

    self.destinationURL = destinationURL

    do {
        if options.contains(.removePreviousFile), FileManager.default.fileExists(atPath: destinationURL.path) {
            try FileManager.default.removeItem(at: destinationURL)
        }

        if options.contains(.createIntermediateDirectories) {
            let directory = destinationURL.deletingLastPathComponent()
            try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
        }
        try FileManager.default.moveItem(at: location, to: destinationURL)
    } catch {
        self.error = error
    }
}
Copy the code
  • Transfer storage address after file download, file processing related and error condition
  • The above is the most typical heavy disgusting operation, is certainly not need to beSessionDelegateAll you need to know!

The process from SessionManager -> Request is explicitly delivered directly, but implicitly via the delegate response: SessionDelegate -> the delegate of the specific Request. See here, don’t you feel very cool! A good framework will always give you a big boost in terms of thinking, and we often have ugly couplings and communication issues. Then surely the design idea of Alamofire can bring you certain enlightenment!

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