RequestModifier.swift 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. //
  2. // RequestModifier.swift
  3. // Kingfisher
  4. //
  5. // Created by Wei Wang on 2016/09/05.
  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. /// Represents and wraps a method for modifying a request before an image download request starts asynchronously.
  28. ///
  29. /// Usually, you pass an ``AsyncImageDownloadRequestModifier`` instance as the associated value of
  30. /// ``KingfisherOptionsInfoItem/requestModifier(_:)`` and use it as the `options` parameter in related methods.
  31. ///
  32. /// For example, the code below defines a modifier to add a header field and its value to the request.
  33. ///
  34. /// ```swift
  35. /// class HeaderFieldModifier: AsyncImageDownloadRequestModifier {
  36. /// var onDownloadTaskStarted: ((DownloadTask?) -> Void)? = nil
  37. /// func modified(for request: URLRequest) async -> URLRequest? {
  38. /// var r = request
  39. /// let token = await service.fetchToken()
  40. /// r.setValue(token, forHTTPHeaderField: "token")
  41. /// return r
  42. /// }
  43. /// }
  44. ///
  45. /// imageView.kf.setImage(with: url, options: [.requestModifier(HeaderFieldModifier())])
  46. /// ```
  47. public protocol AsyncImageDownloadRequestModifier: Sendable {
  48. /// This method will be called just before the `request` is sent.
  49. ///
  50. /// This is the last chance to modify the image download request. You can modify the request for some customizing
  51. /// purposes, such as adding an auth token to the header, performing basic HTTP auth, or something like URL mapping.
  52. ///
  53. /// After making the modification, you should return the modified request, and the data will be downloaded with
  54. /// this modified request.
  55. ///
  56. /// > If you do nothing with the input `request` and return it as-is, the download process will start with it as the
  57. /// modifier doesn't exist. If you return `nil`, the downloading will be interrupted with an
  58. /// ``KingfisherError/RequestErrorReason/emptyRequest`` error.
  59. ///
  60. /// - Parameter request: The input request contains necessary information like `url`. This request is generated
  61. /// according to your resource URL as a GET request.
  62. /// - Returns: The modified request which should be used to trigger the download.
  63. func modified(for request: URLRequest) async -> URLRequest?
  64. /// A block that will be called when the download task starts.
  65. ///
  66. /// If an ``AsyncImageDownloadRequestModifier`` and asynchronous modification occur before the download, the
  67. /// related download method will not return a valid ``DownloadTask`` value. Instead, you can get one from this
  68. /// method.
  69. ///
  70. /// User the ``DownloadTask`` value to track the task, or cancel it when you need to.
  71. var onDownloadTaskStarted: (@Sendable (DownloadTask?) -> Void)? { get }
  72. }
  73. /// Represents and wraps a method for modifying a request before an image download request starts synchronously.
  74. ///
  75. /// Usually, you pass an ``ImageDownloadRequestModifier`` instance as the associated value of
  76. /// ``KingfisherOptionsInfoItem/requestModifier(_:)`` and use it as the `options` parameter in related methods.
  77. ///
  78. /// For example, the code below defines a modifier to add a header field and its value to the request.
  79. ///
  80. /// ```swift
  81. /// class HeaderFieldModifier: AsyncImageDownloadRequestModifier {
  82. /// func modified(for request: URLRequest) -> URLRequest? {
  83. /// var r = request
  84. /// r.setValue("value", forHTTPHeaderField: "key")
  85. /// return r
  86. /// }
  87. /// }
  88. ///
  89. /// imageView.kf.setImage(with: url, options: [.requestModifier(HeaderFieldModifier())])
  90. /// ```
  91. public protocol ImageDownloadRequestModifier: AsyncImageDownloadRequestModifier {
  92. /// This method will be called just before the `request` is sent.
  93. ///
  94. /// This is the last chance to modify the image download request. You can modify the request for some customizing
  95. /// purposes, such as adding an auth token to the header, performing basic HTTP auth, or something like URL mapping.
  96. ///
  97. /// After making the modification, you should return the modified request, and the data will be downloaded with
  98. /// this modified request.
  99. ///
  100. /// > If you do nothing with the input `request` and return it as-is, the download process will start with it as the
  101. /// modifier doesn't exist. If you return `nil`, the downloading will be interrupted with an
  102. /// ``KingfisherError/RequestErrorReason/emptyRequest`` error.
  103. ///
  104. /// > Tip: If you are trying to execute an async operation during the modify, choose to conform the
  105. /// ``AsyncImageDownloadRequestModifier`` instead.
  106. ///
  107. /// - Parameter request: The input request contains necessary information like `url`. This request is generated
  108. /// according to your resource URL as a GET request.
  109. /// - Returns: The modified request which should be used to trigger the download.
  110. func modified(for request: URLRequest) -> URLRequest?
  111. }
  112. extension ImageDownloadRequestModifier {
  113. /// This is `nil` for a sync `ImageDownloadRequestModifier` by default. You can get the `DownloadTask` from the
  114. /// return value of downloader method.
  115. public var onDownloadTaskStarted: (@Sendable (DownloadTask?) -> Void)? { return nil }
  116. }
  117. /// A wrapper for creating an ``ImageDownloadRequestModifier`` instance more easily.
  118. ///
  119. /// This type conforms to ``ImageDownloadRequestModifier`` and wraps an image modification block.
  120. public struct AnyModifier: ImageDownloadRequestModifier {
  121. let block: @Sendable (URLRequest) -> URLRequest?
  122. public func modified(for request: URLRequest) -> URLRequest? {
  123. return block(request)
  124. }
  125. /// Creates a value of ``ImageDownloadRequestModifier`` that runs the `modify` block.
  126. ///
  127. /// - Parameter modify: The request modifying block runs when a request modifying task comes.
  128. public init(modify: @escaping @Sendable (URLRequest) -> URLRequest?) {
  129. block = modify
  130. }
  131. }