Reputation: 331
I am developing an iOS app that requires the SDWebImage to permanently cache pictures onto the phone.
Thanks.
Upvotes: 3
Views: 1435
Reputation: 627
Unfortunately SDWebImage Does not provide such a capability so to make use of the advanced caching capabilities provided by SDWebImage i wrote a wrapper around it
basically this class manages a fallback permanent cache so if the requested image not found in the SDWeb image cache "disk and memory" it will look for it in the permanent cache and if it was found the permanent cache it will be copied to the SDwebImage cache to make use of its memory cashing in later requests
using this approach i managed to keep setting images to table cells as smooth as usual with SDWebImage you can also trigger clearImageCache to errase the permanent cache whenever you need
.h file
@interface CacheManager : NSObject
+ (CacheManager*)sharedManager;
// Images
- (BOOL) diskImageExistsForURL:(NSURL*) url;
- (void) downloadImage:(NSURL*) url completed:(void(^)(UIImage*))onComplete;
- (void) setImage:(NSURL*)url toImageView:(UIImageView*)iv completed:(void(^)(UIImage*))onComplete;
- (void) clearImageCache;
@end
.m file
#import "CacheManager.h"
#import <SDWebImage/UIImageView+WebCache.h>
#define CACH_IMAGES_FOLDER @"ImagesData"
@implementation CacheManager
static CacheManager *sharedManager = nil;
#pragma mark -
#pragma mark Singilton Init Methods
// init shared Cache singleton.
+ (CacheManager*)sharedManager{
@synchronized(self){
if ( !sharedManager ){
sharedManager = [[CacheManager alloc] init];
}
}
return sharedManager;
}
// Dealloc shared API singleton.
+ (id)alloc{
@synchronized( self ){
NSAssert(sharedManager == nil, @"Attempted to allocate a second instance of a singleton.");
return [super alloc];
}
return nil;
}
// Init the manager
- (id)init{
if ( self = [super init] ){}
return self;
}
/**
@returns YES if image found in the permanent cache or the cache managed by SDWebImage lib
*/
- (BOOL) diskImageExistsForURL:(NSURL*) url{
// look for image in the SDWebImage cache
SDWebImageManager *manager = [SDWebImageManager sharedManager];
if([manager diskImageExistsForURL:url])
return YES;
// look for image in the permanent cache
NSString *stringPath = url.path;
NSFileManager *fileManager = [NSFileManager defaultManager];
return [fileManager fileExistsAtPath:stringPath];
}
/**
get the image with specified remote url asynchronosly
first looks for the image in SDWeb cache to make use of the disk and memory cache provided by SDWebImage
if not found, looks for it in the permanent cache we are managing, finally if not found in either places
it will download it using SDWebImage and cache it.
*/
- (void) downloadImage:(NSURL*) url completed:(void(^)(UIImage*))onComplete{
NSString *localPath = [[self getLocalUrlForImageUrl:url] path];
NSFileManager *fileManager = [NSFileManager defaultManager];
// -1 look for image in SDWeb cache
SDWebImageManager *manager = [SDWebImageManager sharedManager];
if([manager diskImageExistsForURL:url]){
[manager downloadImageWithURL:url options:SDWebImageRetryFailed
progress:^(NSInteger receivedSize, NSInteger expectedSize) {}
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
onComplete(image);
// save the image to the perminant cache for later
// if not saved before
if(image){
if ([fileManager fileExistsAtPath:localPath]){
NSURL* localeUrl = [self getLocalUrlForImageUrl:url];
[self saveImage:image toCacheWithLocalPath:localeUrl];
}
}
}];
return;
}
// -2 look for the image in the permanent cache
if ([fileManager fileExistsAtPath:localPath]){
UIImage *img = [self getImageFromCache:url];
onComplete(img);
// save image back to the SDWeb image cache to make use of the memory cache
// provided by SDWebImage in later requests
[manager saveImageToCache:img forURL:url];
return;
}
// -3 download the image using SDWebImage lib
[manager downloadImageWithURL:url options:SDWebImageRetryFailed
progress:^(NSInteger receivedSize, NSInteger expectedSize) {}
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
onComplete(image);
// save the image to the permanent cache for later
if(image){
NSURL* localeUrl = [self getLocalUrlForImageUrl:url];
[self saveImage:image toCacheWithLocalPath:localeUrl];
}
}];
}
- (void) setImage:(NSURL*)url toImageView:(UIImageView*)iv completed:(void(^)(UIImage*))onComplete{
[self downloadImage:url completed:^(UIImage * downloadedImage) {
iv.image = downloadedImage;
onComplete(downloadedImage);
}];
}
/**
@param:imgUrl : local url of image to read
*/
- (UIImage*) getImageFromCache:(NSURL*)imgUrl{
return [UIImage imageWithData: [NSData dataWithContentsOfURL:imgUrl]];
}
/**
writes the suplied image to the local path provided
*/
-(void) saveImage:(UIImage*)img toCacheWithLocalPath:(NSURL*)localPath{
NSData * binaryImageData = UIImagePNGRepresentation(img);
[binaryImageData writeToFile:[localPath path] atomically:YES];
}
// Generate local image URL baesd on the name of the remote image
// this assumes the remote images already has unique names
- (NSURL*)getLocalUrlForImageUrl:(NSURL*)imgUrl{
// Saving an offline copy of the data.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachesDirectory = [paths objectAtIndex:0];
NSString *folderPath = [cachesDirectory stringByAppendingPathComponent:CACH_IMAGES_FOLDER];
BOOL isDir;
// create folder not exist
if (![fileManager fileExistsAtPath:folderPath isDirectory:&isDir]){
NSError *dirWriteError = nil;
if (![fileManager createDirectoryAtPath:folderPath withIntermediateDirectories:YES attributes:nil error:&dirWriteError]){
NSLog(@"Error: failed to create folder!");
}
}
NSString *imgName = [[[imgUrl path] lastPathComponent] stringByDeletingPathExtension];
NSURL *cachesDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
NSString *pathString = [NSString stringWithFormat:@"%@/%@", CACH_IMAGES_FOLDER, imgName];
return [cachesDirectoryURL URLByAppendingPathComponent:pathString];
}
/**
removes the folder contating the cahced images,
the folder will be reacreated whenever a new image is being saved to the permanent cache
*/
-(void)clearImageCache{
// set the directory path
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachesDirectory = [paths objectAtIndex:0];
NSString *folderPath = [cachesDirectory stringByAppendingPathComponent:CACH_IMAGES_FOLDER];
BOOL isDir;
NSError *dirError = nil;
// folder exist
if ([fileManager fileExistsAtPath:folderPath isDirectory:&isDir]){
if (![fileManager removeItemAtPath:folderPath error:&dirError])
NSLog(@"Failed to remove folder");
}
}
@end
Upvotes: 6