Previous navigation:

Alamofire source learning directory collection

Related documents:

SessionDelegate.swift

EventMonitor.swift

NetworkReachabilityManager.swift

SessionDelegate.swift

  • Declare the classSessionDelegateTo implement the URLSessionDelegate and each URLSessionTaskDelegate and send each state event to the event listener object:
    • Holds a FileManager object that can be initialized with a custom object to handle file operations (deleting existing files, creating directories) for download requests.
    • The SessionStateProvider protocol object holds a weak reference to the Session, which is used to obtain the corresponding Request object based on the Task from the request-task mapping stored in the Session
    • Holds a strong reference to the EventMonitor protocol object that is used to receive each status event in the request
    • There’s only one way:
      // Obtain the corresponding Request according to Task and convert it to the corresponding subclass
      func request<R: Request> (for task: URLSessionTask.as type: R.Type) -> R? {
          guard let provider = stateProvider else {
              assertionFailure("StateProvider is nil.")
              return nil
          }
          
          // Obtain request from SessionStateProvider(Session) according to task
          return provider.request(for: task) as? R
      }
      Copy the code
  • Define the agreementSessionStateProviderTo connect a Session to a SessionDelegate(Sesson implements this protocol)
    protocol SessionStateProvider: AnyObject {
        // Obtain the certificate manager, redirection manager, and cache manager from Session
        var serverTrustManager: ServerTrustManager? { get }
        var redirectHandler: RedirectHandler? { get }
        var cachedResponseHandler: CachedResponseHandler? { get }
        // Retrieve the request from the Session RequestTaskMap
        func request(for task: URLSessionTask) -> Request?
        // Get the callback after the task metrics are obtained
        func didGatherMetricsForTask(_ task: URLSessionTask)
        // The callback that requests completion may continue to retrieve the task metrics
        func didCompleteTask(_ task: URLSessionTask.completion: @escaping() - >Void)
        // Authentication and authorization processing
        func credential(for task: URLSessionTask.in protectionSpace: URLProtectionSpace) -> URLCredential?
        // Callback for Session failure
        func cancelRequestsForSessionInvalidation(with error: Error?).
    }
    Copy the code
  • Extend the class four timesSessionDelegateImplementation Implements four URLSessionDelegate and URLSessionTaskDelegate protocols to handle Session and URLSessionTask changes
    
    // MARK: URLSessionDelegate
    
    extension SessionDelegate: URLSessionDelegate {
        open func urlSession(_ session: URLSession.didBecomeInvalidWithError error: Error?). {
            // When the Session is unavailable, notify the listener, and then call back the Session to cancel all requests
            eventMonitor?.urlSession(session, didBecomeInvalidWithError: error)
            stateProvider?.cancelRequestsForSessionInvalidation(with: error)
        }
    }
    
    // MARK: URLSessionTaskDelegate
    
    extension SessionDelegate: URLSessionTaskDelegate {
        // Define tuple package authentication processing, certificate, and error
        typealias ChallengeEvaluation = (disposition: URLSession.AuthChallengeDisposition, credential: URLCredential? , error:AFError?).// Received a callback to verify the certificate
        open func urlSession(_ session: URLSession.task: URLSessionTask.didReceive challenge: URLAuthenticationChallenge.completionHandler: @escaping (URLSession.AuthChallengeDisposition.URLCredential?). ->Void) {
            // Notify the event listener
            eventMonitor?.urlSession(session, task: task, didReceive: challenge)
    
            let evaluation: ChallengeEvaluation
            // Assemble authentication mode, certificate, error message
            switch challenge.protectionSpace.authenticationMethod {
            case NSURLAuthenticationMethodServerTrust:
                / / HTTPS certificate
                evaluation = attemptServerTrustAuthentication(with: challenge)
            case NSURLAuthenticationMethodHTTPBasic.NSURLAuthenticationMethodHTTPDigest.NSURLAuthenticationMethodNTLM.NSURLAuthenticationMethodNegotiate.NSURLAuthenticationMethodClientCertificate:
                // Other authentication modes
                evaluation = attemptCredentialAuthentication(for: challenge, belongingTo: task)
            default:
                evaluation = (.performDefaultHandling, nil.nil)}// If the certificate fails to be obtained, the request fails
            if let error = evaluation.error {
                stateProvider?.request(for: task)?.didFailTask(task, earlyWithError: error)
            }
            // Result callback
            completionHandler(evaluation.disposition, evaluation.credential)
        }
        // Handle server authentication
        func attemptServerTrustAuthentication(with challenge: URLAuthenticationChallenge) -> ChallengeEvaluation {
            let host = challenge.protectionSpace.host
    
            guard challenge.protectionSpace.authenticationMethod = = NSURLAuthenticationMethodServerTrust.let trust = challenge.protectionSpace.serverTrust
            else {
                // If there is no custom certificate manager, return the default processing
                return (.performDefaultHandling, nil.nil)}do {
                guard let evaluator = try stateProvider?.serverTrustManager?.serverTrustEvaluator(forHost: host) else {
                    return (.performDefaultHandling, nil.nil)}// Obtain the ServerTrustEvaluating object from the ServerTrustManager to perform customized authentication
                try evaluator.evaluate(trust, forHost: host)
    
                return (.useCredential, URLCredential(trust: trust), nil)}catch {
                // If a custom authentication error occurs, return the deauthenticating action + error
                return (.cancelAuthenticationChallenge, nil, error.asAFError(or: .serverTrustEvaluationFailed(reason: .customEvaluationFailed(error: error))))
            }
        }
    
        // Other authentication modes
        func attemptCredentialAuthentication(for challenge: URLAuthenticationChallenge.belongingTo task: URLSessionTask) -> ChallengeEvaluation {
            guard challenge.previousFailureCount = = 0 else {
                // If the previous authentication fails, the current authentication is skipped and the next authentication starts
                return (.rejectProtectionSpace, nil.nil)}guard let credential = stateProvider?.credential(for: task, in: challenge.protectionSpace) else {
                // No authentication processing object is obtained. This authentication is ignored
                return (.performDefaultHandling, nil.nil)}return (.useCredential, credential, nil)}// Upload progress
        open func urlSession(_ session: URLSession.task: URLSessionTask.didSendBodyData bytesSent: Int64.totalBytesSent: Int64.totalBytesExpectedToSend: Int64) {
            // Notify the listener
            eventMonitor?.urlSession(session,
                                     task: task,
                                     didSendBodyData: bytesSent,
                                     totalBytesSent: totalBytesSent,
                                     totalBytesExpectedToSend: totalBytesExpectedToSend)
            // Get the Request and update the upload progress
            stateProvider?.request(for: task)?.updateUploadProgress(totalBytesSent: totalBytesSent,
                                                                    totalBytesExpectedToSend: totalBytesExpectedToSend)
        }
    
        // The upload request is ready to upload the new body stream
        open func urlSession(_ session: URLSession.task: URLSessionTask.needNewBodyStream completionHandler: @escaping (InputStream?). ->Void) {
            // Notify the listener
            eventMonitor?.urlSession(session, taskNeedsNewBodyStream: task)
    
            guard let request = request(for: task, as: UploadRequest.self) else {
                // Only upload requests will respond to this method
                assertionFailure("needNewBodyStream did not find UploadRequest.")
                completionHandler(nil)
                return
            }
            // Return the InputStream from the request. Only the upload request created with the InputStream will be returned correctly. Otherwise, an error will be thrown
            completionHandler(request.inputStream())
        }
    
        / / redirection
        open func urlSession(_ session: URLSession.task: URLSessionTask.willPerformHTTPRedirection response: HTTPURLResponse.newRequest request: URLRequest.completionHandler: @escaping (URLRequest?). ->Void) {
            // Notify the listener
            eventMonitor?.urlSession(session, task: task, willPerformHTTPRedirection: response, newRequest: request)
    
            if let redirectHandler = stateProvider?.request(for: task)?.redirectHandler ?? stateProvider?.redirectHandler {
                // Check if the Request has its own redirection handler, and then check if the Session has a global redirection handler
                redirectHandler.task(task, willBeRedirectedTo: request, for: response, completion: completionHandler)
            } else {
                completionHandler(request)
            }
        }
    
        // Get the request metrics
        open func urlSession(_ session: URLSession.task: URLSessionTask.didFinishCollecting metrics: URLSessionTaskMetrics) {
            // Notify the listener
            eventMonitor?.urlSession(session, task: task, didFinishCollecting: metrics)
            / / notification Request
            stateProvider?.request(for: task)?.didGatherMetrics(metrics)
            / / notify the Session
            stateProvider?.didGatherMetricsForTask(task)
        }
    
        // The request is complete, and we may go back to retrieve the metrics later
        open func urlSession(_ session: URLSession.task: URLSessionTask.didCompleteWithError error: Error?). {
            // Notify the listener
            eventMonitor?.urlSession(session, task: task, didCompleteWithError: error)
    
            let request = stateProvider?.request(for: task)
            // Notify Session that the request is complete
            stateProvider?.didCompleteTask(task) {
                // The callback content is to confirm the completion of the Request indicator and notify the Request to complete
                request?.didCompleteTask(task, with: error.map { $0.asAFError(or: .sessionTaskFailed(error: $0))}}}// Requests caused by network changes are waiting to be processed
        @available(macOS 10.13.iOS 11.0.tvOS 11.0.watchOS 4.0.*)
        open func urlSession(_ session: URLSession.taskIsWaitingForConnectivity task: URLSessionTask) {
            eventMonitor?.urlSession(session, taskIsWaitingForConnectivity: task)
        }
    }
    
    // MARK: URLSessionDataDelegate
    
    extension SessionDelegate: URLSessionDataDelegate {
    
        // The response data is received
        open func urlSession(_ session: URLSession.dataTask: URLSessionDataTask.didReceive data: Data) {
            // Notify the listener
            eventMonitor?.urlSession(session, dataTask: dataTask, didReceive: data)
            // Only DataRequest and DataStreamRequest will receive data
            if let request = request(for: dataTask, as: DataRequest.self) {
                request.didReceive(data: data)
            } else if let request = request(for: dataTask, as: DataStreamRequest.self) {
                request.didReceive(data: data)
            } else {
                assertionFailure("dataTask did not find DataRequest or DataStreamRequest in didReceive")
                return}}// Handle whether to save the cache
        open func urlSession(_ session: URLSession.dataTask: URLSessionDataTask.willCacheResponse proposedResponse: CachedURLResponse.completionHandler: @escaping (CachedURLResponse?). ->Void) {
            // Notify the listener
            eventMonitor?.urlSession(session, dataTask: dataTask, willCacheResponse: proposedResponse)
    
            if let handler = stateProvider?.request(for: dataTask)?.cachedResponseHandler ?? stateProvider?.cachedResponseHandler {
                // Use the request cache handler to handle the cache
                handler.dataTask(dataTask, willCacheResponse: proposedResponse, completion: completionHandler)
            } else {
                // If there is no cache handler, use the default logic to handle the cache
                completionHandler(proposedResponse)
            }
        }
    }
    
    // MARK: URLSessionDownloadDelegate
    
    extension SessionDelegate: URLSessionDownloadDelegate {
        // Start the callback for breakpoint continuation
        open func urlSession(_ session: URLSession.downloadTask: URLSessionDownloadTask.didResumeAtOffset fileOffset: Int64.expectedTotalBytes: Int64) {
            // Notify the listener
            eventMonitor?.urlSession(session,
                                     downloadTask: downloadTask,
                                     didResumeAtOffset: fileOffset,
                                     expectedTotalBytes: expectedTotalBytes)
            guard let downloadRequest = request(for: downloadTask, as: DownloadRequest.self) else {
                // Only DownloadRequest can handle this protocol
                assertionFailure("downloadTask did not find DownloadRequest.")
                return
            }
    
            downloadRequest.updateDownloadProgress(bytesWritten: fileOffset,
                                                   totalBytesExpectedToWrite: expectedTotalBytes)
        }
    
        // Update the download progress
        open func urlSession(_ session: URLSession.downloadTask: URLSessionDownloadTask.didWriteData bytesWritten: Int64.totalBytesWritten: Int64.totalBytesExpectedToWrite: Int64) {
            // Notify the listener
            eventMonitor?.urlSession(session,
                                     downloadTask: downloadTask,
                                     didWriteData: bytesWritten,
                                     totalBytesWritten: totalBytesWritten,
                                     totalBytesExpectedToWrite: totalBytesExpectedToWrite)
            guard let downloadRequest = request(for: downloadTask, as: DownloadRequest.self) else {
                assertionFailure("downloadTask did not find DownloadRequest.")
                return
            }
    
            downloadRequest.updateDownloadProgress(bytesWritten: bytesWritten,
                                                   totalBytesExpectedToWrite: totalBytesExpectedToWrite)
        }
    
        // The download is complete
        open func urlSession(_ session: URLSession.downloadTask: URLSessionDownloadTask.didFinishDownloadingTo location: URL) {
            // Notify the listener
            eventMonitor?.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location)
    
            guard let request = request(for: downloadTask, as: DownloadRequest.self) else {
                assertionFailure("downloadTask did not find DownloadRequest.")
                return
            }
            // To prepare the dump file, define the tuple (dump directory, download options)
            let (destination, options): (URL.DownloadRequest.Options)
            if let response = request.response {
                // Get the dump directory from request
                (destination, options) = request.destination(location, response)
            } else {
                // If there's no response this is likely a local file download, so generate the temporary URL directly.
                (destination, options) = (DownloadRequest.defaultDestinationURL(location), [])
            }
            // Notify the listener
            eventMonitor?.request(request, didCreateDestinationURL: destination)
    
            do {
                // Whether to delete the old file
                if options.contains(.removePreviousFile), fileManager.fileExists(atPath: destination.path) {
                    try fileManager.removeItem(at: destination)
                }
                // Whether to create a directory chain
                if options.contains(.createIntermediateDirectories) {
                    let directory = destination.deletingLastPathComponent()
                    try fileManager.createDirectory(at: directory, withIntermediateDirectories: true)}// Dump the file
                try fileManager.moveItem(at: location, to: destination)
                // Call back to request
                request.didFinishDownloading(using: downloadTask, with: .success(destination))
            } catch {
                // Throw an exception if something goes wrong
                request.didFinishDownloading(using: downloadTask, with: .failure(.downloadedFileMoveFailed(error: error,
                                                                                                           source: location,
                                                                                                           destination: destination)))
            }
        }
    }
    
    Copy the code

RequestTaskMap.swift

Only one structure is defined: The RequestTaskMap is used to hold the bidirectional mapping between the Request object and the URLSessionTask. This structure is held by the Session and can be used to quickly locate the corresponding URLSessionTask based on the Request. Or according to the URLSessionTask to find the corresponding Request.

struct RequestTaskMap {
    // The status of the task, which determines whether to release the mapping (whether the task is complete, whether the task has obtained the request metrics)
    private typealias Events = (completed: Bool, metricsGathered: Bool)
    / / the task to the request
    private var tasksToRequests: [URLSessionTask: Request]
    / / request to the task
    private var requestsToTasks: [Request: URLSessionTask]
    / / the state of the task
    private var taskEvents: [URLSessionTask: Events]
    // All requests
    var requests: [Request] {
        Array(tasksToRequests.values)
    }
    // Initializes all three dictionaries to be empty by default
    init(tasksToRequests: [URLSessionTask: Request] =[:].requestsToTasks: [Request: URLSessionTask] =[:].taskEvents: [URLSessionTask: (completed: Bool, metricsGathered: Bool)] = [:]) {
        self.tasksToRequests = tasksToRequests
        self.requestsToTasks = requestsToTasks
        self.taskEvents = taskEvents
    }
    // Corner access task
    subscript(_ request: Request) -> URLSessionTask? {
        get { requestsToTasks[request] }
        set {
            guard let newValue = newValue else {
                // If the new value is null, the mapping is deleted. If no mapping exists, an error is reported
                guard let task = requestsToTasks[request] else {
                    fatalError("RequestTaskMap consistency error: no task corresponding to request found.")
                }

                requestsToTasks.removeValue(forKey: request)
                tasksToRequests.removeValue(forKey: task)
                taskEvents.removeValue(forKey: task)

                return
            }
            // Save the new task
            requestsToTasks[request] = newValue
            tasksToRequests[newValue] = request
            taskEvents[newValue] = (completed: false, metricsGathered: false)}}// Corner access request
    subscript(_ task: URLSessionTask) -> Request? {
        get { tasksToRequests[task] }
        set {
            guard let newValue = newValue else {
                guard let request = tasksToRequests[task] else {
                    fatalError("RequestTaskMap consistency error: no request corresponding to task found.")
                }

                tasksToRequests.removeValue(forKey: task)
                requestsToTasks.removeValue(forKey: request)
                taskEvents.removeValue(forKey: task)

                return
            }

            tasksToRequests[task] = newValue
            requestsToTasks[newValue] = task
            taskEvents[task] = (completed: false, metricsGathered: false)}}// The number of maps made, the number of dictionaries made, etc
    var count: Int {
        precondition(tasksToRequests.count = = requestsToTasks.count,
                     "RequestTaskMap.count invalid, requests.count: \(tasksToRequests.count)! = tasks.count:\(requestsToTasks.count)")

        return tasksToRequests.count
    }
    // Task state number (mapping number, and this attribute is not used)
    var eventCount: Int {
        precondition(taskEvents.count = = count, "RequestTaskMap.eventCount invalid, count: \(count)! = taskEvents.count:\(taskEvents.count)")

        return taskEvents.count
    }
    // Whether to be empty
    var isEmpty: Bool {
        precondition(tasksToRequests.isEmpty = = requestsToTasks.isEmpty,
                     "RequestTaskMap.isEmpty invalid, requests.isEmpty: \(tasksToRequests.isEmpty)! = tasks.isEmpty:\(requestsToTasks.isEmpty)")

        return tasksToRequests.isEmpty
    }
    // whether the task state isEmpty (isEmpty)
    var isEventsEmpty: Bool {
        precondition(taskEvents.isEmpty = = isEmpty, "RequestTaskMap.isEventsEmpty invalid, isEmpty: \(isEmpty)! = taskEvents.isEmpty:\(taskEvents.isEmpty)")

        return taskEvents.isEmpty
    }
    // After the Session receives the task to obtain the request indicator, it will determine whether the task is completely completed (the request is complete and the indicator is obtained), update the taskEvents status, delete the mapping relationship, and then the Session will perform the callback related to the request completion. The value returned by this method is whether the mapping is removed
    mutating func disassociateIfNecessaryAfterGatheringMetricsForTask(_ task: URLSessionTask) -> Bool {
        guard let events = taskEvents[task] else {
            fatalError("RequestTaskMap consistency error: no events corresponding to task found.")}switch (events.completed, events.metricsGathered) {
        case (_.true) :fatalError("RequestTaskMap consistency error: duplicate metricsGatheredForTask call.")
        case (false.false): taskEvents[task] = (completed: false, metricsGathered: true); return false
        case (true.false) :self[task] = nil; return true}}// After the Session receives the task didComplete callback, determine whether the task is :1. Go ahead and get the request metrics. The system updates the status and returns whether to delete the mapping
    mutating func disassociateIfNecessaryAfterCompletingTask(_ task: URLSessionTask) -> Bool {
        guard let events = taskEvents[task] else {
            fatalError("RequestTaskMap consistency error: no events corresponding to task found.")}switch (events.completed, events.metricsGathered) {
        case (true._) :fatalError("RequestTaskMap consistency error: duplicate completionReceivedForTask call.")
        #if os(Linux)
        /// In Linux, the request does not get metrics, so you can delete the mapping directly after completion
        default: self[task] = nil; return true
        #else
        case (false.false) :if #available(macOS 10.12.iOS 10.watchOS 7.tvOS 10.*) {
                taskEvents[task] = (completed: true, metricsGathered: false); return false
            } else {
                WatchOS 7 does not get metrics below, so delete the mapping directly and return true
                self[task] = nil; return true
            }
        case (false.true) :self[task] = nil; return true
        #endif}}}Copy the code

EventMonitor.swift

  • Declare the EventMonitor interface to listen for various states during the request, the protocol defines a queue of callback listeners (the default main queue), and a bunch of methods.
  • There are three classes in Alamofire that implement this interface:
    • The AlamofireNotifications class is used to send notifications at different stages
    • CompositeEventMonitor class for composing different listener objects (more on this below)
    • The ClosureEventMonitor class, which holds N more than one closure, converts proxy calls into closure calls
  • Holding status in Alamofire:
    • The SessionDelegate class holds a CompositeEventMonitor composite listener object: The Session initializes by taking an array of listeners and combining them with the AlamofireNotifications listener into a CompositeEventMonitor to teach the SessionDelegate to hold. In the Session proxy method, these listeners are called back
    • Each Request object also holds a CompositeEventMonitor: When initializing the Request object in Session, we pass the composition simulator held by the SessionDelegate to the created Request, and call back these listeners when the Request responds to the state
  • Method notes:
    /// Internal proxy, for each URLSession, several TaskDelegate proxies, and several proxy methods specific to the Request cycle of Request and its subclasses
    public protocol EventMonitor {
        /// the listener calls back to the queue, which defaults to the main queue
        var queue: DispatchQueue { get }
    
        // MARK: -urlSession event and several TaskDelegate-related events
    
        // MARK: URLSessionDelegate Events
    
        /// Event called during `URLSessionDelegate`'s `urlSession(_:didBecomeInvalidWithError:)` method.
        func urlSession(_ session: URLSession.didBecomeInvalidWithError error: Error?).
    
        // MARK: URLSessionTaskDelegate Events
    
        /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didReceive:completionHandler:)` method.
        func urlSession(_ session: URLSession.task: URLSessionTask.didReceive challenge: URLAuthenticationChallenge)
    
        /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)` method.
        func urlSession(_ session: URLSession.task: URLSessionTask.didSendBodyData bytesSent: Int64.totalBytesSent: Int64.totalBytesExpectedToSend: Int64)
    
        /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:needNewBodyStream:)` method.
        func urlSession(_ session: URLSession.taskNeedsNewBodyStream task: URLSessionTask)
    
        /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` method.
        func urlSession(_ session: URLSession.task: URLSessionTask.willPerformHTTPRedirection response: HTTPURLResponse.newRequest request: URLRequest)
    
        /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didFinishCollecting:)` method.
        func urlSession(_ session: URLSession.task: URLSessionTask.didFinishCollecting metrics: URLSessionTaskMetrics)
    
        /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didCompleteWithError:)` method.
        func urlSession(_ session: URLSession.task: URLSessionTask.didCompleteWithError error: Error?).
    
        /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:taskIsWaitingForConnectivity:)` method.
        @available(macOS 10.13.iOS 11.0.tvOS 11.0.watchOS 4.0.*)
        func urlSession(_ session: URLSession.taskIsWaitingForConnectivity task: URLSessionTask)
    
        // MARK: URLSessionDataDelegate Events
    
        /// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:didReceive:)` method.
        func urlSession(_ session: URLSession.dataTask: URLSessionDataTask.didReceive data: Data)
    
        /// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:willCacheResponse:completionHandler:)` method.
        func urlSession(_ session: URLSession.dataTask: URLSessionDataTask.willCacheResponse proposedResponse: CachedURLResponse)
    
        // MARK: URLSessionDownloadDelegate Events
    
        /// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)` method.
        func urlSession(_ session: URLSession.downloadTask: URLSessionDownloadTask.didResumeAtOffset fileOffset: Int64.expectedTotalBytes: Int64)
    
        /// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)` method.
        func urlSession(_ session: URLSession.downloadTask: URLSessionDownloadTask.didWriteData bytesWritten: Int64.totalBytesWritten: Int64.totalBytesExpectedToWrite: Int64)
    
        /// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didFinishDownloadingTo:)` method.
        func urlSession(_ session: URLSession.downloadTask: URLSessionDownloadTask.didFinishDownloadingTo location: URL)
    
        // MARK: -request and its subclasses related Request cycle events
    
        /// The original URLRequest callback succeeds. If the request has an adapter, the original URLRequest will be processed using the adapter. If the request does not have an adapter, the original URLRequest method will be called back directly
        func request(_ request: Request.didCreateInitialURLRequest urlRequest: URLRequest)
    
        /// Failed to create the original URLRequest callback
        func request(_ request: Request.didFailToCreateURLRequestWithError error: AFError)
    
        /// When creating the Request, the adapter calls back after successfully processing the URLRequest
        func request(_ request: Request.didAdaptInitialRequest initialRequest: URLRequest.to adaptedRequest: URLRequest)
    
        /// The adapter failed to process the Request
        func request(_ request: Request.didFailToAdaptURLRequest initialRequest: URLRequest.withError error: AFError)
    
        // create Request successfully (after adapter processing)
        func request(_ request: Request.didCreateURLRequest urlRequest: URLRequest)
    
        /// Creating URLSessionTask succeeded
        func request(_ request: Request.didCreateTask task: URLSessionTask)
    
        /// Receive the request indicator callback
        func request(_ request: Request.didGatherMetrics metrics: URLSessionTaskMetrics)
    
        /// Create errors thrown by Alamofire, such as custom authentication processing failure
        func request(_ request: Request.didFailTask task: URLSessionTask.earlyWithError error: AFError)
    
        ///URLSessionTask request completed, may succeed or fail, then determine whether to retry, if retry, the callback will be called multiple times
        func request(_ request: Request.didCompleteTask task: URLSessionTask.with error: AFError?).
    
        /// Ready to start retry
        func requestIsRetrying(_ request: Request)
    
        /// The request is complete. Start parsing the response data
        func requestDidFinish(_ request: Request)
    
        // The next six methods are paired with callbacks that call the Request actively and then affect the associated Task
    
        /// Request calls the resume method when it is called
        func requestDidResume(_ request: Request)
    
        // Call back when the Request associated URLSessionTask continues
        func request(_ request: Request.didResumeTask task: URLSessionTask)
    
        /// Request calls suspend
        func requestDidSuspend(_ request: Request)
    
        /// The Task associated with the Request is suspended
        func request(_ request: Request.didSuspendTask task: URLSessionTask)
    
        /// Request calls cancel
        func requestDidCancel(_ request: Request)
    
        // The Task associated with the Request is cancelled
        func request(_ request: Request.didCancelTask task: URLSessionTask)
    
        // MARK: DataRequest specific event
    
        /// Check whether the response is valid and call back
        func request(_ request: DataRequest.didValidateRequest urlRequest: URLRequest? .response: HTTPURLResponse.data: Data? .withResult result: Request.ValidationResult)
    
        /// Callback when DataRequest successfully creates DataResponse of type Data (no serialization)
        func request(_ request: DataRequest.didParseResponse response: DataResponse<Data? .AFError>)
    
        /// DataRequest callback when the serialized DataResponse was successfully created
        func request<Value> (_ request: DataRequest.didParseResponse response: DataResponse<Value.AFError>)
    
        // MARK: DataStreamRequest specific event
    
        /// The response is valid
        func request(_ request: DataStreamRequest.didValidateRequest urlRequest: URLRequest? .response: HTTPURLResponse.withResult result: Request.ValidationResult)
    
        /// called after successful serialization of data from stream
        func request<Value> (_ request: DataStreamRequest.didParseStream result: Result<Value.AFError>)
    
        // MARK: UploadRequest specific event
    
        Upload request succeeded. The Uploadable object was created successfully
        func request(_ request: UploadRequest.didCreateUploadable uploadable: UploadRequest.Uploadable)
    
        Upload request failed to create Uploadable
        func request(_ request: UploadRequest.didFailToCreateUploadableWithError error: AFError)
    
        /// Call back when the upload request starts with the InputSteam, only if the requested InputStream is not a Data or file URL type
        func request(_ request: UploadRequest.didProvideInputStream stream: InputStream)
    
        // MARK: DownloadRequest special events
    
        /// Call back after the download Task is complete and the cache file is cleared
        func request(_ request: DownloadRequest.didFinishDownloadingUsing task: URLSessionTask.with result: Result<URL.AFError>)
    
        /// Call back after the download request successfully created the dump directory
        func request(_ request: DownloadRequest.didCreateDestinationURL url: URL)
    
        // download request validation succeeded
        func request(_ request: DownloadRequest.didValidateRequest urlRequest: URLRequest? .response: HTTPURLResponse.fileURL: URL? .withResult result: Request.ValidationResult)
    
        /// Parsed response successfully with raw data (no serialization)
        func request(_ request: DownloadRequest.didParseResponse response: DownloadResponse<URL? .AFError>)
    
        // Serialization parsed response successfully
        func request<Value> (_ request: DownloadRequest.didParseResponse response: DownloadResponse<Value.AFError>)
    }
    Copy the code
  • After the interface is declared, it extends the interface, adds an empty implementation to all methods, and returns the default main queue for queues. If you want to implement the listener yourself, you can simply implement the interface you are interested in.
  • CompositeEventMonitorCombined listener classes:
    • Queue is an asynchronous queue //TODO
    • Initialize with an array of listeners and hold those arrays
    • Defines a performEvent method to send back calls
        func performEvent(_ event: @escaping (EventMonitor) - >Void) {
        // Loop asynchronously in its own queue, callback methods in each listener's own queue
        queue.async {
            for monitor in self.monitors {
                monitor.queue.async { event(monitor) }
            }
        }
    }
    Copy the code
  • ClosureEventMonitorClosure listener class:
    • Queue main queue
    • N is the number of methods of the EventMonitor interface. Each closure corresponds to an interface. The input and output parameters of the closure are corresponding to the input and output parameters of the participating interface
    • The implementation of each protocol invokes the corresponding closure

NetworkReachabilityManager.swift

  • Network status listener manager, Watchos is not available with Linux
  • The core is the SystemConfiguration system module
  • Define a network status enumerationNetworkReachabilityStatus:
    • The unknown
    • There is no network
    • Networking (substateConnectionType) :
      • Ethernet/wifi
      • The mobile network
  • Define the alias Listener, which is actually a closure.

    public typealias Listener = (NetworkReachabilityStatus) -> Void
  • Defines several read-only computing properties for quick network determination
  • Holds a reachabilityQueue that listens for network changes
  • Defines a MutableState structure that holds a Listener object, a queue of Listener callbacks, and an old network state
  • Holds a thread-safe MutableState variable that notifts the listener when the network changes in the listener callback queue.

Purely personal understanding, there may be misunderstanding, if there is a mistake, please comment to point out ~ thanks ~