diff --git a/Examples/SDWebImage Demo/AppDelegate.m b/Examples/SDWebImage Demo/AppDelegate.m index a7c47598..8eb070d6 100644 --- a/Examples/SDWebImage Demo/AppDelegate.m +++ b/Examples/SDWebImage Demo/AppDelegate.m @@ -21,7 +21,10 @@ { //Add a custom read-only cache path NSString *bundledPath = [[NSBundle mainBundle].resourcePath stringByAppendingPathComponent:@"CustomPathImages"]; - [[SDImageCache sharedImageCache] addReadOnlyCachePath:bundledPath]; + [SDImageCache sharedImageCache].additionalCachePathBlock = ^NSString * _Nullable(NSString * _Nonnull key) { + NSString *fileName = [[SDImageCache sharedImageCache] cachePathForKey:key].lastPathComponent; + return [bundledPath stringByAppendingPathComponent:fileName.stringByDeletingPathExtension]; + }; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; // Override point for customization after application launch. diff --git a/SDWebImage/SDImageCache.h b/SDWebImage/SDImageCache.h index ddc93dd0..81b5bc23 100644 --- a/SDWebImage/SDImageCache.h +++ b/SDWebImage/SDImageCache.h @@ -58,6 +58,8 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot typedef void(^SDWebImageCompletionWithPossibleErrorBlock)(NSError * _Nullable error); +typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString * _Nonnull key); + /** * SDImageCache maintains a memory cache and an optional disk cache. Disk cache write operations are performed * asynchronous so it doesn’t add unnecessary latency to the UI. @@ -71,6 +73,18 @@ typedef void(^SDWebImageCompletionWithPossibleErrorBlock)(NSError * _Nullable er */ @property (nonatomic, nonnull, readonly) SDImageCacheConfig *config; +/** + * The disk cache's root path + */ +@property (nonatomic, copy, nonnull, readonly) NSString *diskCachePath; + +/** + * The additional disk cache path to check if the query from disk cache not exist; + * The `key` param is the image cache key. The returned file path will be used to load the disk cache. If return nil, ignore it. + * Useful if you want to bundle pre-loaded images with your app + */ +@property (nonatomic, copy, nullable) SDImageCacheAdditionalCachePathBlock additionalCachePathBlock; + #pragma mark - Singleton and initialization /** @@ -107,15 +121,13 @@ typedef void(^SDWebImageCompletionWithPossibleErrorBlock)(NSError * _Nullable er #pragma mark - Cache paths -- (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace; - /** - * Add a read-only cache path to search for images pre-cached by SDImageCache - * Useful if you want to bundle pre-loaded images with your app - * - * @param path The path to use for this read-only cache path + Get the cache path for a certain key + + @param key The unique image cache key + @return The cache path. You can check `lastPathComponent` to grab the file name. */ -- (void)addReadOnlyCachePath:(nonnull NSString *)path; +- (nullable NSString *)cachePathForKey:(nullable NSString *)key; #pragma mark - Store Ops @@ -299,25 +311,4 @@ typedef void(^SDWebImageCompletionWithPossibleErrorBlock)(NSError * _Nullable er */ - (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock; -#pragma mark - Cache Paths - -/** - * Get the cache path for a certain key (needs the cache path root folder) - * - * @param key the key (can be obtained from url using cacheKeyForURL) - * @param path the cache path root folder - * - * @return the cache path - */ -- (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path; - -/** - * Get the default cache path for a certain key - * - * @param key the key (can be obtained from url using cacheKeyForURL) - * - * @return the default cache path - */ -- (nullable NSString *)defaultCachePathForKey:(nullable NSString *)key; - @end diff --git a/SDWebImage/SDImageCache.m b/SDWebImage/SDImageCache.m index fc0e50f3..1b6683c0 100644 --- a/SDWebImage/SDImageCache.m +++ b/SDWebImage/SDImageCache.m @@ -128,8 +128,6 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { #pragma mark - Properties @property (strong, nonatomic, nonnull) SDMemoryCache *memCache; -@property (strong, nonatomic, nonnull) NSString *diskCachePath; -@property (strong, nonatomic, nullable) NSMutableArray *customPaths; @property (strong, nonatomic, nullable) dispatch_queue_t ioQueue; @property (strong, nonatomic, nonnull) NSFileManager *fileManager; @@ -218,14 +216,11 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { #pragma mark - Cache paths -- (void)addReadOnlyCachePath:(nonnull NSString *)path { - if (!self.customPaths) { - self.customPaths = [NSMutableArray new]; - } - - if (![self.customPaths containsObject:path]) { - [self.customPaths addObject:path]; +- (nullable NSString *)cachePathForKey:(nullable NSString *)key { + if (!key) { + return nil; } + return [self cachePathForKey:key inPath:self.diskCachePath]; } - (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path { @@ -233,10 +228,6 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { return [path stringByAppendingPathComponent:filename]; } -- (nullable NSString *)defaultCachePathForKey:(nullable NSString *)key { - return [self cachePathForKey:key inPath:self.diskCachePath]; -} - - (nullable NSString *)cachedFileNameForKey:(nullable NSString *)key { const char *str = key.UTF8String; if (str == NULL) { @@ -352,7 +343,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { } // get cache Path for image key - NSString *cachePathForKey = [self defaultCachePathForKey:key]; + NSString *cachePathForKey = [self cachePathForKey:key]; // transform to NSUrl NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey]; @@ -402,12 +393,12 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { if (!key) { return NO; } - BOOL exists = [self.fileManager fileExistsAtPath:[self defaultCachePathForKey:key]]; + BOOL exists = [self.fileManager fileExistsAtPath:[self cachePathForKey:key]]; // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name // checking the key with and without the extension if (!exists) { - exists = [self.fileManager fileExistsAtPath:[self defaultCachePathForKey:key].stringByDeletingPathExtension]; + exists = [self.fileManager fileExistsAtPath:[self cachePathForKey:key].stringByDeletingPathExtension]; } return exists; @@ -440,7 +431,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { } - (nullable NSData *)diskImageDataBySearchingAllPathsForKey:(nullable NSString *)key { - NSString *defaultPath = [self defaultCachePathForKey:key]; + NSString *defaultPath = [self cachePathForKey:key]; NSData *data = [NSData dataWithContentsOfFile:defaultPath options:self.config.diskCacheReadingOptions error:nil]; if (data) { return data; @@ -452,20 +443,12 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { if (data) { return data; } - - NSArray *customPaths = [self.customPaths copy]; - for (NSString *path in customPaths) { - NSString *filePath = [self cachePathForKey:key inPath:path]; - NSData *imageData = [NSData dataWithContentsOfFile:filePath options:self.config.diskCacheReadingOptions error:nil]; - if (imageData) { - return imageData; - } - - // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name - // checking the key with and without the extension - imageData = [NSData dataWithContentsOfFile:filePath.stringByDeletingPathExtension options:self.config.diskCacheReadingOptions error:nil]; - if (imageData) { - return imageData; + + // Addtional cache path for custom pre-load cache + if (self.additionalCachePathBlock) { + NSString *filePath = self.additionalCachePathBlock(key); + if (filePath) { + data = [NSData dataWithContentsOfFile:filePath options:self.config.diskCacheReadingOptions error:nil]; } } @@ -616,7 +599,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { if (fromDisk) { dispatch_async(self.ioQueue, ^{ - [self.fileManager removeItemAtPath:[self defaultCachePathForKey:key] error:nil]; + [self.fileManager removeItemAtPath:[self cachePathForKey:key] error:nil]; if (completion) { dispatch_async(dispatch_get_main_queue(), ^{ diff --git a/Tests/Tests/SDImageCacheTests.m b/Tests/Tests/SDImageCacheTests.m index 0f5a09fd..4a53c7c2 100644 --- a/Tests/Tests/SDImageCacheTests.m +++ b/Tests/Tests/SDImageCacheTests.m @@ -195,13 +195,13 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; [self waitForExpectationsWithCommonTimeout]; } -- (void)test31DefaultCachePathForAnyKey{ - NSString *path = [[SDImageCache sharedImageCache] defaultCachePathForKey:kImageTestKey]; +- (void)test31CachePathForAnyKey{ + NSString *path = [[SDImageCache sharedImageCache] cachePathForKey:kImageTestKey]; expect(path).toNot.beNil; } -- (void)test32CachePathForNonExistingKey{ - NSString *path = [[SDImageCache sharedImageCache] cachePathForKey:kImageTestKey inPath:[[SDImageCache sharedImageCache] defaultCachePathForKey:kImageTestKey]]; +- (void)test32CachePathForNilKey{ + NSString *path = [[SDImageCache sharedImageCache] cachePathForKey:nil]; expect(path).to.beNil; } @@ -209,7 +209,7 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; XCTestExpectation *expectation = [self expectationWithDescription:@"cachePathForKey inPath"]; [[SDImageCache sharedImageCache] storeImage:[self imageForTesting] forKey:kImageTestKey completion:^(NSError * _Nullable error) { expect(error).to.beNil(); - NSString *path = [[SDImageCache sharedImageCache] cachePathForKey:kImageTestKey inPath:[[SDImageCache sharedImageCache] defaultCachePathForKey:kImageTestKey]]; + NSString *path = [[SDImageCache sharedImageCache] cachePathForKey:kImageTestKey]; expect(path).notTo.beNil; [[SDImageCache sharedImageCache] removeImageForKey:kImageTestKey withCompletion:^{ [expectation fulfill]; @@ -219,14 +219,14 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; } - (void)test34CachePathForSimpleKeyWithExtension { - NSString *cachePath = [[SDImageCache sharedImageCache] cachePathForKey:kTestJpegURL inPath:@""]; + NSString *cachePath = [[SDImageCache sharedImageCache] cachePathForKey:kTestJpegURL]; expect(cachePath).toNot.beNil(); expect([cachePath pathExtension]).to.equal(@"jpg"); } - (void)test35CachePathForKeyWithDotButNoExtension { NSString *urlString = @"https://maps.googleapis.com/maps/api/staticmap?center=48.8566,2.3522&format=png&maptype=roadmap&scale=2&size=375x200&zoom=15"; - NSString *cachePath = [[SDImageCache sharedImageCache] cachePathForKey:urlString inPath:@""]; + NSString *cachePath = [[SDImageCache sharedImageCache] cachePathForKey:urlString]; expect(cachePath).toNot.beNil(); expect([cachePath pathExtension]).to.equal(@""); } @@ -242,7 +242,7 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; UIImage *storedImageFromMemory = [[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kImageTestKey]; expect(storedImageFromMemory).to.equal(nil); - NSString *cachePath = [[SDImageCache sharedImageCache] defaultCachePathForKey:kImageTestKey]; + NSString *cachePath = [[SDImageCache sharedImageCache] cachePathForKey:kImageTestKey]; UIImage *cachedImage = [[UIImage alloc] initWithContentsOfFile:cachePath]; NSData *storedImageData = UIImageJPEGRepresentation(cachedImage, 1.0); expect(storedImageData.length).to.beGreaterThan(0);