A lightweight iOS image and data cache with built in queue management.
pod 'SGImageCache'
- Attempt to load the image immediately from the cache, if it exists
- If the image is not available in the cache, fetch the image asynchronously.
- (optional) It is recommended to also xfade in your image using a UIView transition animation for a pleasing effect.
// Swift
if let image = SGImageCache.image(forURL: url) {
imageView.image = image // image loaded immediately from cache
} else {
SGImageCache.getImage(url: url) { [weak self] image in
self?.imageView.image = image // image loaded async
}
}
// Objective-C
if ([SGImageCache haveImageForURL:url]) {
self.imageView.image = [SGImageCache imageForURL:url]; // image loaded immediately from cache
} else {
__weak UIViewController *me = self;
[SGImageCache getImageForURL:url].then(^(UIImage *image) {
me.imageView.image = image; // image loaded async
});
}
// Objective-C
[SGImageCache getImageForURL:url].then(^(UIImage *image) {
if (image) {
self.imageView.image = image;
}
});
// Swift
SGImageCache.getImage(url: url) { [weak self] image in
guard let self = self else { return }
self.imageView.image = image
}
This will add the fetch request to fastQueue
(a parellel queue). All image fetching (either
from memory, disk, or remote) is performed off the main thread.
// Objective-C
[SGImageCache slowGetImageForURL:url];
// Swift
SGImageCache.slowGetImage(url: url)
This will add the fetch request to slowQueue
(a serial queue). All image fetching (either
from memory, disk, or remote) is performed off the main thread.
Adding image fetch tasks to slowQueue
is useful for prefetching images for off screen
content. For example if you have data for 100 table rows, but only 3 are on screen at a time,
you would request the images for on screen rows from fastQueue
with getImageForURL:
and
add the rest to slowQueue
with slowGetImageForURL:
.
// Objective-C
[SGImageCache moveTaskToSlowQueueForURL:url];
// Swift
SGImageCache.moveTaskToSlowQueueForURL(url)
This is useful for deprioritising image fetches for content that has scrolled off screen. The content may scroll back on screen later, so you still want the fetch to happen, but it is no longer urgently required.
// Objective-C
SGCachePromise *promise = [SGImageCache getImageForURL:url];
promise.then(^(UIImage *image) {
self.imageView.image = image;
});
promise.onRetry = ^{
// Called when SGImageCache automatically retries
// fetching the image due to a reachability change.
[self showLoadingSpinner];
};
promise.onFail = ^(NSError *error, BOOL fatal) {
// If the failure was fatal, SGImageCache will not
// automatically retry (eg. from a 404)
[self displayError:error];
};
// Swift
let promise = SGImageCache.getImageForURL(url)
promise.swiftThen({object in
if let image = object as? UIImage {
self.imageView.image = image
}
return nil
})
promise.onRetry = {
self.showLoadingSpinner()
}
promise.onFail = { (error: NSError?, wasFatal: Bool) -> () in
self.displayError(error)
}
This is useful for displaying states of network failure, loading spinners on reachability change, or any other functionality that might need to be notified that an image could not be fetched.
fastQueue
is a parallel queue, used for urgently required images. The getImageForURL:
method adds tasks to this queue. The maximum number of parallel tasks is managed by iOS, based on the device's number of processors, and other factors.
slowQueue
is a serial queue, used for prefetching images that might be required later (eg
for currently off screen content). The slowGetImageForURL:
method adds tasks to this queue.
slowQueue
is automatically suspended while fastQueue
is active, to avoid consuming network bandwidth while urgent image fetches are in progress. Once all fastQueue
tasks are completed
slowQueue
will be resumed.
If an image is requested for a URL that is already queued or in progress, SGImageCache
reuses the existing task, and if necessary will move it from slowQueue
to fastQueue
,
depending on which image fetch method was used. This ensures that there will be only one
network request per URL, regardless of how many times it's been asked for.
If you use SGImageView
instead of UIImageView
, and load the image via one of the
setImageForURL:
methods, off screen image views will release their image
on memory
warning, and subsequently restore them from cache if the image view returns to screen.
This allows off screen but still existing view controllers (eg a previous controller in a
nav controller's stack) to free up memory that would otherwise be unnecessarily retained,
and reduce the chances of your app being terminated by iOS in limited memory situations.
You can use SGImageCache for caching of generic data in the form of an NSData object (eg. PDFs, JSON payloads). Just use the equivalent SGCache
class method instead of the SGImageCache
one:
// Objective-C
[SGCache getFileForURL:url].then(^(NSData *data) {
// do something with data
});
// Swift
SGCache.getFileForURL(url).swiftThen({object in
if let data = object as? NSData {
// do something with data
}
return nil
})