Forward navigation:

Alamofire source learning directory collection

Ps: After a lapse of two months, I have been so busy that I finally have time to continue to write something…

Brief introduction:

HTTP cache is used to control the cache parameters through the relevant fields in the response header. URLSession automatically manages the cache. Alamofire defines a CachedResponseHandler protocol to preprocess the cache, allowing the user to modify the response before the URLSession is about to cache the response data.

Cache processing in URLSession

The URLSessionDataDelegate of the URLSession has a method to declare handling the cache:

/* Invoke the completion routine with a valid NSCachedURLResponse to * allow the resulting data to be cached, or pass nil to prevent * caching. Note that there is no guarantee that caching will be * attempted for a given resource,  and you should not rely on this * message to receive the resource data. */
- (void)URLSession: (NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
                                  willCacheResponse:(NSCachedURLResponse *)proposedResponse 
                                  completionHandler:(void (^) (NSCachedURLResponse * _Nullable cachedResponse))completionHandler;
Copy the code

URLSession will process the Cache itself according to the cache-related fields in the response header, using the interface to provide users with the opportunity to modify the Cache. The change method allows users to modify the response data before caching the response data, and completionHandle must be called after the modification. If the input parameter is not null, the Cache parameter will Cache the data. If the input parameter is empty, the response is not cached.

Abstract encapsulation and traversal cache manager for Alamofire

Alamofire defines the CachedResponseHandler protocol to handle caching operations, which has only one method:

public protocol CachedResponseHandler {
    /// Decide the cache processing operation
    ///
    /// There are three cases of completion callback parameters:
    /// 1. Enter the second parameter (do not change the data, direct caching, default behavior)
    /// 2. The new data after the modification of the response cache data (suitable for the need to modify the data before caching)
    /// 3.nil, do not cache respose
    ///
    ///
    /// - Parameters:
    /// - task: requests a task
    /// -response: Response data to be cached
    /// - completion: determines the completion callback for the cache behavior
    func dataTask(_ task: URLSessionDataTask.willCacheResponse response: CachedURLResponse.completion: @escaping (CachedURLResponse?). ->Void)
}
Copy the code

The protocol has two classes in use:

  1. The SessionStateProvider protocol is declared in the SessionDelegate, which provides the global cacheResponseHandler. The Session class complies with this protocol and sets the global cacheResponseHandler during initialization.
  2. The optional variable cacheResponseHandler is defined in the Request base class, which allows you to use a dedicated cache handler for a single Request, taking precedence over the global cache handler

The cache is then handled using the underlying cache handler in the SessionDelegate implementation’s URLSessionDataDelegate cache handling method

    // Handle whether to save the cache
    open func urlSession(_ session: URLSession.dataTask: URLSessionDataTask.willCacheResponse proposedResponse: CachedURLResponse.completionHandler: @escaping (CachedURLResponse?). ->Void) {
        // Notification listener
        eventMonitor?.urlSession(session, dataTask: dataTask, willCacheResponse: proposedResponse)

        // Get the Request cache handler first, then get the Session global cache handler
        if let handler = stateProvider?.request(for: dataTask)?.cachedResponseHandler ?? stateProvider?.cachedResponseHandler {
            // Use the 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)
        }
    }
Copy the code

ResponseCacher, a simple cache handler defined in Alamofire

Alamofire defines a ResponseCacher, a simple cache handler that defines an enumeration of behaviors to determine the cache logic:

public struct ResponseCacher {
    // define the behavior of handling the cache
    public enum Behavior {
        // cache raw data
        case cache
        / / / no cache
        case doNotCache
        // modify data first, then cache new data, parameter is modified data closure, change closure returns optional new cache data
        case modify((URLSessionDataTask.CachedURLResponse) - >CachedURLResponse?). }// quickly initialize the object that caches raw data
    public static let cache = ResponseCacher(behavior: .cache)
    // quickly initialize an uncached object
    public static let doNotCache = ResponseCacher(behavior: .doNotCache)

    /// Cache behavior
    public let behavior: Behavior

    / / / initialized
    public init(behavior: Behavior) {
        self.behavior = behavior
    }
}
Copy the code

The ResposeCacher is then extended to implement the CachedResponseHandler protocol, invoking the Completion callback based on the type of Behavior

extension ResponseCacher: CachedResponseHandler {
    public func dataTask(_ task: URLSessionDataTask.willCacheResponse response: CachedURLResponse.completion: @escaping (CachedURLResponse?). ->Void) {
        switch behavior {
        case .cache:
            // Cache the original data and pass the original data directly to Completion
            completion(response)
        case .doNotCache:
            // No data is cached and nil is passed to Completion
            completion(nil)
        case let .modify(closure):
            // Modify, calling the closure parameter to retrieve the modified data and passing it to Completion
            let response = closure(task, response)
            completion(response)
        }
    }
}
Copy the code

conclusion

The processing of cache in Alamofire is only a simple abstract encapsulation, packaging the operation that originally needs to be handled by proxy into a protocol object to be handled. When we use it, we can simply use ResponseCacher directly or implement our own cache processor for our own business logic, which is very flexible. However, the cache processing of URLSession depends on the cache parameters in the response header, so the validity period and authentication of the cache need to be more dependent on background processing. If the APP needs to cache all the data to the local database by itself, Then you need to use EventModifier + RequestAdapter to manually save the response, and manually read the cache after the request is sent, all depending on the actual business requirements ~

The above is purely personal understanding, unavoidably wrong, if found there is a mistake, welcome to comment pointed out, will be the first time to modify, very grateful ~