KFOptionsSetter.swift 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802
  1. //
  2. // KFOptionsSetter.swift
  3. // Kingfisher
  4. //
  5. // Created by onevcat on 2020/12/22.
  6. //
  7. // Copyright (c) 2020 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. import CoreGraphics
  28. #if os(macOS)
  29. import AppKit
  30. #else
  31. import UIKit
  32. #endif
  33. /// A protocol that Kingfisher can use to perform chained setting in builder pattern.
  34. public protocol KFOptionSetter {
  35. var options: KingfisherParsedOptionsInfo { get nonmutating set }
  36. var onFailureDelegate: Delegate<KingfisherError, Void> { get }
  37. var onSuccessDelegate: Delegate<RetrieveImageResult, Void> { get }
  38. var onProgressDelegate: Delegate<(Int64, Int64), Void> { get }
  39. var delegateObserver: AnyObject { get }
  40. }
  41. extension KF.Builder: KFOptionSetter {
  42. public var delegateObserver: AnyObject { self }
  43. }
  44. // MARK: - Life cycles
  45. extension KFOptionSetter {
  46. /// Sets the progress block to current builder.
  47. ///
  48. /// - Parameter block:
  49. /// Called when the image downloading progress gets updated. If the response does not contain an
  50. /// [`expectedContentLength`](https://developer.apple.com/documentation/foundation/urlresponse/1413507-expectedcontentlength)
  51. /// in the received `URLResponse`, this block will not be called. If `block` is `nil`, the callback will be reset.
  52. ///
  53. /// - Returns: A `Self` value with changes applied.
  54. ///
  55. public func onProgress(_ block: DownloadProgressBlock?) -> Self {
  56. onProgressDelegate.delegate(on: delegateObserver) { (observer, result) in
  57. block?(result.0, result.1)
  58. }
  59. return self
  60. }
  61. /// Sets the the done block to current builder.
  62. /// - Parameter block: Called when the image task successfully completes and the the image set is done. If `block`
  63. /// is `nil`, the callback will be reset.
  64. /// - Returns: A `Self` with changes applied.
  65. ///
  66. public func onSuccess(_ block: ((RetrieveImageResult) -> Void)?) -> Self {
  67. onSuccessDelegate.delegate(on: delegateObserver) { (observer, result) in
  68. block?(result)
  69. }
  70. return self
  71. }
  72. /// Sets the catch block to current builder.
  73. /// - Parameter block: Called when an error happens during the image task. If `block`
  74. /// is `nil`, the callback will be reset.
  75. /// - Returns: A `Self` with changes applied.
  76. ///
  77. public func onFailure(_ block: ((KingfisherError) -> Void)?) -> Self {
  78. onFailureDelegate.delegate(on: delegateObserver) { (observer, error) in
  79. block?(error)
  80. }
  81. return self
  82. }
  83. }
  84. // MARK: - Basic options settings.
  85. extension KFOptionSetter {
  86. /// Sets the target image cache for this task.
  87. ///
  88. /// - Parameter cache: The target cache to be used for the task.
  89. /// - Returns: A `Self` value with changes applied.
  90. ///
  91. /// Kingfisher will utilize the associated ``ImageCache`` object when performing related operations,
  92. /// such as attempting to retrieve cached images and storing downloaded images within it.
  93. ///
  94. public func targetCache(_ cache: ImageCache) -> Self {
  95. options.targetCache = cache
  96. return self
  97. }
  98. /// Sets the target image cache to store the original downloaded image for this task.
  99. ///
  100. /// - Parameter cache: The target cache is about to be used for storing the original downloaded image from the task.
  101. /// - Returns: A `Self` value with changes applied.
  102. ///
  103. /// The ``ImageCache`` for storing and retrieving original images. If ``KingfisherOptionsInfoItem/originalCache(_:)``
  104. /// is contained in the options, it will be preferred for storing and retrieving original images.
  105. /// If there is no ``KingfisherOptionsInfoItem/originalCache(_:)`` in the options,
  106. /// ``KingfisherOptionsInfoItem/targetCache(_:)`` will be used to store original images.
  107. ///
  108. /// When using ``KingfisherManager`` to download and store an image, if
  109. /// ``KingfisherOptionsInfoItem/cacheOriginalImage`` is applied in the option, the original image will be stored to
  110. /// the `cache` you pass as parameter in this method. At the same time, if a requested final image (with processor
  111. /// applied) cannot be found in the cache defined by ``KingfisherOptionsInfoItem/targetCache(_:)``, Kingfisher
  112. /// will try to search the original image to check whether it is already there. If found, it will be used and
  113. /// applied with the given processor. It is an optimization for not downloading the same image for multiple times.
  114. ///
  115. public func originalCache(_ cache: ImageCache) -> Self {
  116. options.originalCache = cache
  117. return self
  118. }
  119. /// Sets the downloader to be used for the image download task.
  120. ///
  121. /// - Parameter downloader: The `ImageDownloader` instance to use for downloading.
  122. /// - Returns: A `Self` value with the changes applied.
  123. ///
  124. /// Kingfisher will utilize the specified ``ImageDownloader`` instance to download requested images.
  125. ///
  126. public func downloader(_ downloader: ImageDownloader) -> Self {
  127. options.downloader = downloader
  128. return self
  129. }
  130. /// Sets the download priority for the image task.
  131. ///
  132. /// - Parameter priority: The download priority of the image download task.
  133. /// - Returns: A `Self` value with changes applied.
  134. ///
  135. /// The `priority` value will be configured as the priority of the image download task. Valid values range between
  136. /// 0.0 and 1.0. You can select a value from `URLSessionTask.defaultPriority`, `URLSessionTask.lowPriority`,
  137. /// or `URLSessionTask.highPriority`. If this option is not set, the default value
  138. /// (`URLSessionTask.defaultPriority`) will be used.
  139. ///
  140. public func downloadPriority(_ priority: Float) -> Self {
  141. options.downloadPriority = priority
  142. return self
  143. }
  144. /// Sets whether Kingfisher should ignore the cache and attempt to initiate a download task for the image source.
  145. ///
  146. /// - Parameter enabled: Enable force refresh or not.
  147. /// - Returns: A `Self` value with the changes applied.
  148. ///
  149. public func forceRefresh(_ enabled: Bool = true) -> Self {
  150. options.forceRefresh = enabled
  151. return self
  152. }
  153. /// Sets whether Kingfisher should attempt to retrieve the image from the memory cache first. If the image is not
  154. /// found in the memory cache, it bypasses the disk cache and initiates a download task for the image source.
  155. ///
  156. /// - Parameter enabled: Enable memory-only cache searching or not.
  157. /// - Returns: A `Self` value with the changes applied.
  158. ///
  159. /// This option is useful when you want to display a changeable image with the same URL during the same app session
  160. /// while avoiding multiple downloads of the same image.
  161. ///
  162. public func fromMemoryCacheOrRefresh(_ enabled: Bool = true) -> Self {
  163. options.fromMemoryCacheOrRefresh = enabled
  164. return self
  165. }
  166. /// Sets whether the image should be cached only in memory and not on disk.
  167. ///
  168. /// - Parameter enabled: Enable memory-only caching for the image or not.
  169. /// - Returns: A `Self` value with the changes applied.
  170. ///
  171. public func cacheMemoryOnly(_ enabled: Bool = true) -> Self {
  172. options.cacheMemoryOnly = enabled
  173. return self
  174. }
  175. /// Sets whether Kingfisher should wait for caching operations to be completed before invoking the `onSuccess`
  176. /// or `onFailure` block.
  177. ///
  178. /// - Parameter enabled: Enable waiting for caching operations or not.
  179. /// - Returns: A `Self` value with the changes applied.
  180. ///
  181. public func waitForCache(_ enabled: Bool = true) -> Self {
  182. options.waitForCache = enabled
  183. return self
  184. }
  185. /// Sets whether Kingfisher should exclusively attempt to retrieve the image from the cache and not from the network.
  186. ///
  187. /// - Parameter enabled: Enable cache-only image retrieval or not.
  188. /// - Returns: A `Self` value with the changes applied.
  189. ///
  190. /// If the image is not found in the cache, the image retrieval will fail with a
  191. /// ``KingfisherError/CacheErrorReason/imageNotExisting(key:)`` error.
  192. ///
  193. public func onlyFromCache(_ enabled: Bool = true) -> Self {
  194. options.onlyFromCache = enabled
  195. return self
  196. }
  197. /// Sets whether the image should be decoded on a background thread before usage.
  198. ///
  199. /// - Parameter enabled: Enable background image decoding or not.
  200. /// - Returns: A `Self` value with the changes applied.
  201. ///
  202. /// When set to `true`, the downloaded image data will be decoded and undergo off-screen rendering to extract pixel
  203. /// information in the background. This can enhance display speed but may consume additional time and memory for
  204. /// image preparation before usage.
  205. ///
  206. public func backgroundDecode(_ enabled: Bool = true) -> Self {
  207. options.backgroundDecode = enabled
  208. return self
  209. }
  210. /// Sets the callback queue used as the target queue for dispatching callbacks when retrieving images from the
  211. /// cache. If not set, Kingfisher will use the main queue for callbacks.
  212. ///
  213. /// - Parameter queue: The target queue on which cache retrieval callbacks will be invoked.
  214. /// - Returns: A `Self` value with the changes applied.
  215. ///
  216. /// - Note: This option does not impact callbacks for UI-related extension methods or ``KFImage`` result handlers.
  217. /// Callbacks for those methods will always be executed on the main queue.
  218. ///
  219. public func callbackQueue(_ queue: CallbackQueue) -> Self {
  220. options.callbackQueue = queue
  221. return self
  222. }
  223. /// Sets the scale factor value used when converting retrieved data to an image.
  224. ///
  225. /// - Parameter factor: The scale factor value to use.
  226. /// - Returns: A `Self` value with the changes applied.
  227. ///
  228. /// Specify the image scale factor, which may differ from your screen's scale. This is particularly important when
  229. /// working with 2x or 3x retina images. Failure to set the correct scale factor may result in Kingfisher
  230. /// converting the data to an image object with a `scale` of 1.0.
  231. ///
  232. public func scaleFactor(_ factor: CGFloat) -> Self {
  233. options.scaleFactor = factor
  234. return self
  235. }
  236. /// Sets whether the original image should be cached, even when the original image has been processed by other ``ImageProcessor``s.
  237. ///
  238. /// - Parameter enabled: Whether to cache the original image.
  239. /// - Returns: A `Self` value with the changes applied.
  240. ///
  241. /// When this option is set, and an ``ImageProcessor`` is used, Kingfisher will attempt to cache both the final
  242. /// processed image and the original image. This ensures that the original image can be reused when another
  243. /// processor is applied to the same resource, without the need for redownloading. You can use
  244. /// ``KingfisherOptionsInfoItem/originalCache(_:)`` to specify a cache for the original images.
  245. ///
  246. /// - Note: The original image will be cached only in disk storage.
  247. ///
  248. public func cacheOriginalImage(_ enabled: Bool = true) -> Self {
  249. options.cacheOriginalImage = enabled
  250. return self
  251. }
  252. /// Sets writing options for an original image on its initial write to disk storage.
  253. ///
  254. /// - Parameter writingOptions: Options that control the data writing operation to disk storage.
  255. /// - Returns: A `Self` value with the changes applied.
  256. ///
  257. /// If these options are set, they will be applied to the storage operation for new files. This can be useful if
  258. /// you want to implement features such as file encryption on the initial write, for example,
  259. /// using `[.completeFileProtection]`.
  260. ///
  261. public func diskStoreWriteOptions(_ writingOptions: Data.WritingOptions) -> Self {
  262. options.diskStoreWriteOptions = writingOptions
  263. return self
  264. }
  265. /// Sets whether disk storage loading should occur in the same calling queue.
  266. ///
  267. /// - Parameter enabled: Whether disk storage loading should happen in the same calling queue.
  268. /// - Returns: A `Self` value with the changes applied.
  269. ///
  270. /// By default, disk storage file loading operates in its own queue with asynchronous dispatch behavior. While this
  271. /// provides better non-blocking disk loading performance, it can result in flickering when reloading an image
  272. /// from disk if the image view already has an image set.
  273. ///
  274. /// Enabling this option prevents flickering by performing all loading in the same queue (typically the UI queue if
  275. /// you are using Kingfisher's extension methods to set an image). However, this may come at the cost of loading
  276. /// performance.
  277. ///
  278. public func loadDiskFileSynchronously(_ enabled: Bool = true) -> Self {
  279. options.loadDiskFileSynchronously = enabled
  280. return self
  281. }
  282. /// Sets the queue on which image processing should occur.
  283. ///
  284. /// - Parameter queue: The queue on which image processing should take place.
  285. /// - Returns: A `Self` value with the changes applied.
  286. ///
  287. /// By default, Kingfisher employs a pre-defined serial queue for image processing. Use this option to modify this
  288. /// behavior. For example, specify `.mainCurrentOrAsync` to process the image on the main queue, which can prevent
  289. /// potential flickering but may lead to UI blocking if the processor requires substantial time to execute.
  290. ///
  291. public func processingQueue(_ queue: CallbackQueue?) -> Self {
  292. options.processingQueue = queue
  293. return self
  294. }
  295. /// Sets the alternative sources to be used when loading the original input `Source` fails.
  296. ///
  297. /// - Parameter sources: The alternative sources to be used.
  298. /// - Returns: A `Self` value with the changes applied.
  299. ///
  300. /// The values in the `sources` array will be employed to initiate a new image loading task if the previous task
  301. /// fails due to an error. The image source loading process will terminate as soon as one of the alternative
  302. /// sources is successfully loaded. If all `sources` are used but loading still fails,
  303. /// a ``KingfisherError/ImageSettingErrorReason/alternativeSourcesExhausted(_:)`` error will be thrown in the
  304. /// `catch` block.
  305. ///
  306. /// This feature is valuable when implementing a fallback solution for setting images.
  307. ///
  308. /// - Note: User cancellation or calling on ``DownloadTask/cancel()`` on ``DownloadTask`` will not trigger the
  309. /// loading of alternative sources.
  310. ///
  311. public func alternativeSources(_ sources: [Source]?) -> Self {
  312. options.alternativeSources = sources
  313. return self
  314. }
  315. /// Sets a retry strategy to be used when issues arise during image retrieval.
  316. ///
  317. /// - Parameter strategy: The provided strategy that defines how retry attempts should occur.
  318. /// - Returns: A `Self` value with the changes applied.
  319. ///
  320. public func retry(_ strategy: RetryStrategy?) -> Self {
  321. options.retryStrategy = strategy
  322. return self
  323. }
  324. /// Sets a retry strategy with a maximum retry count and retry interval.
  325. ///
  326. /// - Parameters:
  327. /// - maxCount: The maximum number of retry attempts before the retry stops.
  328. /// - interval: The time interval between each retry attempt.
  329. /// - Returns: A `Self` value with the changes applied.
  330. ///
  331. /// This defines a straightforward retry strategy that retries a failing request for a specified number of times
  332. /// with a designated time interval between each attempt. For example, `.retry(maxCount: 3, interval: .second(3))`
  333. /// indicates a maximum of three retry attempts, with a 3-second pause between each retry if the previous attempt
  334. /// fails.
  335. ///
  336. public func retry(maxCount: Int, interval: DelayRetryStrategy.Interval = .seconds(3)) -> Self {
  337. let strategy = DelayRetryStrategy(maxRetryCount: maxCount, retryInterval: interval)
  338. options.retryStrategy = strategy
  339. return self
  340. }
  341. /// Sets the `Source` to be loaded when the user enables Low Data Mode and the original source fails with an
  342. /// `NSURLErrorNetworkUnavailableReason.constrained` error.
  343. ///
  344. /// - Parameter source: The `Source` to be loaded under low data mode.
  345. /// - Returns: A `Self` value with the changes applied.
  346. ///
  347. /// When this option is set, the `allowsConstrainedNetworkAccess` property of the request for the original source
  348. /// will be set to `false`, and the specified ``Source`` will be used to retrieve the image in low data mode.
  349. /// Typically, you can provide a low-resolution version of your image or a local image provider to display a
  350. /// placeholder.
  351. ///
  352. /// If this option is not set or the `source` is `nil`, the device's Low Data Mode setting will be disregarded,
  353. /// and the original source will be loaded following the system's default behavior in a regular manner.
  354. ///
  355. public func lowDataModeSource(_ source: Source?) -> Self {
  356. options.lowDataModeSource = source
  357. return self
  358. }
  359. /// Sets whether the image setting for an image view should include a transition even when the image is retrieved
  360. /// from the cache.
  361. ///
  362. /// - Parameter enabled: Enable the use of a transition or not.
  363. /// - Returns: A `Self` value with the changes applied.
  364. ///
  365. public func forceTransition(_ enabled: Bool = true) -> Self {
  366. options.forceTransition = enabled
  367. return self
  368. }
  369. /// Sets the image to be used in the event of a failure during image retrieval.
  370. ///
  371. /// - Parameter image: The image to be used when an error occurs.
  372. /// - Returns: A `Self` value with the changes applied.
  373. ///
  374. /// If this option is set and an image retrieval error occurs, Kingfisher will use the provided image (or an empty
  375. /// image) in place of the requested one. This is useful when you do not want to display a placeholder during the
  376. /// loading process but prefer to use a default image when requests fail.
  377. ///
  378. public func onFailureImage(_ image: KFCrossPlatformImage?) -> Self {
  379. options.onFailureImage = .some(image)
  380. return self
  381. }
  382. }
  383. // MARK: - Request Modifier
  384. extension KFOptionSetter {
  385. /// Sets an ``ImageDownloadRequestModifier`` to alter the image download request before it is sent.
  386. ///
  387. /// - Parameter modifier: The modifier to be used for changing the request before it is sent.
  388. /// - Returns: A `Self` value with the changes applied.
  389. ///
  390. /// This is your last opportunity to modify the image download request. You can use this for customization
  391. /// purposes, such as adding an authentication token to the header, implementing basic HTTP authentication,
  392. /// or URL mapping.
  393. public func requestModifier(_ modifier: AsyncImageDownloadRequestModifier) -> Self {
  394. options.requestModifier = modifier
  395. return self
  396. }
  397. /// Sets a block to modify the image download request before it is sent.
  398. ///
  399. /// - Parameter modifyBlock: The modifying block that will be called to change the request before it is sent.
  400. /// - Returns: A `Self` value with the changes applied.
  401. ///
  402. /// This is your last opportunity to modify the image download request. You can use this for customization purposes,
  403. /// such as adding an authentication token to the header, implementing basic HTTP authentication, or URL mapping.
  404. ///
  405. public func requestModifier(_ modifyBlock: @escaping (inout URLRequest) -> Void) -> Self {
  406. options.requestModifier = AnyModifier { r -> URLRequest? in
  407. var request = r
  408. modifyBlock(&request)
  409. return request
  410. }
  411. return self
  412. }
  413. }
  414. // MARK: - Redirect Handler
  415. extension KFOptionSetter {
  416. /// Sets an `ImageDownloadRedirectHandler` to modify the image download request during redirection.
  417. ///
  418. /// - Parameter handler: The handler to be used for redirection.
  419. /// - Returns: A `Self` value with the changes applied.
  420. ///
  421. /// This provides an opportunity to modify the image download request during redirection. You can use this for
  422. /// customization purposes, such as adding an authentication token to the header, implementing basic HTTP
  423. /// authentication, or URL mapping. By default, the original redirection request will be sent without any
  424. /// modification.
  425. ///
  426. public func redirectHandler(_ handler: ImageDownloadRedirectHandler) -> Self {
  427. options.redirectHandler = handler
  428. return self
  429. }
  430. /// Sets a block to modify the image download request during redirection.
  431. ///
  432. /// - Parameter block: The block to be used for redirection.
  433. /// - Returns: A `Self` value with the changes applied.
  434. ///
  435. /// This provides an opportunity to modify the image download request during redirection. You can use this for
  436. /// customization purposes, such as adding an authentication token to the header, implementing basic HTTP
  437. /// authentication, or URL mapping. By default, the original redirection request will be sent without any
  438. /// modification.
  439. ///
  440. public func redirectHandler(_ block: @escaping (KF.RedirectPayload) -> Void) -> Self {
  441. let redirectHandler = AnyRedirectHandler { (task, response, request, handler) in
  442. let payload = KF.RedirectPayload(
  443. task: task, response: response, newRequest: request, completionHandler: handler
  444. )
  445. block(payload)
  446. }
  447. options.redirectHandler = redirectHandler
  448. return self
  449. }
  450. }
  451. // MARK: - Processor
  452. extension KFOptionSetter {
  453. /// Sets an image processor for the image task, replacing the current image processor settings.
  454. ///
  455. /// - Parameter processor: The processor to use for processing the image after it is downloaded.
  456. /// - Returns: A `Self` value with the changes applied.
  457. ///
  458. /// - Note: To append a processor to the current ones instead of replacing them all, use ``appendProcessor(_:)``.
  459. ///
  460. public func setProcessor(_ processor: ImageProcessor) -> Self {
  461. options.processor = processor
  462. return self
  463. }
  464. /// Sets an array of image processors for the image task, replacing the current image processor settings.
  465. ///
  466. /// - Parameter processors: An array of processors. The processors in this array will be concatenated one by one to
  467. /// form a processor pipeline.
  468. /// - Returns: A `Self` value with the changes applied.
  469. ///
  470. /// - Note: To append processors to the current ones instead of replacing them all, concatenate them using the
  471. /// `|>` operator, and then use ``KFOptionSetter/appendProcessor(_:)``.
  472. ///
  473. public func setProcessors(_ processors: [ImageProcessor]) -> Self {
  474. switch processors.count {
  475. case 0:
  476. options.processor = DefaultImageProcessor.default
  477. case 1...:
  478. options.processor = processors.dropFirst().reduce(processors[0]) { $0 |> $1 }
  479. default:
  480. assertionFailure("Never happen")
  481. }
  482. return self
  483. }
  484. /// Appends a processor to the current set of processors.
  485. ///
  486. /// - Parameter processor: The processor to append to the current processor settings.
  487. /// - Returns: A `Self` value with the changes applied.
  488. ///
  489. public func appendProcessor(_ processor: ImageProcessor) -> Self {
  490. options.processor = options.processor |> processor
  491. return self
  492. }
  493. /// Appends a ``RoundCornerImageProcessor`` to the current set of processors.
  494. ///
  495. /// - Parameters:
  496. /// - radius: The radius to apply during processing. Specify a certain point value with `.point`, or a fraction
  497. /// of the target image with `.widthFraction` or `.heightFraction`. For example, with a square image where width
  498. /// and height are equal, `.widthFraction(0.5)` means using half of the length of the size to make the final
  499. /// image round.
  500. /// - targetSize: The target size for the output image. If `nil`, the image will retain its original size after
  501. /// processing.
  502. /// - corners: The target corners to round.
  503. /// - backgroundColor: The background color of the output image. If `nil`, a transparent background will be used.
  504. /// - Returns: A `Self` value with the changes applied.
  505. ///
  506. public func roundCorner(
  507. radius: Radius,
  508. targetSize: CGSize? = nil,
  509. roundingCorners corners: RectCorner = .all,
  510. backgroundColor: KFCrossPlatformColor? = nil
  511. ) -> Self
  512. {
  513. let processor = RoundCornerImageProcessor(
  514. radius: radius,
  515. targetSize: targetSize,
  516. roundingCorners: corners,
  517. backgroundColor: backgroundColor
  518. )
  519. return appendProcessor(processor)
  520. }
  521. /// Appends a ``BlurImageProcessor`` to the current set of processors.
  522. ///
  523. /// - Parameter radius: The blur radius for simulating Gaussian blur.
  524. /// - Returns: A `Self` value with the changes applied.
  525. ///
  526. public func blur(radius: CGFloat) -> Self {
  527. appendProcessor(
  528. BlurImageProcessor(blurRadius: radius)
  529. )
  530. }
  531. /// Appends an ``OverlayImageProcessor`` to the current set of processors.
  532. ///
  533. /// - Parameters:
  534. /// - color: The overlay color to be used when overlaying the input image.
  535. /// - fraction: The fraction to be used when overlaying the color onto the image.
  536. /// - Returns: A `Self` value with the changes applied.
  537. ///
  538. public func overlay(color: KFCrossPlatformColor, fraction: CGFloat = 0.5) -> Self {
  539. appendProcessor(
  540. OverlayImageProcessor(overlay: color, fraction: fraction)
  541. )
  542. }
  543. /// Appends a ``TintImageProcessor`` to the current set of processors.
  544. ///
  545. /// - Parameter color: The tint color to be used for tinting the input image.
  546. /// - Returns: A `Self` value with the changes applied.
  547. ///
  548. public func tint(color: KFCrossPlatformColor) -> Self {
  549. appendProcessor(
  550. TintImageProcessor(tint: color)
  551. )
  552. }
  553. /// Appends a ``BlackWhiteProcessor`` to the current set of processors.
  554. ///
  555. /// - Returns: A `Self` value with the changes applied.
  556. ///
  557. public func blackWhite() -> Self {
  558. appendProcessor(
  559. BlackWhiteProcessor()
  560. )
  561. }
  562. /// Appends a ``CroppingImageProcessor`` to the current set of processors.
  563. ///
  564. /// - Parameters:
  565. /// - size: The target size for the output image.
  566. /// - anchor: The anchor point from which the output size should be calculated. The anchor point is represented
  567. /// by two values between 0.0 and 1.0, indicating a relative point in the current image. See
  568. /// ``CroppingImageProcessor/init(size:anchor:)`` for more details.
  569. /// - Returns: A `Self` value with the changes applied.
  570. ///
  571. public func cropping(size: CGSize, anchor: CGPoint = .init(x: 0.5, y: 0.5)) -> Self {
  572. appendProcessor(
  573. CroppingImageProcessor(size: size, anchor: anchor)
  574. )
  575. }
  576. /// Appends a ``DownsamplingImageProcessor`` to the current set of processors.
  577. ///
  578. /// Compared to the ``ResizingImageProcessor``, the ``DownsamplingImageProcessor`` doesn't render the original
  579. /// images and then resize them. Instead, it directly downsamples the input data to a thumbnail image, making it
  580. /// more efficient than the ``ResizingImageProcessor``. It is recommended to use the ``DownsamplingImageProcessor``
  581. /// whenever possible instead of the ``ResizingImageProcessor``.
  582. ///
  583. /// - Parameter size: The target size for the output image. It should be smaller than the size of the input image. If it is larger, the resulting image will be the same size as the input data without downsampling.
  584. /// - Returns: A `Self` value with the changes applied.
  585. ///
  586. /// - Note: Only CG-based images are supported, and animated images (e.g., GIF) are not supported.
  587. ///
  588. public func downsampling(size: CGSize) -> Self {
  589. let processor = DownsamplingImageProcessor(size: size)
  590. if options.processor == DefaultImageProcessor.default {
  591. return setProcessor(processor)
  592. } else {
  593. return appendProcessor(processor)
  594. }
  595. }
  596. /// Appends a ``ResizingImageProcessor`` to the current set of processors.
  597. ///
  598. /// If you need to resize a data-represented image to a smaller size, it is recommended to use the
  599. /// ``DownsamplingImageProcessor`` instead, which is more efficient and uses less memory.
  600. ///
  601. /// - Parameters:
  602. /// - referenceSize: The reference size for the resizing operation in points.
  603. /// - mode: The target content mode for the output image. The default is `.none`.
  604. /// - Returns: A `Self` value with the changes applied.
  605. ///
  606. public func resizing(referenceSize: CGSize, mode: ContentMode = .none) -> Self {
  607. appendProcessor(
  608. ResizingImageProcessor(referenceSize: referenceSize, mode: mode)
  609. )
  610. }
  611. }
  612. // MARK: - Cache Serializer
  613. extension KFOptionSetter {
  614. /// Uses a specified ``CacheSerializer`` to convert data to an image object for retrieval from the disk cache or
  615. /// vice versa for storage to the disk cache.
  616. ///
  617. /// - Parameter cacheSerializer: The ``CacheSerializer`` to be used.
  618. /// - Returns: A `Self` value with the changes applied.
  619. ///
  620. public func serialize(by cacheSerializer: CacheSerializer) -> Self {
  621. options.cacheSerializer = cacheSerializer
  622. return self
  623. }
  624. /// Uses a specified format to serialize the image data to disk. It converts the image object to the given data
  625. /// format.
  626. ///
  627. /// - Parameters:
  628. /// - format: The desired data encoding format when storing the image on disk.
  629. /// - jpegCompressionQuality: If the format is ``ImageFormat/JPEG``, it specifies the compression quality when
  630. /// converting the image to JPEG data. Otherwise, it is ignored.
  631. /// - Returns: A `Self` value with the changes applied.
  632. ///
  633. public func serialize(as format: ImageFormat, jpegCompressionQuality: CGFloat? = nil) -> Self {
  634. let cacheSerializer: FormatIndicatedCacheSerializer
  635. switch format {
  636. case .JPEG:
  637. cacheSerializer = .jpeg(compressionQuality: jpegCompressionQuality ?? 1.0)
  638. case .PNG:
  639. cacheSerializer = .png
  640. case .GIF:
  641. cacheSerializer = .gif
  642. case .unknown:
  643. cacheSerializer = .png
  644. }
  645. options.cacheSerializer = cacheSerializer
  646. return self
  647. }
  648. }
  649. // MARK: - Image Modifier
  650. extension KFOptionSetter {
  651. /// Sets an ``ImageModifier`` for the image task. Use this to modify the fetched image object's properties if needed.
  652. ///
  653. /// If the image was fetched directly from the downloader, the modifier will run directly after the
  654. /// ``ImageProcessor``. If the image is being fetched from a cache, the modifier will run after the
  655. /// ``CacheSerializer``.
  656. ///
  657. /// - Parameter modifier: The ``ImageModifier`` to be used for modifying the image object.
  658. /// - Returns: A `Self` value with the changes applied.
  659. ///
  660. public func imageModifier(_ modifier: ImageModifier?) -> Self {
  661. options.imageModifier = modifier
  662. return self
  663. }
  664. /// Sets a block to modify the image object. Use this to modify the fetched image object's properties if needed.
  665. ///
  666. /// If the image was fetched directly from the downloader, the modifier block will run directly after the
  667. /// ``ImageProcessor``. If the image is being fetched from a cache, the modifier will run after the
  668. /// ``CacheSerializer``.
  669. ///
  670. /// - Parameter block: The block used to modify the image object.
  671. /// - Returns: A `Self` value with the changes applied.
  672. ///
  673. public func imageModifier(_ block: @escaping (inout KFCrossPlatformImage) throws -> Void) -> Self {
  674. let modifier = AnyImageModifier { image -> KFCrossPlatformImage in
  675. var image = image
  676. try block(&image)
  677. return image
  678. }
  679. options.imageModifier = modifier
  680. return self
  681. }
  682. }
  683. // MARK: - Cache Expiration
  684. extension KFOptionSetter {
  685. /// Sets the expiration setting for the memory cache of this image task.
  686. ///
  687. /// By default, the underlying ``MemoryStorage/Backend`` uses the expiration in its configuration for all items.
  688. /// If set, the ``MemoryStorage/Backend`` will use this value to overwrite the configuration setting for this
  689. /// caching item.
  690. ///
  691. /// - Parameter expiration: The expiration setting used in cache storage.
  692. /// - Returns: A `Self` value with the changes applied.
  693. ///
  694. public func memoryCacheExpiration(_ expiration: StorageExpiration?) -> Self {
  695. options.memoryCacheExpiration = expiration
  696. return self
  697. }
  698. /// Sets the expiration extending setting for the memory cache. The item expiration time will be incremented by this
  699. /// value after access.
  700. ///
  701. /// By default, the underlying ``MemoryStorage/Backend`` uses the initial cache expiration as the extending value:
  702. /// ``ExpirationExtending/cacheTime``.
  703. ///
  704. /// To disable the extending option entirely, set `.none` to it.
  705. ///
  706. /// - Parameter extending: The expiration extending setting used in cache storage.
  707. /// - Returns: A `Self` value with the changes applied.
  708. ///
  709. public func memoryCacheAccessExtending(_ extending: ExpirationExtending) -> Self {
  710. options.memoryCacheAccessExtendingExpiration = extending
  711. return self
  712. }
  713. /// Sets the expiration setting for the disk cache of this image task.
  714. ///
  715. /// By default, the underlying ``DiskStorage/Backend`` uses the expiration in its configuration for all items.
  716. /// If set, the ``DiskStorage/Backend`` will use this value to overwrite the configuration setting for this caching
  717. /// item.
  718. ///
  719. /// - Parameter expiration: The expiration setting used in cache storage.
  720. /// - Returns: A `Self` value with the changes applied.
  721. ///
  722. public func diskCacheExpiration(_ expiration: StorageExpiration?) -> Self {
  723. options.diskCacheExpiration = expiration
  724. return self
  725. }
  726. /// Sets the expiration extending setting for the disk cache. The item expiration time will be incremented by this
  727. /// value after access.
  728. ///
  729. /// By default, the underlying ``DiskStorage.Backend`` uses the initial cache expiration as the extending
  730. /// value: ``ExpirationExtending/cacheTime``.
  731. ///
  732. /// To disable the extending option entirely, set `.none` to it.
  733. ///
  734. /// - Parameter extending: The expiration extending setting used in cache storage.
  735. /// - Returns: A `Self` value with the changes applied.
  736. ///
  737. public func diskCacheAccessExtending(_ extending: ExpirationExtending) -> Self {
  738. options.diskCacheAccessExtendingExpiration = extending
  739. return self
  740. }
  741. }