123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714 |
- //
- // MKNetworkEngine.m
- // MKNetworkKit
- //
- // Created by Mugunth Kumar (@mugunthkumar) on 11/11/11.
- // Copyright (C) 2011-2020 by Steinlogic Consulting and Training Pte Ltd
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- #import "MKNetworkKit.h"
- #define kFreezableOperationExtension @"mknetworkkitfrozenoperation"
- #ifdef __OBJC_GC__
- #error MKNetworkKit does not support Objective-C Garbage Collection
- #endif
- #if TARGET_OS_IPHONE
- #ifndef __IPHONE_5_0
- #error MKNetworkKit does not support iOS 4 and lower
- #endif
- #endif
- #if ! __has_feature(objc_arc)
- #error MKNetworkKit is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
- #endif
- @interface MKNetworkEngine (/*Private Methods*/)
- @property (copy, nonatomic) NSString *hostName;
- @property (strong, nonatomic) Reachability *reachability;
- @property (copy, nonatomic) NSDictionary *customHeaders;
- @property (assign, nonatomic) Class customOperationSubclass;
- @property (nonatomic, strong) NSMutableDictionary *memoryCache;
- @property (nonatomic, strong) NSMutableArray *memoryCacheKeys;
- @property (nonatomic, strong) NSMutableDictionary *cacheInvalidationParams;
- #if OS_OBJECT_USE_OBJC
- @property (strong, nonatomic) dispatch_queue_t backgroundCacheQueue;
- @property (strong, nonatomic) dispatch_queue_t operationQueue;
- #else
- @property (assign, nonatomic) dispatch_queue_t backgroundCacheQueue;
- @property (assign, nonatomic) dispatch_queue_t operationQueue;
- #endif
- -(void) saveCache;
- -(void) saveCacheData:(NSData*) data forKey:(NSString*) cacheDataKey;
- -(void) freezeOperations;
- -(void) checkAndRestoreFrozenOperations;
- -(BOOL) isCacheEnabled;
- @end
- static NSOperationQueue *_sharedNetworkQueue;
- @implementation MKNetworkEngine
- // Network Queue is a shared singleton object.
- // no matter how many instances of MKNetworkEngine is created, there is one and only one network queue
- // In theory an app should contain as many network engines as the number of domains it talks to
- #pragma mark -
- #pragma mark Initialization
- #define kHImageCacheExpire 24.0*3600*365*100
- +(void) initialize {
-
- if(!_sharedNetworkQueue) {
- static dispatch_once_t oncePredicate;
- dispatch_once(&oncePredicate, ^{
- _sharedNetworkQueue = [[NSOperationQueue alloc] init];
- [_sharedNetworkQueue addObserver:[self self] forKeyPath:@"operationCount" options:0 context:NULL];
- [_sharedNetworkQueue setMaxConcurrentOperationCount:6];
-
- });
- }
- }
- - (id) init {
-
- return [self initWithHostName:nil];
- }
- - (id) initWithHostName:(NSString*) hostName {
-
- return [self initWithHostName:hostName apiPath:nil customHeaderFields:nil];
- }
- - (id) initWithHostName:(NSString*) hostName apiPath:(NSString*) apiPath customHeaderFields:(NSDictionary*) headers {
-
- if((self = [super init])) {
-
- self.apiPath = apiPath;
- self.backgroundCacheQueue = dispatch_queue_create("com.mknetworkkit.cachequeue", DISPATCH_QUEUE_SERIAL);
- self.operationQueue = dispatch_queue_create("com.mknetworkkit.operationqueue", DISPATCH_QUEUE_SERIAL);
-
- if(hostName) {
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(reachabilityChanged:)
- name:kReachabilityChangedNotification
- object:nil];
-
- self.hostName = hostName;
- self.reachability = [Reachability reachabilityWithHostname:self.hostName];
-
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-
- [self.reachability startNotifier];
- });
- }
-
- if(headers[@"User-Agent"] == nil) {
-
- NSMutableDictionary *newHeadersDict = [headers mutableCopy];
- NSString *userAgentString = [NSString stringWithFormat:@"%@/%@",
- [[NSBundle mainBundle] infoDictionary][(NSString *)kCFBundleNameKey],
- [[NSBundle mainBundle] infoDictionary][(NSString *)kCFBundleVersionKey]];
- newHeadersDict[@"User-Agent"] = userAgentString;
- self.customHeaders = newHeadersDict;
- } else {
- self.customHeaders = [headers mutableCopy];
- }
-
- self.customOperationSubclass = [MKNetworkOperation class];
- }
-
- return self;
- }
- - (id) initWithHostName:(NSString*) hostName customHeaderFields:(NSDictionary*) headers {
-
- return [self initWithHostName:hostName apiPath:nil customHeaderFields:headers];
- }
- #pragma mark -
- #pragma mark Memory Mangement
- -(void) dealloc {
-
- [[NSNotificationCenter defaultCenter] removeObserver:self name:kReachabilityChangedNotification object:nil];
- #if TARGET_OS_IPHONE
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillTerminateNotification object:nil];
- #elif TARGET_OS_MAC
- [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillHideNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillResignActiveNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillTerminateNotification object:nil];
- #endif
-
- }
- +(void) dealloc {
-
- [_sharedNetworkQueue removeObserver:[self self] forKeyPath:@"operationCount"];
- }
- #pragma mark -
- #pragma mark KVO for network Queue
- + (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
- change:(NSDictionary *)change context:(void *)context
- {
- if (object == _sharedNetworkQueue && [keyPath isEqualToString:@"operationCount"]) {
-
- [[NSNotificationCenter defaultCenter] postNotificationName:kMKNetworkEngineOperationCountChanged
- object:[NSNumber numberWithInteger:(NSInteger)[_sharedNetworkQueue operationCount]]];
- #if TARGET_OS_IPHONE
- [UIApplication sharedApplication].networkActivityIndicatorVisible =
- ([_sharedNetworkQueue.operations count] > 0);
- #endif
- }
- else {
- [super observeValueForKeyPath:keyPath ofObject:object
- change:change context:context];
- }
- }
- #pragma mark -
- #pragma mark Reachability related
- -(void) reachabilityChanged:(NSNotification*) notification
- {
- if([self.reachability currentReachabilityStatus] == ReachableViaWiFi)
- {
- // DLog(@"Server [%@] is reachable via Wifi", self.hostName);
- [_sharedNetworkQueue setMaxConcurrentOperationCount:6];
-
- [self checkAndRestoreFrozenOperations];
- }
- else if([self.reachability currentReachabilityStatus] == ReachableViaWWAN)
- {
- if(self.wifiOnlyMode) {
-
- DLog(@" Disabling engine as server [%@] is reachable only via cellular data.", self.hostName);
- [_sharedNetworkQueue setMaxConcurrentOperationCount:0];
- } else {
- DLog(@"Server [%@] is reachable only via cellular data", self.hostName);
- [_sharedNetworkQueue setMaxConcurrentOperationCount:2];
- [self checkAndRestoreFrozenOperations];
- }
- }
- else if([self.reachability currentReachabilityStatus] == NotReachable)
- {
- DLog(@"Server [%@] is not reachable", self.hostName);
- [self freezeOperations];
- }
-
- if(self.reachabilityChangedHandler) {
- self.reachabilityChangedHandler([self.reachability currentReachabilityStatus]);
- }
- }
- #pragma mark Freezing operations (Called when network connectivity fails)
- -(void) freezeOperations {
-
- if(![self isCacheEnabled]) return;
-
- for(MKNetworkOperation *operation in _sharedNetworkQueue.operations) {
-
- // freeze only freeable operations.
- if(![operation freezable]) continue;
-
- if(!self.hostName) return;
-
- // freeze only operations that belong to this server
- if([[operation url] rangeOfString:self.hostName].location == NSNotFound) continue;
-
- NSString *archivePath = [[[self cacheDirectoryName] stringByAppendingPathComponent:[operation uniqueIdentifier]]
- stringByAppendingPathExtension:kFreezableOperationExtension];
- [NSKeyedArchiver archiveRootObject:operation toFile:archivePath];
- [operation cancel];
- }
-
- }
- -(void) checkAndRestoreFrozenOperations {
-
- if(![self isCacheEnabled]) return;
-
- NSError *error = nil;
- NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[self cacheDirectoryName] error:&error];
- if(error)
- DLog(@"%@", error);
-
- NSArray *pendingOperations = [files filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
-
- NSString *thisFile = (NSString*) evaluatedObject;
- return ([thisFile rangeOfString:kFreezableOperationExtension].location != NSNotFound);
- }]];
-
- for(NSString *pendingOperationFile in pendingOperations) {
-
- NSString *archivePath = [[self cacheDirectoryName] stringByAppendingPathComponent:pendingOperationFile];
- MKNetworkOperation *pendingOperation = [NSKeyedUnarchiver unarchiveObjectWithFile:archivePath];
- [self enqueueOperation:pendingOperation];
- NSError *error2 = nil;
- [[NSFileManager defaultManager] removeItemAtPath:archivePath error:&error2];
- if(error2)
- DLog(@"%@", error2);
- }
- }
- -(NSString*) readonlyHostName {
-
- return [_hostName copy];
- }
- -(BOOL) isReachable {
-
- return ([self.reachability currentReachabilityStatus] != NotReachable);
- }
- #pragma mark -
- #pragma mark Create methods
- -(void) registerOperationSubclass:(Class) aClass {
-
- self.customOperationSubclass = aClass;
- }
- -(MKNetworkOperation*) operationWithPath:(NSString*) path {
-
- return [self operationWithPath:path params:nil];
- }
- -(MKNetworkOperation*) operationWithPath:(NSString*) path
- params:(NSDictionary*) body {
-
- return [self operationWithPath:path
- params:body
- httpMethod:@"GET"];
- }
- -(MKNetworkOperation*) operationWithPath:(NSString*) path
- params:(NSDictionary*) body
- httpMethod:(NSString*)method {
-
- return [self operationWithPath:path params:body httpMethod:method ssl:NO];
- }
- -(MKNetworkOperation*) operationWithPath:(NSString*) path
- params:(NSDictionary*) body
- httpMethod:(NSString*)method
- ssl:(BOOL) useSSL {
-
- if(self.hostName == nil) {
-
- DLog(@"Hostname is nil, use operationWithURLString: method to create absolute URL operations");
- return nil;
- }
-
- NSMutableString *urlString = [NSMutableString stringWithFormat:@"%@://%@", useSSL ? @"https" : @"http", self.hostName];
-
- if(self.portNumber != 0)
- [urlString appendFormat:@":%d", self.portNumber];
-
- if(self.apiPath)
- [urlString appendFormat:@"/%@", self.apiPath];
-
- [urlString appendFormat:@"/%@", path];
-
- return [self operationWithURLString:urlString params:body httpMethod:method];
- }
- -(MKNetworkOperation*) operationWithURLString:(NSString*) urlString {
-
- return [self operationWithURLString:urlString params:nil httpMethod:@"GET"];
- }
- -(MKNetworkOperation*) operationWithURLString:(NSString*) urlString
- params:(NSDictionary*) body {
-
- return [self operationWithURLString:urlString params:body httpMethod:@"GET"];
- }
- -(MKNetworkOperation*) operationWithURLString:(NSString*) urlString
- params:(NSDictionary*) body
- httpMethod:(NSString*)method {
-
- MKNetworkOperation *operation = [[self.customOperationSubclass alloc] initWithURLString:urlString params:body httpMethod:method];
-
- [self prepareHeaders:operation];
- return operation;
- }
- -(void) prepareHeaders:(MKNetworkOperation*) operation {
-
- [operation addHeaders:self.customHeaders];
- }
- -(NSData*) cachedDataForOperation:(MKNetworkOperation*) operation {
-
- NSData *cachedData = (self.memoryCache)[[operation uniqueIdentifier]];
- if(cachedData) return cachedData;
-
- NSString *filePath = [[self cacheDirectoryName] stringByAppendingPathComponent:[operation uniqueIdentifier]];
-
- if([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
-
- cachedData = [NSData dataWithContentsOfFile:filePath];
- [self saveCacheData:cachedData forKey:[operation uniqueIdentifier]]; // bring it back to the in-memory cache
- return cachedData;
- }
-
- return nil;
- }
- -(void) enqueueOperation:(MKNetworkOperation*) operation {
-
- [self enqueueOperation:operation forceReload:NO];
- }
- -(void) enqueueOperation:(MKNetworkOperation*) operation forceReload:(BOOL) forceReload {
-
- NSParameterAssert(operation != nil);
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- [operation setCacheHandler:^(MKNetworkOperation* completedCacheableOperation) {
-
- // if this is not called, the request would have been a non cacheable request
- //completedCacheableOperation.cacheHeaders;
- NSString *uniqueId = [completedCacheableOperation uniqueIdentifier];
- [self saveCacheData:[completedCacheableOperation responseData]
- forKey:uniqueId];
-
- (self.cacheInvalidationParams)[uniqueId] = completedCacheableOperation.cacheHeaders;
- }];
-
- __block double expiryTimeInSeconds = 0.0f;
-
- if([operation isCacheable]) {
-
- NSData *cachedData = [self cachedDataForOperation:operation];
- if(cachedData) {
- dispatch_async(dispatch_get_main_queue(), ^{
- // Jump back to the original thread here since setCachedData updates the main thread
- [operation setCachedData:cachedData];
- });
-
- if(!forceReload) {
-
- NSString *uniqueId = [operation uniqueIdentifier];
- NSMutableDictionary *savedCacheHeaders = (self.cacheInvalidationParams)[uniqueId];
- // there is a cached version.
- // this means, the current operation is a "GET"
- if(savedCacheHeaders) {
- NSString *expiresOn = savedCacheHeaders[@"Expires"];
-
- dispatch_sync(self.operationQueue, ^{
- NSDate *expiresOnDate = [NSDate dateFromRFC1123:expiresOn];
- if (!expiresOnDate) {
- expiryTimeInSeconds = operation.imageCacheDuration;
- }
- else{
- expiryTimeInSeconds = [expiresOnDate timeIntervalSinceNow];
- }
- });
-
- [operation updateOperationBasedOnPreviousHeaders:savedCacheHeaders];
- }
- }
- }
-
- dispatch_sync(self.operationQueue, ^{
-
- NSArray *operations = _sharedNetworkQueue.operations;
- NSUInteger index = [operations indexOfObject:operation];
- BOOL operationFinished = NO;
- if(index != NSNotFound) {
-
- MKNetworkOperation *queuedOperation = (MKNetworkOperation*) (operations)[index];
- operationFinished = [queuedOperation isFinished];
- if(!operationFinished)
- [queuedOperation updateHandlersFromOperation:operation];
- }
-
- if(expiryTimeInSeconds <= 0 || forceReload || operationFinished)
- [_sharedNetworkQueue addOperation:operation];
- // else don't do anything
- });
-
- } else {
-
- [_sharedNetworkQueue addOperation:operation];
- }
-
- if([self.reachability currentReachabilityStatus] == NotReachable)
- [self freezeOperations];
- });
- }
- - (MKNetworkOperation*)imageAtURL:(NSURL *)url completionHandler:(MKNKImageBlock) imageFetchedBlock errorHandler:(MKNKResponseErrorBlock) errorBlock {
-
- #ifdef DEBUG
- // I could enable caching here, but that hits performance and inturn affects table view scrolling
- // if imageAtURL is called for loading thumbnails.
- if(![self isCacheEnabled]) DLog(@"imageAtURL:onCompletion: requires caching to be enabled.")
- #endif
-
- if (url == nil) {
- return nil;
- }
-
- MKNetworkOperation *op = [self operationWithURLString:[url absoluteString]];
-
- [op addCompletionHandler:^(MKNetworkOperation *completedOperation) {
-
- imageFetchedBlock([completedOperation responseImage],
- url,
- [completedOperation isCachedResponse]);
-
- } errorHandler:^(MKNetworkOperation *completedOperation, NSError *error) {
-
- errorBlock(completedOperation, error);
- }];
-
- [self enqueueOperation:op];
-
- return op;
- }
- #if TARGET_OS_IPHONE
- - (MKNetworkOperation*)imageAtURL:(NSURL *)url size:(CGSize) size completionHandler:(MKNKImageBlock) imageFetchedBlock errorHandler:(MKNKResponseErrorBlock) errorBlock {
-
- #ifdef DEBUG
- // I could enable caching here, but that hits performance and inturn affects table view scrolling
- // if imageAtURL is called for loading thumbnails.
- if(![self isCacheEnabled]) DLog(@"imageAtURL:size:onCompletion: requires caching to be enabled.")
- #endif
-
- if (url == nil) {
- return nil;
- }
-
- MKNetworkOperation *op = [self operationWithURLString:[url absoluteString]];
-
- [op addCompletionHandler:^(MKNetworkOperation *completedOperation) {
- [completedOperation decompressedResponseImageOfSize:size
- completionHandler:^(UIImage *decompressedImage) {
-
- imageFetchedBlock(decompressedImage,
- url,
- [completedOperation isCachedResponse]);
- }];
- } errorHandler:^(MKNetworkOperation *completedOperation, NSError *error) {
-
- errorBlock(completedOperation, error);
- DLog(@"%@", error);
- }];
-
- [self enqueueOperation:op];
-
- return op;
- }
- - (MKNetworkOperation*)imageAtURL:(NSURL *)url size:(CGSize) size onCompletion:(MKNKImageBlock) imageFetchedBlock {
-
- return [self imageAtURL:url size:size completionHandler:imageFetchedBlock errorHandler:^(MKNetworkOperation* op, NSError* error){}];
- }
- #endif
- - (MKNetworkOperation*)imageAtURL:(NSURL *)url onCompletion:(MKNKImageBlock) imageFetchedBlock
- {
- return [self imageAtURL:url completionHandler:imageFetchedBlock errorHandler:^(MKNetworkOperation* op, NSError* error){}];
- }
- #pragma mark -
- #pragma mark Cache related
- -(NSString*) cacheDirectoryName {
-
- static NSString *cacheDirectoryName = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
- NSString *documentsDirectory = paths[0];
- cacheDirectoryName = [documentsDirectory stringByAppendingPathComponent:MKNETWORKCACHE_DEFAULT_DIRECTORY];
- });
-
- return cacheDirectoryName;
- }
- -(int) cacheMemoryCost {
-
- return MKNETWORKCACHE_DEFAULT_COST;
- }
- -(void) saveCache {
-
- for(NSString *cacheKey in [self.memoryCache allKeys])
- {
- NSString *filePath = [[self cacheDirectoryName] stringByAppendingPathComponent:cacheKey];
- if([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
-
- NSError *error = nil;
- [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
- ELog(error);
- }
-
- [(self.memoryCache)[cacheKey] writeToFile:filePath atomically:YES];
- }
-
- [self.memoryCache removeAllObjects];
- [self.memoryCacheKeys removeAllObjects];
-
- NSString *cacheInvalidationPlistFilePath = [[self cacheDirectoryName] stringByAppendingPathExtension:@"plist"];
- [self.cacheInvalidationParams writeToFile:cacheInvalidationPlistFilePath atomically:YES];
- }
- -(void) saveCacheData:(NSData*) data forKey:(NSString*) cacheDataKey
- {
- dispatch_async(self.backgroundCacheQueue, ^{
-
- (self.memoryCache)[cacheDataKey] = data;
-
- NSUInteger index = [self.memoryCacheKeys indexOfObject:cacheDataKey];
- if(index != NSNotFound)
- [self.memoryCacheKeys removeObjectAtIndex:index];
-
- [self.memoryCacheKeys insertObject:cacheDataKey atIndex:0]; // remove it and insert it at start
-
- if([self.memoryCacheKeys count] >= (NSUInteger)[self cacheMemoryCost])
- {
- NSString *lastKey = [self.memoryCacheKeys lastObject];
- NSData *data2 = (self.memoryCache)[lastKey];
- NSString *filePath = [[self cacheDirectoryName] stringByAppendingPathComponent:lastKey];
-
- if([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
-
- NSError *error = nil;
- [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
- ELog(error);
- }
- [data2 writeToFile:filePath atomically:YES];
-
- [self.memoryCacheKeys removeLastObject];
- [self.memoryCache removeObjectForKey:lastKey];
- }
- });
- }
- /*
- - (BOOL) dataOldness:(NSString*) imagePath
- {
- NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:imagePath error:nil];
- NSDate *creationDate = [attributes valueForKey:NSFileCreationDate];
-
- return abs([creationDate timeIntervalSinceNow]);
- }*/
- -(BOOL) isCacheEnabled {
-
- BOOL isDir = NO;
- BOOL isCachingEnabled = [[NSFileManager defaultManager] fileExistsAtPath:[self cacheDirectoryName] isDirectory:&isDir];
- return isCachingEnabled;
- }
- -(void) useCache {
-
- self.memoryCache = [NSMutableDictionary dictionaryWithCapacity:[self cacheMemoryCost]];
- self.memoryCacheKeys = [NSMutableArray arrayWithCapacity:[self cacheMemoryCost]];
- self.cacheInvalidationParams = [NSMutableDictionary dictionary];
-
- NSString *cacheDirectory = [self cacheDirectoryName];
- BOOL isDirectory = YES;
- BOOL folderExists = [[NSFileManager defaultManager] fileExistsAtPath:cacheDirectory isDirectory:&isDirectory] && isDirectory;
-
- if (!folderExists)
- {
- NSError *error = nil;
- [[NSFileManager defaultManager] createDirectoryAtPath:cacheDirectory withIntermediateDirectories:YES attributes:nil error:&error];
- }
-
- NSString *cacheInvalidationPlistFilePath = [cacheDirectory stringByAppendingPathExtension:@"plist"];
-
- BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:cacheInvalidationPlistFilePath];
-
- if (fileExists)
- {
- self.cacheInvalidationParams = [NSMutableDictionary dictionaryWithContentsOfFile:cacheInvalidationPlistFilePath];
- }
-
- #if TARGET_OS_IPHONE
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveCache)
- name:UIApplicationDidReceiveMemoryWarningNotification
- object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveCache)
- name:UIApplicationDidEnterBackgroundNotification
- object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveCache)
- name:UIApplicationWillTerminateNotification
- object:nil];
-
- #elif TARGET_OS_MAC
-
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveCache)
- name:NSApplicationWillHideNotification
- object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveCache)
- name:NSApplicationWillResignActiveNotification
- object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveCache)
- name:NSApplicationWillTerminateNotification
- object:nil];
-
- #endif
-
-
- }
- -(void) emptyCache {
-
- [self saveCache]; // ensures that invalidation params are written to disk properly
- NSError *error = nil;
- NSArray *directoryContents = [[NSFileManager defaultManager]
- contentsOfDirectoryAtPath:[self cacheDirectoryName] error:&error];
- // if(error) DLog(@"%@", error);
-
- error = nil;
- for(NSString *fileName in directoryContents) {
-
- NSString *path = [[self cacheDirectoryName] stringByAppendingPathComponent:fileName];
- [[NSFileManager defaultManager] removeItemAtPath:path error:&error];
- // if(error) DLog(@"%@", error);
- }
-
- error = nil;
- NSString *cacheInvalidationPlistFilePath = [[self cacheDirectoryName] stringByAppendingPathExtension:@"plist"];
- [[NSFileManager defaultManager] removeItemAtPath:cacheInvalidationPlistFilePath error:&error];
- // if(error) DLog(@"%@", error);
- }
- @end
|