Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions Demo/JMImageCacheDemo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@
29B97313FDCFA39411CA2CEA /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0440;
LastUpgradeCheck = 0510;
};
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "JMImageCacheDemo" */;
compatibilityVersion = "Xcode 3.2";
Expand Down Expand Up @@ -254,19 +254,18 @@
C01FCF4F08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
GCC_C_LANGUAGE_STANDARD = c99;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
C01FCF5008A954540054247B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
GCC_C_LANGUAGE_STANDARD = c99;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
Expand Down
121 changes: 84 additions & 37 deletions JMImageCache.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
dispatch_once(&onceToken, ^{
_JMImageCacheDirectory = [[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Caches/JMCache"] copy];
});

return _JMImageCacheDirectory;
}
inline static NSString *keyForURL(NSURL *url) {
return [url absoluteString];
}
static inline NSString *cachePathForKey(NSString *key) {
NSString *fileName = [NSString stringWithFormat:@"JMImageCache-%u", [key hash]];
NSString *fileName = [NSString stringWithFormat:@"JMImageCache-%lu", (unsigned long)[key hash]];
return [JMImageCacheDirectory() stringByAppendingPathComponent:fileName];
}

Expand All @@ -41,20 +41,20 @@ @implementation JMImageCache
+ (JMImageCache *) sharedCache {
static JMImageCache *_sharedCache = nil;
static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{
_sharedCache = [[JMImageCache alloc] init];
});

return _sharedCache;
}

- (id) init {
self = [super init];
if(!self) return nil;

self.diskOperationQueue = [[NSOperationQueue alloc] init];

[[NSFileManager defaultManager] createDirectoryAtPath:JMImageCacheDirectory()
withIntermediateDirectories:YES
attributes:nil
Expand All @@ -65,14 +65,24 @@ - (id) init {
- (void) _downloadAndWriteImageForURL:(NSURL *)url key:(NSString *)key completionBlock:(void (^)(UIImage *image))completion failureBlock:(void (^)(NSURLRequest *request, NSURLResponse *response, NSError* error))failure
{
if (!key && !url) return;

if (!key) {
key = keyForURL(url);
}


__weak JMImageCache *safeSelf = self;


dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

NSURLRequest* request = [NSURLRequest requestWithURL:url];
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];

NSDate *lastUpdated = [safeSelf dateForImageKey:key];
NSString *HTTPdate = [safeSelf httpDateForDate:lastUpdated];
if (HTTPdate) {
[request addValue:HTTPdate forHTTPHeaderField:@"If-Modified-Since"];
}

NSURLResponse* response = nil;
NSError* error = nil;
NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
Expand All @@ -86,8 +96,15 @@ - (void) _downloadAndWriteImageForURL:(NSURL *)url key:(NSString *)key completio
return;
}

UIImage *i = [[UIImage alloc] initWithData:data];
if (!i)
NSInteger kUnModifiedHttpCode = 304;
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (httpResponse.statusCode == kUnModifiedHttpCode)
{
return;
}

UIImage *imageDownloaded = [[UIImage alloc] initWithData:data];
if (!imageDownloaded)
{
NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
[errorDetail setValue:[NSString stringWithFormat:@"Failed to init image with data from for URL: %@", url] forKey:NSLocalizedDescriptionKey];
Expand All @@ -102,33 +119,42 @@ - (void) _downloadAndWriteImageForURL:(NSURL *)url key:(NSString *)key completio
NSString *cachePath = cachePathForKey(key);
NSInvocation *writeInvocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(writeData:toPath:)]];

[writeInvocation setTarget:self];
[writeInvocation setTarget:safeSelf];
[writeInvocation setSelector:@selector(writeData:toPath:)];
[writeInvocation setArgument:&data atIndex:2];
[writeInvocation setArgument:&cachePath atIndex:3];

[self performDiskWriteOperation:writeInvocation];
[self setImage:i forKey:key];
[safeSelf performDiskWriteOperation:writeInvocation];
[safeSelf setImage:imageDownloaded forKey:key];

dispatch_async(dispatch_get_main_queue(), ^{
if(completion) completion(i);
if(completion) completion(imageDownloaded);
});
}
});
}

- (NSString *)httpDateForDate:(NSDate *)lastUpdated {
NSDateFormatter* httpDateFormatter = [[NSDateFormatter alloc] init];
httpDateFormatter.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'";
httpDateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
httpDateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
NSString *HTTPdate = [httpDateFormatter stringFromDate:lastUpdated];
return HTTPdate;
}

- (void) removeAllObjects {
[super removeAllObjects];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSError *error = nil;
NSArray *directoryContents = [fileMgr contentsOfDirectoryAtPath:JMImageCacheDirectory() error:&error];

if (error == nil) {
for (NSString *path in directoryContents) {
NSString *fullPath = [JMImageCacheDirectory() stringByAppendingPathComponent:path];

BOOL removeSuccess = [fileMgr removeItemAtPath:fullPath error:&error];
if (!removeSuccess) {
//Error Occured
Expand All @@ -141,13 +167,13 @@ - (void) removeAllObjects {
}
- (void) removeObjectForKey:(id)key {
[super removeObjectForKey:key];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *cachePath = cachePathForKey(key);

NSError *error = nil;

BOOL removeSuccess = [fileMgr removeItemAtPath:cachePath error:&error];
if (!removeSuccess) {
//Error Occured
Expand All @@ -159,14 +185,18 @@ - (void) removeObjectForKey:(id)key {
#pragma mark Getter Methods

- (void) imageForURL:(NSURL *)url key:(NSString *)key completionBlock:(void (^)(UIImage *image))completion failureBlock:(void (^)(NSURLRequest *request, NSURLResponse *response, NSError* error))failure{

UIImage *i = [self cachedImageForKey:key];

if(i) {
if(completion) completion(i);
} else {
[self _downloadAndWriteImageForURL:url key:key completionBlock:completion failureBlock:failure];

if (!key) {
key = keyForURL(url);
}

UIImage *cachedImage = [self cachedImageForKey:key];

if(cachedImage) {
if(completion) completion(cachedImage);
}
[self _downloadAndWriteImageForURL:url key:key completionBlock:completion failureBlock:failure];

}

- (void) imageForURL:(NSURL *)url completionBlock:(void (^)(UIImage *image))completion failureBlock:(void (^)(NSURLRequest *request, NSURLResponse *response, NSError* error))failure{
Expand All @@ -175,18 +205,18 @@ - (void) imageForURL:(NSURL *)url completionBlock:(void (^)(UIImage *image))comp

- (UIImage *) cachedImageForKey:(NSString *)key {
if(!key) return nil;

id returner = [super objectForKey:key];

if(returner) {
return returner;
} else {
UIImage *i = [self imageFromDiskForKey:key];
if(i) [self setImage:i forKey:key];

return i;
}

return nil;
}

Expand All @@ -197,9 +227,9 @@ - (UIImage *) cachedImageForURL:(NSURL *)url {

- (UIImage *) imageForURL:(NSURL *)url key:(NSString*)key delegate:(id<JMImageCacheDelegate>)d {
if(!url) return nil;

UIImage *i = [self cachedImageForURL:url];

if(i) {
return i;
} else {
Expand All @@ -213,9 +243,9 @@ - (UIImage *) imageForURL:(NSURL *)url key:(NSString*)key delegate:(id<JMImageCa
}
}
}
failureBlock:nil];
failureBlock:nil];
}

return nil;
}

Expand All @@ -224,10 +254,27 @@ - (UIImage *) imageForURL:(NSURL *)url delegate:(id<JMImageCacheDelegate>)d {
}

- (UIImage *) imageFromDiskForKey:(NSString *)key {
UIImage *i = [[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:cachePathForKey(key) options:0 error:NULL]];

NSString *pathToImage = cachePathForKey(key);

UIImage *i = [[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:pathToImage options:0 error:NULL]];
return i;
}

- (NSDate *)dateForImageKey:(NSString *)key {

NSString *pathToImage = cachePathForKey(key);

if(!pathToImage)
return nil;

NSFileManager *fileManager = [NSFileManager defaultManager];
NSDictionary* imageAttribute = [fileManager attributesOfItemAtPath:pathToImage error:nil];
NSDate *lastUpdated = [imageAttribute fileModificationDate];

return lastUpdated;
}

- (UIImage *) imageFromDiskForURL:(NSURL *)url {
return [self imageFromDiskForKey:keyForURL(url)];
}
Expand Down
4 changes: 2 additions & 2 deletions JMImageCache.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "JMImageCache"
s.version = "0.4.0"
s.version = "0.4.2"
s.summary = "NSCache based remote-image caching and downloading mechanism for iOS."
s.description = 'NSCache based remote-image caching and downloading mechanism for iOS. Is block based and uses a simple UIImageView category to handle loading images with placeholders. With fix to the placeholder behaviour'

Expand All @@ -13,7 +13,7 @@ Pod::Spec.new do |s|

s.authors = { "Jake Marsh" => "[email protected]" }

s.source = { :git => "https://github.com/jakemarsh/JMImageCache.git", :tag => "0.4.0" }
s.source = { :git => "https://github.com/antoinerabanes/JMImageCache.git", :tag => s.version.to_s }

s.platform = :ios, '5.0'
s.requires_arc = true
Expand Down
Loading