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 ~