Continuous image caching in iOS

I am developing an application where I need to constantly upload and store images until I delete them manually (in memory + disk). This is necessary because the application must work offline. I know that there are AFNetworking and SDWebImage for caching images, but I do not think that they allow permanent caching and manual deletion. Afaik, they delete images automatically after the cache expires.

Is there any other library for this kind of thing? I tried to write a class for this, but it does not work very stably. I think it's better not to reinvent the wheel.

+4
source share
2 answers

Save the files in a folder inside the application support service. They will be saved, and iOS will not delete them. I say that I use a folder, since later, if you want to delete them, you can simply delete the folder.

Exactly how to access and configure the application support directory in detail in this post

If you want, you can save the URLs or paths to these images in the Core Data repository.

EDIT: I have a singleton class that manages my Core Data image store. An object that has an image associated with it has a data property, a URL property, a filePath property, and a boolean flag indicating whether there is an outstanding selection. When something is needed for an image, it requests an image (which is created from the data), and if there is no data, then singleton issues a web selection for it. When it arrives, it will be saved in the file system, and the entity will receive filePath for this set of images. He then sends a notification that such and such an image has arrived. [For debugging, at various points I also checked so that I could create an image from the data.]

When viewControllers receive a notification, they look at a list of visible cells, and if any of them is associated with a recently received image, this class requests an image from a singleton and then sets it to a cell. This works for me well.

0
source

Unfortunately, SDWebImage does not provide such an opportunity, therefore, to use the advanced caching capabilities provided by SDWebImage, I wrote a wrapper around SDWebImage

basically this class manages the backup persistent cache, so if the requested image not found in the SDWeb image cache is โ€œdisk and memoryโ€, it will look for it in the persistent cache

using this approach, I was able to constantly install images in table cells as usual using SDWebImage

.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 
0
source

Source: https://habr.com/ru/post/1434476/


All Articles