Authored by Salvatore Randazzo
Committed by Olivier Poitrey

Custom image cache search paths

This is particularly useful if you are bundling images with your app that have been cached by SDWebImage. (ie. if you are 'seeding' your app with a core-data file that contains a lot of URL's to images and would like to also seed those images without having to copy every one of them over)

For example, you can tell SDImageCache to add '[[NSBundle mainBundle] resourcePath]' as a custom path, so that the main bundle will be queried for cached images.

This prevents the need for you to copy pre-cached images over to the caches/ImageCache folder that SDImageCache normally checks for.

The custom paths are read-only.
@@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
7 objects = { 7 objects = {
8 8
9 /* Begin PBXBuildFile section */ 9 /* Begin PBXBuildFile section */
  10 + 3E75A9861742DBE700DA412D /* CustomPathImages in Resources */ = {isa = PBXBuildFile; fileRef = 3E75A9851742DBE700DA412D /* CustomPathImages */; };
10 531041C1157EAC8F00BBABC3 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 537612E6155ABA44005750A4 /* ImageIO.framework */; }; 11 531041C1157EAC8F00BBABC3 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 537612E6155ABA44005750A4 /* ImageIO.framework */; };
11 5376129A155AB74D005750A4 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53761299155AB74D005750A4 /* UIKit.framework */; }; 12 5376129A155AB74D005750A4 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53761299155AB74D005750A4 /* UIKit.framework */; };
12 5376129C155AB74D005750A4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5376129B155AB74D005750A4 /* Foundation.framework */; }; 13 5376129C155AB74D005750A4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5376129B155AB74D005750A4 /* Foundation.framework */; };
@@ -48,6 +49,7 @@ @@ -48,6 +49,7 @@
48 /* End PBXContainerItemProxy section */ 49 /* End PBXContainerItemProxy section */
49 50
50 /* Begin PBXFileReference section */ 51 /* Begin PBXFileReference section */
  52 + 3E75A9851742DBE700DA412D /* CustomPathImages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = CustomPathImages; sourceTree = SOURCE_ROOT; };
51 53761295155AB74D005750A4 /* SDWebImage Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SDWebImage Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 53 53761295155AB74D005750A4 /* SDWebImage Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SDWebImage Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; };
52 53761299155AB74D005750A4 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 54 53761299155AB74D005750A4 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
53 5376129B155AB74D005750A4 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 55 5376129B155AB74D005750A4 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
@@ -120,6 +122,7 @@ @@ -120,6 +122,7 @@
120 5376129F155AB74D005750A4 /* SDWebImage Demo */ = { 122 5376129F155AB74D005750A4 /* SDWebImage Demo */ = {
121 isa = PBXGroup; 123 isa = PBXGroup;
122 children = ( 124 children = (
  125 + 3E75A9851742DBE700DA412D /* CustomPathImages */,
123 537612A8155AB74D005750A4 /* AppDelegate.h */, 126 537612A8155AB74D005750A4 /* AppDelegate.h */,
124 537612A9155AB74D005750A4 /* AppDelegate.m */, 127 537612A9155AB74D005750A4 /* AppDelegate.m */,
125 537612AB155AB74D005750A4 /* MasterViewController.h */, 128 537612AB155AB74D005750A4 /* MasterViewController.h */,
@@ -235,6 +238,7 @@ @@ -235,6 +238,7 @@
235 53A2B50D155B155A00B12423 /* placeholder.png in Resources */, 238 53A2B50D155B155A00B12423 /* placeholder.png in Resources */,
236 53A2B50E155B155A00B12423 /* placeholder@2x.png in Resources */, 239 53A2B50E155B155A00B12423 /* placeholder@2x.png in Resources */,
237 53EEC18916484553007601E1 /* Default-568h@2x.png in Resources */, 240 53EEC18916484553007601E1 /* Default-568h@2x.png in Resources */,
  241 + 3E75A9861742DBE700DA412D /* CustomPathImages in Resources */,
238 ); 242 );
239 runOnlyForDeploymentPostprocessing = 0; 243 runOnlyForDeploymentPostprocessing = 0;
240 }; 244 };
@@ -10,6 +10,8 @@ @@ -10,6 +10,8 @@
10 10
11 #import "MasterViewController.h" 11 #import "MasterViewController.h"
12 12
  13 +#import <SDWebImage/SDImageCache.h>
  14 +
13 @implementation AppDelegate 15 @implementation AppDelegate
14 16
15 @synthesize window = _window; 17 @synthesize window = _window;
@@ -17,6 +19,10 @@ @@ -17,6 +19,10 @@
17 19
18 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 20 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
19 { 21 {
  22 + //Add a custom read-only cache path
  23 + NSString *bundledPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"CustomPathImages"];
  24 + [[SDImageCache sharedImageCache] addReadOnlyCachePath:bundledPath];
  25 +
20 self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 26 self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
21 // Override point for customization after application launch. 27 // Override point for customization after application launch.
22 28
@@ -57,6 +57,14 @@ typedef enum SDImageCacheType SDImageCacheType; @@ -57,6 +57,14 @@ typedef enum SDImageCacheType SDImageCacheType;
57 - (id)initWithNamespace:(NSString *)ns; 57 - (id)initWithNamespace:(NSString *)ns;
58 58
59 /** 59 /**
  60 + * Add a read-only cache path to search for images pre-cached by SDImageCache
  61 + * Useful if you want to bundle pre-loaded images with your app
  62 + *
  63 + * @param path The path to use for this read-only cache path
  64 + */
  65 +- (void)addReadOnlyCachePath:(NSString *)path;
  66 +
  67 +/**
60 * Store an image into memory and disk cache at the given key. 68 * Store an image into memory and disk cache at the given key.
61 * 69 *
62 * @param image The image to store 70 * @param image The image to store
@@ -19,6 +19,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week @@ -19,6 +19,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
19 19
20 @property (strong, nonatomic) NSCache *memCache; 20 @property (strong, nonatomic) NSCache *memCache;
21 @property (strong, nonatomic) NSString *diskCachePath; 21 @property (strong, nonatomic) NSString *diskCachePath;
  22 +@property (strong, nonatomic) NSMutableArray *customPaths;
22 @property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t ioQueue; 23 @property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t ioQueue;
23 24
24 @end 25 @end
@@ -82,9 +83,33 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week @@ -82,9 +83,33 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
82 SDDispatchQueueRelease(_ioQueue); 83 SDDispatchQueueRelease(_ioQueue);
83 } 84 }
84 85
  86 +- (void)addReadOnlyCachePath:(NSString *)path
  87 +{
  88 + if (!self.customPaths)
  89 + {
  90 + self.customPaths = NSMutableArray.new;
  91 + }
  92 +
  93 + if (![self.customPaths containsObject:path])
  94 + {
  95 + [self.customPaths addObject:path];
  96 + }
  97 +}
  98 +
85 #pragma mark SDImageCache (private) 99 #pragma mark SDImageCache (private)
86 100
87 -- (NSString *)cachePathForKey:(NSString *)key 101 +- (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path
  102 +{
  103 + NSString *filename = [self cachedFileNameForKey:key];
  104 + return [path stringByAppendingPathComponent:filename];
  105 +}
  106 +
  107 +- (NSString *)defaultCachePathForKey:(NSString *)key
  108 +{
  109 + return [self cachePathForKey:key inPath:self.diskCachePath];
  110 +}
  111 +
  112 +- (NSString *)cachedFileNameForKey:(NSString *)key
88 { 113 {
89 const char *str = [key UTF8String]; 114 const char *str = [key UTF8String];
90 unsigned char r[CC_MD5_DIGEST_LENGTH]; 115 unsigned char r[CC_MD5_DIGEST_LENGTH];
@@ -92,7 +117,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week @@ -92,7 +117,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
92 NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", 117 NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
93 r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15]]; 118 r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15]];
94 119
95 - return [self.diskCachePath stringByAppendingPathComponent:filename]; 120 + return filename;
96 } 121 }
97 122
98 #pragma mark ImageCache 123 #pragma mark ImageCache
@@ -134,7 +159,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week @@ -134,7 +159,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
134 [fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL]; 159 [fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
135 } 160 }
136 161
137 - [fileManager createFileAtPath:[self cachePathForKey:key] contents:data attributes:nil]; 162 + [fileManager createFileAtPath:[self defaultCachePathForKey:key] contents:data attributes:nil];
138 } 163 }
139 }); 164 });
140 } 165 }
@@ -175,10 +200,30 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week @@ -175,10 +200,30 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
175 return diskImage; 200 return diskImage;
176 } 201 }
177 202
  203 +- (NSData *)diskImageDataBySearchingAllPathsForKey:(NSString *)key
  204 +{
  205 + NSString *defaultPath = [self defaultCachePathForKey:key];
  206 + NSData *data = [NSData dataWithContentsOfFile:defaultPath];
  207 + if (data)
  208 + {
  209 + return data;
  210 + }
  211 +
  212 + for (NSString *path in self.customPaths)
  213 + {
  214 + NSString *filePath = [self cachePathForKey:key inPath:path];
  215 + NSData *imageData = [NSData dataWithContentsOfFile:filePath];
  216 + if (imageData) {
  217 + return imageData;
  218 + }
  219 + }
  220 +
  221 + return nil;
  222 +}
  223 +
178 - (UIImage *)diskImageForKey:(NSString *)key 224 - (UIImage *)diskImageForKey:(NSString *)key
179 { 225 {
180 - NSString *path = [self cachePathForKey:key];  
181 - NSData *data = [NSData dataWithContentsOfFile:path]; 226 + NSData *data = [self diskImageDataBySearchingAllPathsForKey:key];
182 if (data) 227 if (data)
183 { 228 {
184 if ([data sd_isGIF]) 229 if ([data sd_isGIF])
@@ -259,7 +304,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week @@ -259,7 +304,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
259 { 304 {
260 dispatch_async(self.ioQueue, ^ 305 dispatch_async(self.ioQueue, ^
261 { 306 {
262 - [[NSFileManager defaultManager] removeItemAtPath:[self cachePathForKey:key] error:nil]; 307 + [[NSFileManager defaultManager] removeItemAtPath:[self defaultCachePathForKey:key] error:nil];
263 }); 308 });
264 } 309 }
265 } 310 }