|
@@ -322,28 +322,53 @@ public class KingfisherManager {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- @discardableResult
|
|
|
|
|
- func loadAndCacheImage(
|
|
|
|
|
|
|
+ private func cacheImage(
|
|
|
source: Source,
|
|
source: Source,
|
|
|
|
|
+ options: KingfisherParsedOptionsInfo,
|
|
|
context: RetrievingContext,
|
|
context: RetrievingContext,
|
|
|
- completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)?) -> DownloadTask.WrappedTask?
|
|
|
|
|
|
|
+ result: Result<ImageLoadingResult, KingfisherError>,
|
|
|
|
|
+ completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)?
|
|
|
|
|
+ )
|
|
|
{
|
|
{
|
|
|
- let options = context.options
|
|
|
|
|
- func cacheImage(_ result: Result<ImageLoadingResult, KingfisherError>)
|
|
|
|
|
- {
|
|
|
|
|
- switch result {
|
|
|
|
|
- case .success(let value):
|
|
|
|
|
- // Add image to cache.
|
|
|
|
|
- let targetCache = options.targetCache ?? self.cache
|
|
|
|
|
- targetCache.store(
|
|
|
|
|
- value.image,
|
|
|
|
|
- original: value.originalData,
|
|
|
|
|
|
|
+ switch result {
|
|
|
|
|
+ case .success(let value):
|
|
|
|
|
+ let needToCacheOriginalImage = options.cacheOriginalImage &&
|
|
|
|
|
+ options.processor != DefaultImageProcessor.default
|
|
|
|
|
+ let coordinator = CacheCallbackCoordinator(
|
|
|
|
|
+ shouldWaitForCache: options.waitForCache, shouldCacheOriginal: needToCacheOriginalImage)
|
|
|
|
|
+ // Add image to cache.
|
|
|
|
|
+ let targetCache = options.targetCache ?? self.cache
|
|
|
|
|
+ targetCache.store(
|
|
|
|
|
+ value.image,
|
|
|
|
|
+ original: value.originalData,
|
|
|
|
|
+ forKey: source.cacheKey,
|
|
|
|
|
+ options: options,
|
|
|
|
|
+ toDisk: !options.cacheMemoryOnly)
|
|
|
|
|
+ {
|
|
|
|
|
+ _ in
|
|
|
|
|
+ coordinator.apply(.cachingImage) {
|
|
|
|
|
+ let result = RetrieveImageResult(
|
|
|
|
|
+ image: value.image,
|
|
|
|
|
+ cacheType: .none,
|
|
|
|
|
+ source: source,
|
|
|
|
|
+ originalSource: context.originalSource
|
|
|
|
|
+ )
|
|
|
|
|
+ completionHandler?(.success(result))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Add original image to cache if necessary.
|
|
|
|
|
+
|
|
|
|
|
+ if needToCacheOriginalImage {
|
|
|
|
|
+ let originalCache = options.originalCache ?? targetCache
|
|
|
|
|
+ originalCache.storeToDisk(
|
|
|
|
|
+ value.originalData,
|
|
|
forKey: source.cacheKey,
|
|
forKey: source.cacheKey,
|
|
|
- options: options,
|
|
|
|
|
- toDisk: !options.cacheMemoryOnly)
|
|
|
|
|
|
|
+ processorIdentifier: DefaultImageProcessor.default.identifier,
|
|
|
|
|
+ expiration: options.diskCacheExpiration)
|
|
|
{
|
|
{
|
|
|
_ in
|
|
_ in
|
|
|
- if options.waitForCache {
|
|
|
|
|
|
|
+ coordinator.apply(.cachingOriginalImage) {
|
|
|
let result = RetrieveImageResult(
|
|
let result = RetrieveImageResult(
|
|
|
image: value.image,
|
|
image: value.image,
|
|
|
cacheType: .none,
|
|
cacheType: .none,
|
|
@@ -353,47 +378,50 @@ public class KingfisherManager {
|
|
|
completionHandler?(.success(result))
|
|
completionHandler?(.success(result))
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // Add original image to cache if necessary.
|
|
|
|
|
- let needToCacheOriginalImage = options.cacheOriginalImage &&
|
|
|
|
|
- options.processor != DefaultImageProcessor.default
|
|
|
|
|
- if needToCacheOriginalImage {
|
|
|
|
|
- let originalCache = options.originalCache ?? targetCache
|
|
|
|
|
- originalCache.storeToDisk(
|
|
|
|
|
- value.originalData,
|
|
|
|
|
- forKey: source.cacheKey,
|
|
|
|
|
- processorIdentifier: DefaultImageProcessor.default.identifier,
|
|
|
|
|
- expiration: options.diskCacheExpiration)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if !options.waitForCache {
|
|
|
|
|
- let result = RetrieveImageResult(
|
|
|
|
|
- image: value.image,
|
|
|
|
|
- cacheType: .none,
|
|
|
|
|
- source: source,
|
|
|
|
|
- originalSource: context.originalSource
|
|
|
|
|
- )
|
|
|
|
|
- completionHandler?(.success(result))
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- case .failure(let error):
|
|
|
|
|
- completionHandler?(.failure(error))
|
|
|
|
|
|
|
+ coordinator.apply(.cacheInitiated) {
|
|
|
|
|
+ let result = RetrieveImageResult(
|
|
|
|
|
+ image: value.image,
|
|
|
|
|
+ cacheType: .none,
|
|
|
|
|
+ source: source,
|
|
|
|
|
+ originalSource: context.originalSource
|
|
|
|
|
+ )
|
|
|
|
|
+ completionHandler?(.success(result))
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ case .failure(let error):
|
|
|
|
|
+ completionHandler?(.failure(error))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @discardableResult
|
|
|
|
|
+ func loadAndCacheImage(
|
|
|
|
|
+ source: Source,
|
|
|
|
|
+ context: RetrievingContext,
|
|
|
|
|
+ completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)?) -> DownloadTask.WrappedTask?
|
|
|
|
|
+ {
|
|
|
|
|
+ let options = context.options
|
|
|
|
|
+ func _cacheImage(_ result: Result<ImageLoadingResult, KingfisherError>) {
|
|
|
|
|
+ cacheImage(
|
|
|
|
|
+ source: source,
|
|
|
|
|
+ options: options,
|
|
|
|
|
+ context: context,
|
|
|
|
|
+ result: result,
|
|
|
|
|
+ completionHandler: completionHandler
|
|
|
|
|
+ )
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
switch source {
|
|
switch source {
|
|
|
case .network(let resource):
|
|
case .network(let resource):
|
|
|
let downloader = options.downloader ?? self.downloader
|
|
let downloader = options.downloader ?? self.downloader
|
|
|
- guard let task = downloader.downloadImage(
|
|
|
|
|
- with: resource.downloadURL,
|
|
|
|
|
- options: options,
|
|
|
|
|
- completionHandler: cacheImage) else {
|
|
|
|
|
- return nil
|
|
|
|
|
- }
|
|
|
|
|
- return .download(task)
|
|
|
|
|
-
|
|
|
|
|
|
|
+ let task = downloader.downloadImage(
|
|
|
|
|
+ with: resource.downloadURL, options: options, completionHandler: _cacheImage
|
|
|
|
|
+ )
|
|
|
|
|
+ return task.map(DownloadTask.WrappedTask.download)
|
|
|
|
|
+
|
|
|
case .provider(let provider):
|
|
case .provider(let provider):
|
|
|
- provideImage(provider: provider, options: options, completionHandler: cacheImage)
|
|
|
|
|
|
|
+ provideImage(provider: provider, options: options, completionHandler: _cacheImage)
|
|
|
return .dataProviding
|
|
return .dataProviding
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -496,6 +524,10 @@ public class KingfisherManager {
|
|
|
|
|
|
|
|
var cacheOptions = options
|
|
var cacheOptions = options
|
|
|
cacheOptions.callbackQueue = .untouch
|
|
cacheOptions.callbackQueue = .untouch
|
|
|
|
|
+
|
|
|
|
|
+ let coordinator = CacheCallbackCoordinator(
|
|
|
|
|
+ shouldWaitForCache: options.waitForCache, shouldCacheOriginal: false)
|
|
|
|
|
+
|
|
|
targetCache.store(
|
|
targetCache.store(
|
|
|
processedImage,
|
|
processedImage,
|
|
|
forKey: key,
|
|
forKey: key,
|
|
@@ -503,7 +535,7 @@ public class KingfisherManager {
|
|
|
toDisk: !options.cacheMemoryOnly)
|
|
toDisk: !options.cacheMemoryOnly)
|
|
|
{
|
|
{
|
|
|
_ in
|
|
_ in
|
|
|
- if options.waitForCache {
|
|
|
|
|
|
|
+ coordinator.apply(.cachingImage) {
|
|
|
let value = RetrieveImageResult(
|
|
let value = RetrieveImageResult(
|
|
|
image: processedImage,
|
|
image: processedImage,
|
|
|
cacheType: .none,
|
|
cacheType: .none,
|
|
@@ -514,7 +546,7 @@ public class KingfisherManager {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if !options.waitForCache {
|
|
|
|
|
|
|
+ coordinator.apply(.cacheInitiated) {
|
|
|
let value = RetrieveImageResult(
|
|
let value = RetrieveImageResult(
|
|
|
image: processedImage,
|
|
image: processedImage,
|
|
|
cacheType: .none,
|
|
cacheType: .none,
|
|
@@ -566,3 +598,65 @@ struct RetrievingContext {
|
|
|
return propagationErrors
|
|
return propagationErrors
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+class CacheCallbackCoordinator {
|
|
|
|
|
+
|
|
|
|
|
+ enum State {
|
|
|
|
|
+ case idle
|
|
|
|
|
+ case imageCached
|
|
|
|
|
+ case originalImageCached
|
|
|
|
|
+ case done
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ enum Action {
|
|
|
|
|
+ case cacheInitiated
|
|
|
|
|
+ case cachingImage
|
|
|
|
|
+ case cachingOriginalImage
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private let shouldWaitForCache: Bool
|
|
|
|
|
+ private let shouldCacheOriginal: Bool
|
|
|
|
|
+
|
|
|
|
|
+ private (set) var state: State = .idle
|
|
|
|
|
+
|
|
|
|
|
+ init(shouldWaitForCache: Bool, shouldCacheOriginal: Bool) {
|
|
|
|
|
+ self.shouldWaitForCache = shouldWaitForCache
|
|
|
|
|
+ self.shouldCacheOriginal = shouldCacheOriginal
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ func apply(_ action: Action, trigger: () -> Void) {
|
|
|
|
|
+ switch (state, action) {
|
|
|
|
|
+ case (.done, _):
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
|
|
+ // From .idle
|
|
|
|
|
+ case (.idle, .cacheInitiated):
|
|
|
|
|
+ if !shouldWaitForCache {
|
|
|
|
|
+ state = .done
|
|
|
|
|
+ trigger()
|
|
|
|
|
+ }
|
|
|
|
|
+ case (.idle, .cachingImage):
|
|
|
|
|
+ if shouldCacheOriginal {
|
|
|
|
|
+ state = .imageCached
|
|
|
|
|
+ } else {
|
|
|
|
|
+ state = .done
|
|
|
|
|
+ trigger()
|
|
|
|
|
+ }
|
|
|
|
|
+ case (.idle, .cachingOriginalImage):
|
|
|
|
|
+ state = .originalImageCached
|
|
|
|
|
+
|
|
|
|
|
+ // From .imageCached
|
|
|
|
|
+ case (.imageCached, .cachingOriginalImage):
|
|
|
|
|
+ state = .done
|
|
|
|
|
+ trigger()
|
|
|
|
|
+
|
|
|
|
|
+ // From .originalImageCached
|
|
|
|
|
+ case (.originalImageCached, .cachingImage):
|
|
|
|
|
+ state = .done
|
|
|
|
|
+ trigger()
|
|
|
|
|
+
|
|
|
|
|
+ default:
|
|
|
|
|
+ assertionFailure("This case should not happen in CacheCallbackCoordinator: \(state) - \(action)")
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|