This is the 15th day of my participation in Gwen Challenge

This article is received at the time of reading the Kingfisher source.

Other series

Kingfisher (1)

Kingfisher source code reading Notes (2)

Kingfisher source code reading Notes (3)

Kingfisher source code reading Notes (4)

The main classes in Kingfisher responsible for image download logic are:

  • ImageDownloaderThe main classes that add and remove network requests;
  • SessionDelegateResponsible for all urls and correspondenceSessionDataTaskAnd as aURLSessionDataTaskTo handle the request process.
  • SessionDataTaskResponsible for aURLSessionDataTaskAnd all callback notifications associated with it;

Going from URL to UIImage

The entire process from a URL to a UIImage includes:

  1. Create URLRequest based on the URL, set whether pipelining is enabled, whether the connection can use the network in Low Data Mode,
  2. Check whether the user needs to modify the URLRequest, such as adding a token to the header, performing basic HTTP authentication, or url mapping.
  3. Check whether the URLSessionDataTask corresponding to the URLRequest already exists to avoid creating multiple URLSessionDataTask for the same URLRequest. If no, create URLSessionDataTask and set its priority.
  4. Set the URLSessionDataTask callback and start the task;
  5. Handle authentication challenges and redirects that may occur in network requests.
  6. Notify the outside world when data is received from the server;
  7. If the user cancels a network request, the corresponding callback is removed and the URLSessionDataTask is not actually stopped. Unless all listening URLSessionDataTask callbacks are cancelled.
  8. Filter HTTP statusCode, the default acceptable value is 200 to 400;
  9. After receiving all the data, convert the data to UIImage;
  10. Determines whether the UIImage needs to be decoded, and if it needs to be decrypted, the image is drawn in CGContext and data is returned from it. Decoding improves drawing performance when UIImage is just created from data but not displayed for the first time, but requires more time to prepare the data;
  11. Notifies final data results in user-specified queues.

URLSession

Kingfisher internally uses URLSession processing to process network requests. Initialize URLSession configuration for URLSessionConfiguration. Ephemeral.

open var sessionConfiguration = URLSessionConfiguration.ephemeral {
        didSet {
            session.invalidateAndCancel()
            session = URLSession(configuration: sessionConfiguration, delegate: sessionDelegate, delegateQueue: nil)
        }
    }
Copy the code

URLSessionConfiguration Configures session attributes, such as timeout, HTTP header, and cache policy. There are three ways to create it:

  • URLSessionConfiguration.default: default instance creation, persistent global cache on hard disk, store credential to user keychain, store cookie to shareCookie;
  • URLSessionConfiguration.ephemeral: The only difference with default is that all the data related to the session is stored in memory.
  • URLSessionConfiguration.background(withIdentifier:): Lets the session perform upload or download tasks in the background. Transmission continues even if the application itself is suspended or terminated.

Set URLRequest pipeline and low data mode

Kingfisher creates URLRequest as follows:

var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: downloadTimeout)
request.httpShouldUsePipelining = requestsUsePipelining
if #available(macOS 10.15.iOS 13.0.watchOS 6.0.tvOS 13.0.*),
   options.lowDataModeSource ! = nil {
    request.allowsConstrainedNetworkAccess = false
}
Copy the code

Among them:

  • HttpShouldUsePipelining: Sets whether pipelining is enabled. By default, requests and responses are sequential, meaning that after one request is sent, a second request can only be sent after the response is received. If you havehttpShouldUsePipeliningIf set to true, new requests are allowed to be sent before the previous response is received.
  • AllowsConstrainedNetworkAccess: set Low when the user to specify the Data model (Low Data Mode) connection whether can use the Internet, the default is true.

Check whether URLSessionDataTask has been cancelled

Kingfisher’s authors do not use task.state to determine whether URLSessionDataTask has been cancelled. =.running Because sometimes cancelling a task does not immediately change to.cancelling. Therefore, the author determines by the number of callback listeners.

We should be able to use task.state ! = .running to check it. However, in some rare cases, cancelling the task does not change task state to .cancelling immediately, but still in .running. So we need to check callbacks count to for sure that it is safe to remove the task in delegate.

private var callbacksStore = [CancelToken: TaskCallback] ()var containsCallbacks: Bool {
    return !callbacks.isEmpty
}

var callbacks: [SessionDataTask.TaskCallback] {
    lock.lock()
    defer { lock.unlock() }
    return Array(callbacksStore.values)
}
Copy the code

CancelToken is the unique value of the callback, and TaskCallback is the corresponding callback.