Forward navigation:

Alamofire source learning directory collection

The Request base class cannot be used directly, but instead uses its four subclasses:

  1. DataRequest
    • Data is requested using URLSessionDataTask and stored in memory using Data objects
  2. DataStreamRequest
    • Use URLSessionDataTask to request data and OutputStream to send data
  3. DownloadRequest
    • Use the URLSessionDownloadTask to download data and files to disk
  4. UploadRequest
    • Upload requests using URLSessionUploadTask. Upload content using forms, files, InputStream

These four subclasses simply define initialization and the necessary methods, which are extended in ResponseSerialization to parse and process data usage

DataRequest

DataRequest subclass is one of the most basic subclasses, request to the Data using the Data object is written in the memory, suitable for general request, request small pictures, small file request

public class DataRequest: Request {
    /// The protocol object used to create URLRequest objects
    public let convertible: URLRequestConvertible
    /// open to external computations
    public var data: Data? { mutableData }

    /// a private thread-safe Data object that holds requested Data
    @Protected
    private var mutableData: Data? = nil

    /// The initialization method has one more argument than the parent: URLRequestConvertible, used to initialize the URLRequest object
    init(id: UUID = UUID(),
         convertible: URLRequestConvertible.underlyingQueue: DispatchQueue.serializationQueue: DispatchQueue.eventMonitor: EventMonitor? .interceptor: RequestInterceptor? .delegate: RequestDelegate) {
        self.convertible = convertible

        super.init(id: id,
                   underlyingQueue: underlyingQueue,
                   serializationQueue: serializationQueue,
                   eventMonitor: eventMonitor,
                   interceptor: interceptor,
                   delegate: delegate)
    }

    // reset the request to delete the downloaded data
    override func reset(a) {
        super.reset()

        mutableData = nil
    }

    /// Called when `Data` is received by this instance.
    ///
    /// - Note: Also calls `updateDownloadProgress`.
    ///
    /// - Parameter data: The `Data` received.
    /// When the task receives data, the SessionDelegate is called to save the data
    func didReceive(data: Data) {
        if self.data = = nil {
            / / the initial
            mutableData = data
        } else {
            / / append
            $mutableData.write { $0?.append(data) }
        }
        // Update the download progress
        updateDownloadProgress()
    }
    /// the parent method that must be implemented to return the Task corresponding to the DataRequest
    override func task(for request: URLRequest.using session: URLSession) -> URLSessionTask {
        // URLRequest is a struct in swift, so use assignment to copy it
        let copiedRequest = request
        return session.dataTask(with: copiedRequest)
    }

    /// Update download progress (downloaded bytes/to be downloaded bytes)
    func updateDownloadProgress(a) {
        let totalBytesReceived = Int64(data?.count ?? 0)
        let totalBytesExpected = task?.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown

        downloadProgress.totalUnitCount = totalBytesExpected
        downloadProgress.completedUnitCount = totalBytesReceived
        Call the progress callback in the progress callback queue
        downloadProgressHandler?.queue.async { self.downloadProgressHandler?.handler(self.downloadProgress) }
    }

    /// Validates the request, using the specified closure.
    ///
    /// - Note: If validation fails, subsequent calls to response handlers will have an associated error.
    ///
    /// - Parameter validation: `Validation` closure used to validate the response.
    ///
    /// - Returns: The instance.
    / / / add validity judgment callback, which is used to determine the current URLRequest, URLSessionTask, download the Data is valid
    @discardableResult
    public func validate(_ validation: @escaping Validation) -> Self {
        // Create a no-argument processing closure
        let validator: () -> Void ={[unowned self] in
            // There must be no errors, there must be responses
            guard self.error = = nil.let response = self.response else { return }
            // Call the judgment callback
            let result = validation(self.request, response, self.data)
            // If there is an error, set it to error
            if case let .failure(error) = result {
                self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error)))
            }
            // Notify the listener that validation is complete
            self.eventMonitor?.request(self,
                                       didValidateRequest: self.request,
                                       response: response,
                                       data: self.data,
                                       withResult: result)
        }
        // Add closure to array
        $validators.write { $0.append(validator) }

        return self}}Copy the code

DataStreamRequest

DataStreamRequest is similar to DataRequest, except that instead of using a single Data to store all Data, DataStreamRequest encapsulates the received Data into datastreamRequest. Stream types (including Data, done, and errors). It holds N closures for processing the data stream, encapsulates the data when requested and uses the closure to hand it to the caller for processing.

  • When parsing DataStreamRequest in ResponseSerialization, a callback that encapsulates Data into DataStreamRequest.Stream type is created and saved. When Data is received, all callbacks are called one by one. A callback is removed each time a Stream is processed, and when all processing is complete, the call completes the callback.
  • A pair of IOStreams can be created. If you choose to create an IOStream, the request will be sent immediately and the caller will get an InputStream. DataStreamRequest uses the corresponding OutputStream to write data out of the DataStreamRequest, and the upper layer uses the InputStream to receive data
  • Because Stream processing is used, Data is not written in memory. The upper layer can convert Data to memory as required, or directly write Data to disk for storage.
/// `Request` subclass which streams HTTP response `Data` through a `Handler` closure.
public final class DataStreamRequest: Request {
    /// Define the callback that the caller uses to handle the Stream, which can throw an error, and can control whether the request is cancelled when the error is thrown
    public typealias Handler<Success.Failure: Error> = (Stream<Success.Failure>) throws -> Void

    /// encapsulates the data stream object + cancel token, which is used for upper-layer processing data
    public struct Stream<Success.Failure: Error> {
        // The latest stream event (data or error)
        public let event: Event<Success.Failure>
        /// The token used to cancel the data stream encapsulates the DataStreamRequest object with a cancel method
        public let token: CancellationToken

        // cancel the data stream (cancel the request)
        public func cancel(a) {
            token.cancel()
        }
    }

    /// encapsulates the data stream, including: data, error, complete three kinds
    public enum Event<Success.Failure: Error> {
        /// Data or error
        case stream(Result<Success.Failure>)
        Request completed, request cancelled, request error)
        case complete(Completion)}/// The data carried in the Event when the data flow completes
    public struct Completion {
        /// The last request made
        public let request: URLRequest?
        /// The last response received
        public let response: HTTPURLResponse?
        /// Last received request indicator
        public let metrics: URLSessionTaskMetrics?
        /// Data flow error error
        public let error: AFError?
    }

    // cancel the request token when used to cancel the data stream
    public struct CancellationToken {
        weak var request: DataStreamRequest?

        init(_ request: DataStreamRequest) {
            self.request = request
        }

        // cancel the request directly when canceling the data stream
        public func cancel(a) {
            request?.cancel()
        }
    }

    /// used to create
    public let convertible: URLRequestConvertible
    // Whether to cancel the request directly if the data stream fails to parse. The default value is false
    public let automaticallyCancelOnStreamError: Bool

    // wrap some data that requires thread-safe operation
    struct StreamMutableState {
        // When DataStreamRequest is used as an InputStream, this object is created and the output stream is bound to the InputStream
        var outputStream: OutputStream?
        /// The Stream is a callback object, and the Stream is a callback object, and the Stream is a callback object
        var streams: [(_ data: Data) - >Void] = []
        /// The number of streams currently being executed
        var numberOfExecutingStreams = 0
        /// Hold the array that completed the callback while the data flow is still incomplete
        var enqueuedCompletionEvents: [() -> Void] =[]}@Protected
    var streamMutableState = StreamMutableState(a)1.URLRequestConvertible, 2. Whether to cancel the request if the Stream processing fails
    init(id: UUID = UUID(),
         convertible: URLRequestConvertible.automaticallyCancelOnStreamError: Bool.underlyingQueue: DispatchQueue.serializationQueue: DispatchQueue.eventMonitor: EventMonitor? .interceptor: RequestInterceptor? .delegate: RequestDelegate) {
        self.convertible = convertible
        self.automaticallyCancelOnStreamError = automaticallyCancelOnStreamError

        super.init(id: id,
                   underlyingQueue: underlyingQueue,
                   serializationQueue: serializationQueue,
                   eventMonitor: eventMonitor,
                   interceptor: interceptor,
                   delegate: delegate)
    }

    /// Task is also URLSessionDataTask
    override func task(for request: URLRequest.using session: URLSession) -> URLSessionTask {
        let copiedRequest = request
        return session.dataTask(with: copiedRequest)
    }

    // Close OutputStream when done
    override func finish(error: AFError? = nil) {
        $streamMutableState.write { state in
            state.outputStream?.close()
        }

        super.finish(error: error)
    }
    /// Receive Data processing
    func didReceive(data: Data) {
        $streamMutableState.write { state in
            if let stream = state.outputStream {
                // If there is an InputStream, the corresponding OutputStream will be created to output data
                underlyingQueue.async {
                    var bytes = Array(data)
                    stream.write(&bytes, maxLength: bytes.count)
                }
            }
            // Number of callbacks currently being processed + number of new callbacks to be processed
            state.numberOfExecutingStreams + = state.streams.count
            // Make a copy
            let localState = state
            // Process data
            underlyingQueue.async { localState.streams.forEach { $0(data) } }
        }
    }

    /// Add validation callback
    @discardableResult
    public func validate(_ validation: @escaping Validation) -> Self {
        let validator: () -> Void ={[unowned self] in
            guard self.error = = nil.let response = self.response else { return }

            let result = validation(self.request, response)

            if case let .failure(error) = result {
                self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error)))
            }

            self.eventMonitor?.request(self,
                                       didValidateRequest: self.request,
                                       response: response,
                                       withResult: result)
        }

        $validators.write { $0.append(validator) }

        return self
    }

    /// Produces an `InputStream` that receives the `Data` received by the instance.
    ///
    /// - Note: The `InputStream` produced by this method must have `open()` called before being able to read `Data`.
    /// Additionally, this method will automatically call `resume()` on the instance, regardless of whether or
    /// not the creating session has `startRequestsImmediately` set to `true`.
    ///
    /// - Parameter bufferSize: Size, in bytes, of the buffer between the `OutputStream` and `InputStream`.
    ///
    /// - Returns: The `InputStream` bound to the internal `OutboundStream`.
    The request must be opened () and closed () before the data is read by InputStream.
    public func asInputStream(bufferSize: Int = 1024) -> InputStream? {
        // The request is sent immediately after the IOStream is created, regardless of whether the request is sent immediately
        defer { resume() }

        var inputStream: InputStream?
        $streamMutableState.write { state in
            // Create a pair of iostreams, OutputStream writes to InputStream, buffer defaults to 1K
            Foundation.Stream.getBoundStreams(withBufferSize: bufferSize,
                                              inputStream: &inputStream,
                                              outputStream: &state.outputStream)
            // Open OutputStream, ready to read data
            state.outputStream?.open()
        }

        return inputStream
    }
    // Execute a callback that can throw an error, catch the exception, cancel the request, and throw the error (called in ResponseSerialization).
    func capturingError(from closure: () throws -> Void) {
        do {
            try closure()
        } catch {
            self.error = error.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error)))
            cancel()
        }
    }
    // add data flow to complete callback and queue
    func appendStreamCompletion<Success.Failure> (on queue: DispatchQueue.stream: @escaping Handler<Success.Failure>) {
        // Add a parse callback first
        appendResponseSerializer {
            // Add a parsed callback to the internal queue execution
            self.underlyingQueue.async {
                self.responseSerializerDidComplete {
                    // The parse completed callback is the operation $streamMutableState
                    self.$streamMutableState.write { state in
                        guard state.numberOfExecutingStreams = = 0 else {
                            // If there are still streams processing data, append the completion callback to the array
                            state.enqueuedCompletionEvents.append {
                                self.enqueueCompletion(on: queue, stream: stream)
                            }

                            return
                        }
                        // Otherwise, execute the callback directly
                        self.enqueueCompletion(on: queue, stream: stream)
                    }
                }
            }
        }
        // This ensures that the completion callback is added after all other parser processing is complete
    }
    // Send the completion event
    func enqueueCompletion<Success.Failure> (on queue: DispatchQueue.stream: @escaping Handler<Success.Failure>) {
        queue.async {
            do {
                // Create the completion event
                let completion = Completion(request: self.request,
                                            response: self.response,
                                            metrics: self.metrics,
                                            error: self.error)
                / / you
                try stream(.init(event: .complete(completion), token: .init(self)))}catch {
                
                // Ignore the error, the completion error cannot be processed, the data is complete}}}}Copy the code

We then extend datastreamRequest. Stream to quickly retrieve internal data:

/// The principle is the same
extension DataStreamRequest.Stream {
    public var result: Result<Success.Failure>? {
        guard case let .stream(result) = event else { return nil }
        return result
    }

    public var value: Success? {
        guard case let .success(value) = result else { return nil }
        return value
    }

    public var error: Failure? {
        guard case let .failure(error) = result else { return nil }
        return error
    }

    public var completion: DataStreamRequest.Completion? {
        guard case let .complete(completion) = event else { return nil }
        return completion
    }
}
Copy the code

DownloadRequest

DownloadRequest is used to handle downloads and is used when saving files as files on disk.

Document processing:

Handles local file saving policies and local file saving paths

    // define a structure that complies with the OptionSet protocol to determine the local file saving strategy:
    public struct Options: OptionSet {
        /// Whether to create an intermediate directory
        public static let createIntermediateDirectories = Options(rawValue: 1 << 0)
        /// Whether to remove old files before downloading them
        public static let removePreviousFile = Options(rawValue: 1 << 1)

        public let rawValue: Int

        public init(rawValue: Int) {
            self.rawValue = rawValue
        }
    }

    // MARK: Download target path
    
    /// The download task will first download the file to the temporary cache path, then copy the file to the download destination path, using closures to defer the download path decision until the download is complete, depending on the temporary file directory and the download response header to determine the download destination path and file saving policy
    /// note: if you are downloading a local file (url starting with file://), the callback will not respond
    public typealias Destination = (_ temporaryURL: URL._ response: HTTPURLResponse) -> (destinationURL: URL, options: Options)

    // create the default recommended download path callback (document root)
    public class func suggestedDownloadDestination(for directory: FileManager.SearchPathDirectory = .documentDirectory.in domain: FileManager.SearchPathDomainMask=.userDomainMask.options: Options= []) - >Destination {
        { temporaryURL, response in
            let directoryURLs = FileManager.default.urls(for: directory, in: domain)
            let url = directoryURLs.first?.appendingPathComponent(response.suggestedFilename!) ?? temporaryURL

            return (url, options)
        }
    }

    // default download path callback (see below)
    static let defaultDestination: Destination = { url, _ in
        (defaultDestinationURL(url), [])
    }

    /// return the default file storage path (just rename the default file name to Alamofire_ prefix, the file is still in the cache directory, will be deleted, if you want to save the file, you need to move to another directory)
    static let defaultDestinationURL: (URL) - >URL = { url in
        let filename = "Alamofire_\(url.lastPathComponent)"
        let destination = url.deletingLastPathComponent().appendingPathComponent(filename)

        return destination
    }
Copy the code

Download the source Downloadable

    /// defines the download source for the download request
    public enum Downloadable {
        /// Download from URLRequest
        case request(URLRequestConvertible)
        /// Resumable
        case resumeData(Data)}Copy the code

Thread-safe mutable state

    // wrap it privately
    private struct DownloadRequestMutableState {
        /// The downloaded data that needs to be processed when the task supporting breakpoint continuation is cancelled
        var resumeData: Data?
        /// Save the file after downloading
        var fileURL: URL?
    }

    // private thread-safe property
    @Protected
    private var mutableDownloadState = DownloadRequestMutableState(a)/// open to external acquisition
    public var resumeData: Data? { mutableDownloadState.resumeData }
    public var fileURL: URL? { mutableDownloadState.fileURL }
Copy the code

Initialization and two parameters that need to be determined during initialization

    / / / download the source
    public let downloadable: Downloadable
    /// Download path callback
    let destination: Destination

    / / / initialized
    init(id: UUID = UUID(),
         downloadable: Downloadable.underlyingQueue: DispatchQueue.serializationQueue: DispatchQueue.eventMonitor: EventMonitor? .interceptor: RequestInterceptor? .delegate: RequestDelegate.destination: @escaping Destination) {
        self.downloadable = downloadable
        self.destination = destination

        super.init(id: id,
                   underlyingQueue: underlyingQueue,
                   serializationQueue: serializationQueue,
                   eventMonitor: eventMonitor,
                   interceptor: interceptor,
                   delegate: delegate)
    }
    // Clear data when retry
    override func reset(a) {
        super.reset()

        $mutableDownloadState.write {
            $0.resumeData = nil
            $0.fileURL = nil}}Copy the code

Handling of downloads and cancellations

    /// URLSession download completed/failed, call back to update status
    func didFinishDownloading(using task: URLSessionTask.with result: Result<URL.AFError>) {
        eventMonitor?.request(self, didFinishDownloadingUsing: task, with: result)

        switch result {
        case let .success(url): mutableDownloadState.fileURL = url
        case let .failure(error): self.error = error
        }
    }

    /// update progress is called back when URLSession is downloaded
    func updateDownloadProgress(bytesWritten: Int64.totalBytesExpectedToWrite: Int64) {
        downloadProgress.totalUnitCount = totalBytesExpectedToWrite
        downloadProgress.completedUnitCount + = bytesWritten

        downloadProgressHandler?.queue.async { self.downloadProgressHandler?.handler(self.downloadProgress) }
    }

    Create URLSessionTask / / /
    /// New file download
    override func task(for request: URLRequest.using session: URLSession) -> URLSessionTask {
        session.downloadTask(with: request)
    }
    /// Resumable
    public func task(forResumeData data: Data.using session: URLSession) -> URLSessionTask {
        session.downloadTask(withResumeData: data)
    }

    /// 1. Cancel the request without setting the resumeData attribute to the listener
    @discardableResult
    override public func cancel(a) -> Self {
        cancel(producingResumeData: false)}/// 2. Cancel the download and check whether the listener needs to be populated with the resumeData property
    @discardableResult
    public func cancel(producingResumeData shouldProduceResumeData: Bool) -> Self {
        cancel(optionallyProducingResumeData: shouldProduceResumeData ? { _ in } : nil)}/// 3. Cancel the download and use a callback to process the downloaded data, which will first fill the resumeData property, notify the listener and invoke the callback
    @discardableResult
    public func cancel(byProducingResumeData completionHandler: @escaping (_ data: Data?). ->Void) -> Self {
        cancel(optionallyProducingResumeData: completionHandler)
    }

    /// the integration of 1, 2, 3 above
    private func cancel(optionallyProducingResumeData completionHandler: ((_ resumeData: Data?). ->Void)?) -> Self {
        // Thread safety
        $mutableState.write { mutableState in
            // Determine whether to cancel
            guard mutableState.state.canTransitionTo(.cancelled) else { return }
            // Update the status
            mutableState.state = .cancelled
            // The parent class tells the listener that the call was cancelled
            underlyingQueue.async { self.didCancel() }

            guard let task = mutableState.tasks.last as? URLSessionDownloadTask, task.state ! = .completed else {
                // If the download is complete, go to finish logic
                underlyingQueue.async { self.finish() }
                return
            }

            if let completionHandler = completionHandler {
                // Cancel callbacks that handle downloaded data
                // Let's make sure the request metric is retrieved
                task.resume()
                task.cancel { resumeData in
                    // Fill in the resumeData attribute
                    self.mutableDownloadState.resumeData = resumeData
                    // The parent class tells the listener that the cancellation succeeded
                    self.underlyingQueue.async { self.didCancelTask(task) }
                    // Execute the downloaded data processing callback
                    completionHandler(resumeData)
                }
            } else {
                // No callback to handle downloaded data
                task.resume()
                // Cancel directly
                task.cancel(byProducingResumeData: { _ in })
                / / to inform
                self.underlyingQueue.async { self.didCancelTask(task) }
            }
        }

        return self
    }

    /// Determine the validity of the response
    @discardableResult
    public func validate(_ validation: @escaping Validation) -> Self {
        let validator: () -> Void ={[unowned self] in
            guard self.error = = nil.let response = self.response else { return }

            let result = validation(self.request, response, self.fileURL)

            if case let .failure(error) = result {
                self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error)))
            }

            self.eventMonitor?.request(self,
                                       didValidateRequest: self.request,
                                       response: response,
                                       fileURL: self.fileURL,
                                       withResult: result)
        }

        $validators.write { $0.append(validator) }

        return self
    }
Copy the code

UploadRequest

It is used to create upload requests, which can upload Data from memory (Data), disk (file), and InputStream (InputStream). Note that upload requests are subclasses of DataRequest

Upload the source

    public enum Uploadable {
        / / / from memory
        case data(Data)
        /// Upload from a file, and you can set whether to delete the source file after the upload is complete
        case file(URL, shouldRemove: Bool)
        // upload from stream
        case stream(InputStream)}Copy the code

Initialization and initial state properties

    // the protocol used to create the upload source
    public let upload: UploadableConvertible

    /// File manager, used to clear tasks, including multi-form upload data written to the hard disk
    public let fileManager: FileManager

    // upload the source, which is created when the request starts. Optional because it is possible to fail when creating a protocol object from the upload source
    public var uploadable: Uploadable?

    / / / initialized
    init(id: UUID = UUID(),
         convertible: UploadConvertible.underlyingQueue: DispatchQueue.serializationQueue: DispatchQueue.eventMonitor: EventMonitor? .interceptor: RequestInterceptor? .fileManager: FileManager.delegate: RequestDelegate) {
        upload = convertible
        self.fileManager = fileManager

        super.init(id: id,
                   convertible: convertible,
                   underlyingQueue: underlyingQueue,
                   serializationQueue: serializationQueue,
                   eventMonitor: eventMonitor,
                   interceptor: interceptor,
                   delegate: delegate)
    }

    // tell the listener that the upload source was created successfully (called in Session)
    func didCreateUploadable(_ uploadable: Uploadable) {
        self.uploadable = uploadable

        eventMonitor?.request(self, didCreateUploadable: uploadable)
    }
    /// tell the listener that it failed to create the upload source (called during Session) and retry or complete the logic
    func didFailToCreateUploadable(with error: AFError) {
        self.error = error

        eventMonitor?.request(self, didFailToCreateUploadableWithError: error)

        retryOrFinish(error: error)
    }
    
    // Create URLSessionUploadTask from URLRequest to URLSession
    override func task(for request: URLRequest.using session: URLSession) -> URLSessionTask {
        guard let uploadable = uploadable else {
            fatalError("Attempting to create a URLSessionUploadTask when Uploadable value doesn't exist.")}switch uploadable {
        case let .data(data): return session.uploadTask(with: request, from: data)
        case let .file(url, _) :return session.uploadTask(with: request, fromFile: url)
        case .stream: return session.uploadTask(withStreamedRequest: request)
        }
    }

    /// Delete data during retry. In retry, the upload source must be created again
    override func reset(a) {
        // Uploadable must be recreated on every retry.
        uploadable = nil

        super.reset()
    }

    /// Convert to InputStream for external connection. Uploadable. Stream must be used otherwise an exception will be thrown
    func inputStream(a) -> InputStream {
        guard let uploadable = uploadable else {
            fatalError("Attempting to access the input stream but the uploadable doesn't exist.")}guard case let .stream(stream) = uploadable else {
            fatalError("Attempted to access the stream of an UploadRequest that wasn't created with one.")
        }

        eventMonitor?.request(self, didProvideInputStream: stream)

        return stream
    }
    
    // When the request completes, clean up the task (delete the source file)
    override public func cleanup(a) {
        defer { super.cleanup() }

        guard
            let uploadable = self.uploadable,
            case let .file(url, shouldRemove) = uploadable,
            shouldRemove
        else { return }

        try? fileManager.removeItem(at: url)
    }
Copy the code

Protocol for creating the upload source

UploadableConvertible Specifies the protocol used to create the upload source
public protocol UploadableConvertible {
    func createUploadable(a) throws -> UploadRequest.Uploadable
}
    
* / *UploadRequest.Uploadable** Implements the UploadableConvertible protocol. UploadableConvertible ** Returns to UploadableConvertible
extension UploadRequest.Uploadable: UploadableConvertible {
    public func createUploadable(a) throws -> UploadRequest.Uploadable {
        self}}UploadableConvertible; URLRequestConvertible: UploadableConvertible;
public protocol UploadConvertible: UploadableConvertible & URLRequestConvertible {}
Copy the code

conclusion

  • The Request class is responsible for connecting the URLRequest and URLSessionTask. The base class mainly defines a bunch of attributes used in the Request, and then defines the callback method for each stage of the Request (excluding the Response parsing part, which is related to the Response parsing, in Response.swift). Call external or self to notify the listener and proceed with the logic (retry, complete)
  • The four subclasses add attributes, states, and callbacks with their own characteristics
  • Each of the four subclasses uses a protocol to isolate the creation of the Request source, so that the Request class doesn’t care where the upload source comes from. The upload Request also uses a protocol to isolate the creation of the upload source, and uses a composite protocol to combine the Request’s protocol from the upload source
  • Request. Most of the content in Swift is the creation of Request and processing of various states. The sending time of Task is as follows:
    1. If startImmediately is true (the default), response*** in your request will add responseSerializers to your request. When you add the first responseSerializers, The resume method is automatically called to begin the request
    2. Otherwise, you need to manually call the Resume method on the Request object to send a Request

note

Alamofire is very simple to use:

AF.request("Request address").responseJSON(completionHandler: {
    resp in
    // Process the response
})
Copy the code

However, the internal processing is very complex, from the initial processing of the request address, to the creation of the URLRequest, to the creation of the URLSessionTask, each step has a lot of fine processing. Session schedules requests. Request and Response are the core of requests. In combination with various validity judgments, authentication, caching, retry, error throwing, etc., the EventMonitor interface alone has a full 43 callback methods, which run through all the state processing from the beginning of the request to the completion of the Response parsing. After watching the Response parsing, I will try to draw a sequence diagram to see if I can clarify the logic. Learn about code design and packaging logic, to their own thinking when the code has a great improvement.

All of the above are personal understanding, if there is a mistake, welcome to comment pointed out, will be the first time to modify ~~ very thank ~~