KingfisherError.swift 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. //
  2. // KingfisherError.swift
  3. // Kingfisher
  4. //
  5. // Created by onevcat on 2018/09/26.
  6. //
  7. // Copyright (c) 2018 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. extension Never: Error {}
  28. /// Represents all the errors which can happen in Kingfisher framework.
  29. /// Kingfisher related methods always throw a `KingfisherError` or invoke the callback with `KingfisherError`
  30. /// as its error type. To handle errors from Kingfisher, you switch over the error to get a reason cateklog,
  31. /// then switch over the reason to know error detail.
  32. public enum KingfisherError: Error {
  33. /// The error domain of Kingfisher.
  34. public static let domain = "com.onevcat.Kingfisher.Error"
  35. /// Represents the error reason during networking request phase.
  36. ///
  37. /// - emptyRequest: The request is empty. Code 1001.
  38. /// - invalidURL: The URL of request is invalid. Code 1002.
  39. /// - taskCancelled: The downloading task is cancelled by user. Code 1003.
  40. public enum RequestErrorReason {
  41. /// The request is empty. Code 1001.
  42. case emptyRequest
  43. /// The URL of request is invalid. Code 1002.
  44. /// - request: The request is tend to be sent but its URL is invalid.
  45. case invalidURL(request: URLRequest)
  46. /// The downloading task is cancelled by user. Code 1003.
  47. /// - task: The session data task which is cancelled.
  48. /// - token: The cancel token which is used for cancelling the task.
  49. case taskCancelled(task: SessionDataTask, token: SessionDataTask.CancelToken)
  50. }
  51. /// Represents the error reason during networking response phase.
  52. ///
  53. /// - invalidURLResponse: The response is not a valid URL response. Code 2001.
  54. /// - invalidHTTPStatusCode: The response contains an invalid HTTP status code. Code 2002.
  55. /// - URLSessionError: An error happens in the system URL session. Code 2003.
  56. /// - dataModifyingFailed: Data modifying fails on returning a valid data. Code 2004.
  57. /// - noURLResponse: The task is done but no URL response found. Code 2005.
  58. public enum ResponseErrorReason {
  59. /// The response is not a valid URL response. Code 2001.
  60. /// - response: The received invalid URL response.
  61. /// The response is expected to be an HTTP response, but it is not.
  62. case invalidURLResponse(response: URLResponse)
  63. /// The response contains an invalid HTTP status code. Code 2002.
  64. /// - Note:
  65. /// By defalt, status code 200..<400 is recognized as valid. You can override
  66. /// this behavior by conforming to the `ImageDownloaderDelegate`.
  67. /// - response: The received response.
  68. case invalidHTTPStatusCode(response: HTTPURLResponse)
  69. /// An error happens in the system URL session. Code 2003.
  70. /// - error: The underlying URLSession error object.
  71. case URLSessionError(error: Error)
  72. /// Data modifying fails on returning a valid data. Code 2004.
  73. /// - task: The failed task.
  74. case dataModifyingFailed(task: SessionDataTask)
  75. /// The task is done but no URL response found. Code 2005.
  76. /// - task: The failed task.
  77. case noURLResponse(task: SessionDataTask)
  78. }
  79. /// Represents the error reason during Kingfisher caching system.
  80. ///
  81. /// - fileEnumeratorCreationFailed: Cannot create a file enumerator for a certain disk URL. Code 3001.
  82. /// - invalidFileEnumeratorContent: Cannot get correct file contents from a file enumerator. Code 3002.
  83. /// - invalidURLResource: The file at target URL exists, but its URL resource is unavailable. Code 3003.
  84. /// - cannotLoadDataFromDisk: The file at target URL exists, but the data cannot be loaded from it. Code 3004.
  85. /// - cannotCreateDirectory: Cannot create a folder at a given path. Code 3005.
  86. /// - imageNotExisting: The requested image does not exist in cache. Code 3006.
  87. /// - cannotConvertToData: Cannot convert an object to data for storing. Code 3007.
  88. /// - cannotSerializeImage: Cannot serialize an image to data for storing. Code 3008.
  89. public enum CacheErrorReason {
  90. /// Cannot create a file enumerator for a certain disk URL. Code 3001.
  91. /// - url: The target disk URL from which the file enumerator should be created.
  92. case fileEnumeratorCreationFailed(url: URL)
  93. /// Cannot get correct file contents from a file enumerator. Code 3002.
  94. /// - url: The target disk URL from which the content of a file enumerator should be got.
  95. case invalidFileEnumeratorContent(url: URL)
  96. /// The file at target URL exists, but its URL resource is unavailable. Code 3003.
  97. /// - error: The underlying error thrown by file manager.
  98. /// - key: The key used to getting the resource from cache.
  99. /// - url: The disk URL where the target cached file exists.
  100. case invalidURLResource(error: Error, key: String, url: URL)
  101. /// The file at target URL exists, but the data cannot be loaded from it. Code 3004.
  102. /// - url: The disk URL where the target cached file exists.
  103. /// - error: The underlying error which describes why this error happens.
  104. case cannotLoadDataFromDisk(url: URL, error: Error)
  105. /// Cannot create a folder at a given path. Code 3005.
  106. /// - path: The disk path where the directory creating operation fails.
  107. /// - error: The underlying error which describes why this error happens.
  108. case cannotCreateDirectory(path: String, error: Error)
  109. /// The requested image does not exist in cache. Code 3006.
  110. /// - key: Key of the requested image in cache.
  111. case imageNotExisting(key: String)
  112. /// Cannot convert an object to data for storing. Code 3007.
  113. /// - object: The object which needs be convert to data.
  114. case cannotConvertToData(object: Any, error: Error)
  115. /// Cannot serialize an image to data for storing. Code 3008.
  116. /// - image: The input image needs to be serialized to cache.
  117. /// - original: The original image data, if exists.
  118. /// - serializer: The `CacheSerializer` used for the image serializing.
  119. case cannotSerializeImage(image: Image?, original: Data?, serializer: CacheSerializer)
  120. }
  121. /// Represents the error reason during image processing phase.
  122. ///
  123. /// - processingFailed: Image processing fails. There is no valid output image from the processor. Code 4001.
  124. public enum ProcessorErrorReason {
  125. /// Image processing fails. There is no valid output image from the processor. Code 4001.
  126. /// - processor: The `ImageProcessor` used to process the image or its data in `item`.
  127. /// - item: The image or its data content.
  128. case processingFailed(processor: ImageProcessor, item: ImageProcessItem)
  129. }
  130. /// Represnts the error reason duting image setting in a view related class.
  131. ///
  132. /// - emptySource: The input resource is empty or `nil`. Code 5001.
  133. /// - notCurrentSource: The source task is finished, but it is not the one expected now. Code 5002.
  134. /// - dataProviderError: An error happens during getting data from an `ImageDataProvider`. Code 5003.
  135. public enum ImageSettingErrorReason {
  136. /// The input resource is empty or `nil`. Code 5001.
  137. case emptySource
  138. /// The resource task is finished, but it is not the one expected now. This usually happens when you set another
  139. /// resource on the view without cancelling the current on-going one. The previous setting task will fail with
  140. /// this `.notCurrentSource` error when a result got, regardless of it being successful or not for that task.
  141. /// Code 5002.
  142. case notCurrentSource(result: RetrieveImageResult?, error: Error?, source: Source)
  143. /// An error happens during getting data from an `ImageDataProvider`. Code 5003.
  144. case dataProviderError(provider: ImageDataProvider, error: Error)
  145. }
  146. /// Represents the error reason during networking request phase.
  147. case requestError(reason: RequestErrorReason)
  148. /// Represents the error reason during networking response phase.
  149. case responseError(reason: ResponseErrorReason)
  150. /// Represents the error reason during Kingfisher caching system.
  151. case cacheError(reason: CacheErrorReason)
  152. /// Represents the error reason during image processing phase.
  153. case processorError(reason: ProcessorErrorReason)
  154. /// Represnts the error reason duting image setting in a view related class.
  155. case imageSettingError(reason: ImageSettingErrorReason)
  156. /// Helper property to check whether this error is a `RequestErrorReason.taskCancelled` or not.
  157. public var isTaskCancelled: Bool {
  158. if case .requestError(reason: .taskCancelled) = self {
  159. return true
  160. }
  161. return false
  162. }
  163. /// Helper method to check whether this error is a `ResponseErrorReason.invalidHTTPStatusCode` and the
  164. /// associated value is a given status code.
  165. ///
  166. /// - Parameter code: The given status code.
  167. /// - Returns: If `self` is a `ResponseErrorReason.invalidHTTPStatusCode` error
  168. /// and its status code equals to `code`, `true` is returned. Otherwise, `false`.
  169. public func isInvalidResponseStatusCode(_ code: Int) -> Bool {
  170. if case .responseError(reason: .invalidHTTPStatusCode(let response)) = self {
  171. return response.statusCode == code
  172. }
  173. return false
  174. }
  175. }
  176. extension KingfisherError: LocalizedError {
  177. public var errorDescription: String? {
  178. switch self {
  179. case .requestError(let reason): return reason.errorDescription
  180. case .responseError(let reason): return reason.errorDescription
  181. case .cacheError(let reason): return reason.errorDescription
  182. case .processorError(let reason): return reason.errorDescription
  183. case .imageSettingError(let reason): return reason.errorDescription
  184. }
  185. }
  186. }
  187. extension KingfisherError: CustomNSError {
  188. public var errorCode: Int {
  189. switch self {
  190. case .requestError(let reason): return reason.errorCode
  191. case .responseError(let reason): return reason.errorCode
  192. case .cacheError(let reason): return reason.errorCode
  193. case .processorError(let reason): return reason.errorCode
  194. case .imageSettingError(let reason): return reason.errorCode
  195. }
  196. }
  197. }
  198. extension KingfisherError.RequestErrorReason {
  199. var errorDescription: String? {
  200. switch self {
  201. case .emptyRequest:
  202. return "The request is empty or `nil`."
  203. case .invalidURL(let request):
  204. return "The request contains an invalid or empty URL. Request: \(request)."
  205. case .taskCancelled(let task, let token):
  206. return "The session task was canncelled. Task: \(task), cancel token: \(token)."
  207. }
  208. }
  209. var errorCode: Int {
  210. switch self {
  211. case .emptyRequest: return 1001
  212. case .invalidURL: return 1002
  213. case .taskCancelled: return 1003
  214. }
  215. }
  216. }
  217. extension KingfisherError.ResponseErrorReason {
  218. var errorDescription: String? {
  219. switch self {
  220. case .invalidURLResponse(let response):
  221. return "The URL response is invalid: \(response)"
  222. case .invalidHTTPStatusCode(let response):
  223. return "The HTTP status code in response is invalid. Code: \(response.statusCode), response: \(response)."
  224. case .URLSessionError(let error):
  225. return "A URL session error happened. The underlying error: \(error)"
  226. case .dataModifyingFailed(let task):
  227. return "The data modifying delegate returned `nil` for the downloaded data. Task: \(task)."
  228. case .noURLResponse(let task):
  229. return "No URL response received. Task: \(task),"
  230. }
  231. }
  232. var errorCode: Int {
  233. switch self {
  234. case .invalidURLResponse: return 2001
  235. case .invalidHTTPStatusCode: return 2002
  236. case .URLSessionError: return 2003
  237. case .dataModifyingFailed: return 2004
  238. case .noURLResponse: return 2005
  239. }
  240. }
  241. }
  242. extension KingfisherError.CacheErrorReason {
  243. var errorDescription: String? {
  244. switch self {
  245. case .fileEnumeratorCreationFailed(let url):
  246. return "Cannot create file enumerator for URL: \(url)."
  247. case .invalidFileEnumeratorContent(let url):
  248. return "Cannot get contents from the file enumerator at URL: \(url)."
  249. case .invalidURLResource(let error, let key, let url):
  250. return "Cannot get URL resource values or data for the given URL: \(url). " +
  251. "Cache key: \(key). Underlying error: \(error)"
  252. case .cannotLoadDataFromDisk(let url, let error):
  253. return "Cannot load data from disk at URL: \(url). Underlying error: \(error)"
  254. case .cannotCreateDirectory(let path, let error):
  255. return "Cannot create directory at given path: Path: \(path). Underlying error: \(error)"
  256. case .imageNotExisting(let key):
  257. return "The image is not in cache, but you requires it should only be " +
  258. "from cache by enabling the `.onlyFromCache` option. Key: \(key)."
  259. case .cannotConvertToData(let object, let error):
  260. return "Cannot convert the input object to a `Data` object when storing it to disk cache. " +
  261. "Object: \(object). Underlying error: \(error)"
  262. case .cannotSerializeImage(let image, let originalData, let serializer):
  263. return "Cannot serialize an image due to the cache serializer returning `nil`. " +
  264. "Image: \(String(describing:image)), original data: \(String(describing: originalData)), serializer: \(serializer)."
  265. }
  266. }
  267. var errorCode: Int {
  268. switch self {
  269. case .fileEnumeratorCreationFailed: return 3001
  270. case .invalidFileEnumeratorContent: return 3002
  271. case .invalidURLResource: return 3003
  272. case .cannotLoadDataFromDisk: return 3004
  273. case .cannotCreateDirectory: return 3005
  274. case .imageNotExisting: return 3006
  275. case .cannotConvertToData: return 3007
  276. case .cannotSerializeImage: return 3008
  277. }
  278. }
  279. }
  280. extension KingfisherError.ProcessorErrorReason {
  281. var errorDescription: String? {
  282. switch self {
  283. case .processingFailed(let processor, let item):
  284. return "Processing image failed. Processor: \(processor). Processing item: \(item)."
  285. }
  286. }
  287. var errorCode: Int {
  288. switch self {
  289. case .processingFailed: return 4001
  290. }
  291. }
  292. }
  293. extension KingfisherError.ImageSettingErrorReason {
  294. var errorDescription: String? {
  295. switch self {
  296. case .emptySource:
  297. return "The input resource is empty."
  298. case .notCurrentSource(let result, let error, let resource):
  299. if let result = result {
  300. return "Retrieving resource succeeded, but this resource is " +
  301. "not the one currently expected. Result: \(result). Resource: \(resource)."
  302. } else if let error = error {
  303. return "Retrieving resource failed, and this resource is " +
  304. "not the one currently expected. Error: \(error). Resource: \(resource)."
  305. } else {
  306. return nil
  307. }
  308. case .dataProviderError(let provider, let error):
  309. return "Image data provider fails to provide data. Provider: \(provider), error: \(error)"
  310. }
  311. }
  312. var errorCode: Int {
  313. switch self {
  314. case .emptySource: return 5001
  315. case .notCurrentSource: return 5002
  316. case .dataProviderError: return 5003
  317. }
  318. }
  319. }