ImageDownloaderDelegate.swift 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. //
  2. // ImageDownloaderDelegate.swift
  3. // Kingfisher
  4. //
  5. // Created by Wei Wang on 2018/10/11.
  6. //
  7. // Copyright (c) 2019 Wei Wang <onevcat@gmail.com>
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining a copy
  10. // of this software and associated documentation files (the "Software"), to deal
  11. // in the Software without restriction, including without limitation the rights
  12. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. // copies of the Software, and to permit persons to whom the Software is
  14. // furnished to do so, subject to the following conditions:
  15. //
  16. // The above copyright notice and this permission notice shall be included in
  17. // all copies or substantial portions of the Software.
  18. //
  19. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. // THE SOFTWARE.
  26. import Foundation
  27. #if os(macOS)
  28. import AppKit
  29. #else
  30. import UIKit
  31. #endif
  32. /// Protocol for handling events for ``ImageDownloader``.
  33. ///
  34. /// This delegate protocol provides a set of methods related to the stages and rules of the image downloader. You use
  35. /// the provided methods to inspect the downloader working phases or respond to some events to make decisions.
  36. public protocol ImageDownloaderDelegate: AnyObject {
  37. /// Called when the ``ImageDownloader`` object is about to start downloading an image from a specified URL.
  38. ///
  39. /// - Parameters:
  40. /// - downloader: The ``ImageDownloader`` object used for the downloading operation.
  41. /// - url: The URL of the starting request.
  42. /// - request: The request object for the download process.
  43. func imageDownloader(_ downloader: ImageDownloader, willDownloadImageForURL url: URL, with request: URLRequest?)
  44. /// Called when the ``ImageDownloader`` completes a downloading request with success or failure.
  45. ///
  46. /// - Parameters:
  47. /// - downloader: The ``ImageDownloader`` object used for the downloading operation.
  48. /// - url: The URL of the original request.
  49. /// - response: The response object of the downloading process.
  50. /// - error: The error in case of failure.
  51. func imageDownloader(
  52. _ downloader: ImageDownloader,
  53. didFinishDownloadingImageForURL url: URL,
  54. with response: URLResponse?,
  55. error: Error?)
  56. /// Called when the ``ImageDownloader`` object successfully downloads image data with a specified task.
  57. ///
  58. /// This is your last chance to verify or modify the downloaded data before Kingfisher attempts to perform
  59. /// additional processing on the image data.
  60. ///
  61. /// - Parameters:
  62. /// - downloader: The ``ImageDownloader`` object used for the downloading operation.
  63. /// - data: The original downloaded data.
  64. /// - dataTask: The data task containing request and response information for the download.
  65. ///
  66. /// - Returns: The data that Kingfisher should use to create an image. You need to provide valid data that is in
  67. /// one of the supported image file formats. Kingfisher will process this data and attempt to convert it into an
  68. /// image object.
  69. ///
  70. ///
  71. /// This method can be used to preprocess raw image data before the creation of the `Image` instance (e.g.,
  72. /// decrypting or verification). If `nil` is returned, the processing is interrupted and a
  73. /// ``KingfisherError/ResponseErrorReason/dataModifyingFailed(task:)`` error will be raised. You can use this fact
  74. /// to stop the image processing flow if you find that the data is corrupted or malformed.
  75. ///
  76. /// > If this method is implemented, the `URL` version of
  77. /// ``ImageDownloaderDelegate/imageDownloader(_:didDownload:for:)-5btcl`` will not be called anymore.
  78. func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, with dataTask: SessionDataTask) -> Data?
  79. /// Called when the ``ImageDownloader`` object successfully downloads image data from a specified URL.
  80. ///
  81. /// This is your last chance to verify or modify the downloaded data before Kingfisher attempts to perform
  82. /// additional processing on the image data.
  83. ///
  84. /// - Parameters:
  85. /// - downloader: The ``ImageDownloader`` object used for the downloading operation.
  86. /// - data: The original downloaded data.
  87. /// - url: The URL of the original request.
  88. ///
  89. /// - Returns: The data that Kingfisher should use to create an image. You need to provide valid data that is in
  90. /// one of the supported image file formats. Kingfisher will process this data and attempt to convert it into an
  91. /// image object.
  92. ///
  93. /// This method can be used to preprocess raw image data before the creation of the `Image` instance (e.g.,
  94. /// decrypting or verification). If `nil` is returned, the processing is interrupted and a
  95. /// ``KingfisherError/ResponseErrorReason/dataModifyingFailed(task:)`` error will be raised. You can use this fact
  96. /// to stop the image processing flow if you find that the data is corrupted or malformed.
  97. ///
  98. /// > If the ``SessionDataTask`` version of `imageDownloader(_:didDownload:with:)` is implemented, this method will
  99. /// > not be called anymore.
  100. func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, for url: URL) -> Data?
  101. /// Called when the ``ImageDownloader`` object successfully downloads and processes an image from a specified URL.
  102. ///
  103. /// - Parameters:
  104. /// - downloader: The ``ImageDownloader`` object used for the downloading operation.
  105. /// - image: The downloaded and processed image.
  106. /// - url: The URL of the original request.
  107. /// - response: The original response object of the downloading process.
  108. func imageDownloader(
  109. _ downloader: ImageDownloader,
  110. didDownload image: KFCrossPlatformImage,
  111. for url: URL,
  112. with response: URLResponse?)
  113. /// Checks if a received HTTP status code is valid or not.
  114. ///
  115. /// By default, a status code in the range `200..<400` is considered as valid. If an invalid code is received,
  116. /// the downloader will raise a ``KingfisherError/ResponseErrorReason/invalidHTTPStatusCode(response:)`` error.
  117. ///
  118. /// - Parameters:
  119. /// - code: The received HTTP status code.
  120. /// - downloader: The ``ImageDownloader`` object requesting validation of the status code.
  121. /// - Returns: A value indicating whether this HTTP status code is valid or not.
  122. ///
  123. /// > If the default range of `200..<400` as valid codes does not suit your needs, you can implement this method to
  124. /// change that behavior.
  125. func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool
  126. /// Called when the task has received a valid HTTP response after passing other checks such as the status code.
  127. /// You can perform additional checks or verifications on the response to determine if the download should be
  128. /// allowed or cancelled.
  129. ///
  130. /// For example, this is useful if you want to verify some header values in the response before actually starting
  131. /// the download.
  132. ///
  133. /// If implemented, you have to return a proper response disposition, such as `.allow` to start the actual
  134. /// downloading or `.cancel` to cancel the task. If `.cancel` is used as the disposition, the downloader will raise
  135. /// a ``KingfisherError/ResponseErrorReason/cancelledByDelegate(response:)`` error. If not implemented, any response
  136. /// that passes other checks will be allowed, and the download will start.
  137. ///
  138. /// - Parameters:
  139. /// - downloader: The `ImageDownloader` object used for the downloading operation.
  140. /// - response: The original response object of the downloading process.
  141. ///
  142. /// - Returns: The disposition for the download task. You have to return either `.allow` or `.cancel`.
  143. func imageDownloader(
  144. _ downloader: ImageDownloader,
  145. didReceive response: URLResponse
  146. ) async -> URLSession.ResponseDisposition
  147. }
  148. // Default implementation for `ImageDownloaderDelegate`.
  149. extension ImageDownloaderDelegate {
  150. public func imageDownloader(
  151. _ downloader: ImageDownloader,
  152. willDownloadImageForURL url: URL,
  153. with request: URLRequest?) {}
  154. public func imageDownloader(
  155. _ downloader: ImageDownloader,
  156. didFinishDownloadingImageForURL url: URL,
  157. with response: URLResponse?,
  158. error: Error?) {}
  159. public func imageDownloader(
  160. _ downloader: ImageDownloader,
  161. didDownload image: KFCrossPlatformImage,
  162. for url: URL,
  163. with response: URLResponse?) {}
  164. public func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool {
  165. return (200..<400).contains(code)
  166. }
  167. public func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, with task: SessionDataTask) -> Data? {
  168. guard let url = task.originalURL else {
  169. return data
  170. }
  171. return imageDownloader(downloader, didDownload: data, for: url)
  172. }
  173. public func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, for url: URL) -> Data? {
  174. return data
  175. }
  176. public func imageDownloader(
  177. _ downloader: ImageDownloader,
  178. didReceive response: URLResponse
  179. ) async -> URLSession.ResponseDisposition {
  180. .allow
  181. }
  182. }