Created
May 30, 2017 08:00
-
-
Save ksmandersen/81dffdb7cfc58b880726afb7480e94bc to your computer and use it in GitHub Desktop.
Haneke & Swift image caching
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
import Forbind | |
import ForbindExtensions | |
import Haneke | |
typealias ImageProcess = (UIImage) -> UIImage | |
public class ImageCache { | |
public static let shared = ImageCache() | |
private let queue = OperationQueue() | |
private let cache = Cache<UIImage>(name: "dk.nordiskfilm.minbio.image-cache") | |
public func load(from url: URL) -> Promise<Result<UIImage>> { | |
return load(from: url, process: nil) | |
} | |
private func load(from url: URL, process: ImageProcess? = nil) -> Promise<Result<UIImage>> { | |
let promise = Promise<Result<UIImage>>() | |
let fetch = cache.fetch(key: url.absoluteString) | |
// The image is in already in the cache | |
let _ = fetch.onSuccess { image in | |
promise.setValue(.ok(image)) | |
} | |
// We need to download the image | |
let _ = fetch.onFailure { [weak self] _ in | |
self?.performDownload(of: url, promise: promise, process: process) | |
} | |
return promise | |
} | |
private func performDownload(of url: URL, promise: Promise<Result<UIImage>>, | |
process: ImageProcess? = nil) { | |
let downloadPromise = self.networkFetch(from: url) | |
downloadPromise.getValue { [weak self] result in | |
guard let `self` = self else { return } | |
switch result { | |
// There was an error downloading the image | |
case .error(let error): promise.setValue(.error(error)) | |
// Image downloaded | |
case .ok(let image): | |
if let process = process { | |
// Process the image | |
let processPromise = self.processDownload(image: image, process: process) | |
processPromise.getValue { image in | |
self.cache.set(value: image, key: url.absoluteString) | |
promise.setValue(.ok(image)) | |
} | |
} else { | |
self.cache.set(value: image, key: url.absoluteString) | |
promise.setValue(.ok(image)) | |
} | |
} | |
} | |
} | |
private func processDownload(image: UIImage, process: @escaping ImageProcess) -> Promise<UIImage> { | |
let promise = Promise<UIImage>() | |
queue.addOperation { | |
let processedImage = process(image) | |
OperationQueue.main.addOperation { | |
promise.setValue(processedImage) | |
} | |
} | |
return promise | |
} | |
private func networkFetch(from url: URL) -> Promise<Result<UIImage>> { | |
let downloadPromise = URLSession.shared.dataTask(url) | |
return dispatchAsync(downloadPromise => { UIImage(data: $0.0) }, queue: .main) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
import Forbind | |
class URLImageView: UIImageView { | |
public enum State { | |
case notSet | |
case loading | |
case loaded | |
} | |
private(set) var url: URL? | |
private(set) var state: State = .notSet | |
private var promise: Promise<Result<UIImage>>? | |
private var placeholderImage: UIImage? | |
public init(placeholderImage: UIImage? = nil) { | |
super.init(image: placeholderImage) | |
} | |
public convenience init(from url: URL, placeholder: UIImage) { | |
self.init(placeholderImage: placeholder) | |
loadImage(from: url, placeholder: placeholder) | |
} | |
public required init?(coder: NSCoder) { | |
super.init(coder: coder) | |
} | |
public func loadImage(from url: URL, placeholder: UIImage? = nil) { | |
self.placeholderImage = placeholder | |
self.image = placeholder | |
self.url = url | |
sendImageRequest() | |
} | |
public func cancelLoad() { | |
promise = nil | |
guard let placeholderImage = placeholderImage else { return } | |
image = placeholderImage | |
} | |
private func sendImageRequest() { | |
guard let url = url else { return } | |
promise = ImageCache.shared.load(from: url) | |
promise?.getValueWeak { [weak self] result in | |
if let image = result.okValue { | |
guard let imageUrl = self?.url, imageUrl == url else { return } | |
self?.image = image | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment