Skip to content
This repository has been archived by the owner on Mar 4, 2020. It is now read-only.

LRU Implementation for disk cache with overridable limit for disk cache size #382

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
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
17 changes: 17 additions & 0 deletions MKNetworkKit/Categories/NSArray+RequestEncoding.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// NSArray+RequestEncoding.h
// Bild.de-iPad
//
// Created by Vittorio Monaco on 07/10/13.
// Copyright (c) 2013 Axel Springer Verlag. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface NSArray (RequestEncoding)

-(NSString*) urlEncodedKeyValueString;
-(NSString*) jsonEncodedKeyValueString;
-(NSString*) plistEncodedKeyValueString;

@end
45 changes: 45 additions & 0 deletions MKNetworkKit/Categories/NSArray+RequestEncoding.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// NSArray+RequestEncoding.m
// Bild.de-iPad
//
// Created by Vittorio Monaco on 07/10/13.
// Copyright (c) 2013 Axel Springer Verlag. All rights reserved.
//

#import "NSArray+RequestEncoding.h"
#import "MKNetworkKit.h"

@implementation NSArray (RequestEncoding)

-(NSString*) jsonEncodedKeyValueString
{

NSError *error = nil;
NSData *data = [NSJSONSerialization dataWithJSONObject:self
options:0 // non-pretty printing
error:&error];
if(error)
DLog(@"JSON Parsing Error: %@", error);

return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}

-(NSString*) urlEncodedKeyValueString {

//How to correctly URL-encode NSArray?
return nil;
}

-(NSString*) plistEncodedKeyValueString {

NSError *error = nil;
NSData *data = [NSPropertyListSerialization dataWithPropertyList:self
format:NSPropertyListXMLFormat_v1_0
options:0 error:&error];
if(error)
DLog(@"JSON Parsing Error: %@", error);

return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}

@end
12 changes: 12 additions & 0 deletions MKNetworkKit/MKNetworkEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -422,4 +422,16 @@
*/
@property (nonatomic, assign) BOOL shouldSendAcceptLanguageHeader;

//Disk cache management additions
/*!
* @abstract Cache Directory In Disk Size
*
* @discussion
* This method can be over-ridden by subclasses to provide an alternative in disk cache size.
* By default, MKNetworkKit caches MKNETWORKCACHE_DEFAULT_DISK_SIZE bytes in disk cache
* The default size is MKNETWORKCACHE_DEFAULT_DISK_SIZE (50 MB)
* Overriding this method is optional
*/
-(unsigned long long int) cacheDiskSize;

@end
97 changes: 92 additions & 5 deletions MKNetworkKit/MKNetworkEngine.m
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,12 @@ -(void) useCache {
name:UIApplicationWillTerminateNotification
object:nil];

//check for emptyCache whenever coming to app
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(emptyCache)
name:UIApplicationDidBecomeActiveNotification
object:nil];

#elif TARGET_OS_MAC

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveCache)
Expand All @@ -745,16 +751,63 @@ -(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];
NSArray *directoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:[NSURL fileURLWithPath:[self cacheDirectoryName]]
includingPropertiesForKeys:@[NSURLContentModificationDateKey]
options:NSDirectoryEnumerationSkipsHiddenFiles
error:&error];

if(error) DLog(@"%@", error);

error = nil;
for(NSString *fileName in directoryContents) {

unsigned long long int folderSize = [self folderSize:[self cacheDirectoryName]];
long long int sizeToRemove = folderSize - [self cacheDiskSize];

if (sizeToRemove <= 0) {
//return if we have not exceeded the limit
return;
}

NSArray *sortedContent = [directoryContents sortedArrayUsingComparator:
^(NSURL *file1, NSURL *file2)
{
// compare
NSDate *file1Date;
[file1 getResourceValue:&file1Date forKey:NSURLContentModificationDateKey error:nil];

NSDate *file2Date;
[file2 getResourceValue:&file2Date forKey:NSURLContentModificationDateKey error:nil];

// Ascending:
return [file1Date compare: file2Date];
// Descending:
//return [file2Date compare: file1Date];
}];


unsigned long long int fileSize = 0;

for(NSString *fileName in sortedContent) {

NSString *path = [[self cacheDirectoryName] stringByAppendingPathComponent:fileName];

//calcualte size and add to count
NSDictionary *fileDictionary = [[NSFileManager defaultManager]
attributesOfItemAtPath:path
error:&error];

fileSize += [fileDictionary fileSize];

//remove file
[[NSFileManager defaultManager] removeItemAtPath:path error:&error];
if(error) DLog(@"%@", error);

if(error)
DLog(@"%@", error);

//break if we are back in limit
if (fileSize > sizeToRemove) {
break;
}
}

error = nil;
Expand All @@ -763,4 +816,38 @@ -(void) emptyCache {
if(error) DLog(@"%@", error);
}

@end
- (NSOperationQueue *)networkQueue
{
return _sharedNetworkQueue;
}

#pragma mark - Disk cache size management additions

-(unsigned long long int) cacheDiskSize{

return MKNETWORKCACHE_DEFAULT_DISK_SIZE;
}

- (unsigned long long int)folderSize:(NSString *)folderPath {

NSArray *filesArray = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil];
NSEnumerator *filesEnumerator = [filesArray objectEnumerator];
NSString *fileName;
unsigned long long int fileSize = 0;

while (fileName = [filesEnumerator nextObject]) {

NSString *path = [[self cacheDirectoryName] stringByAppendingPathComponent:fileName];
NSError *error = nil;

NSDictionary *fileDictionary = [[NSFileManager defaultManager]
attributesOfItemAtPath:path
error:&error];

fileSize += [fileDictionary fileSize];
}

return fileSize;
}

@end
3 changes: 3 additions & 0 deletions MKNetworkKit/MKNetworkKit.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@

#import "Categories/NSString+MKNetworkKitAdditions.h"
#import "Categories/NSDictionary+RequestEncoding.h"
#import "Categories/NSArray+RequestEncoding.h"
#import "Categories/NSDate+RFC1123.h"
#import "Categories/NSData+MKBase64.h"
#import "Categories/UIImageView+MKNetworkKitAdditions.h"
Expand All @@ -87,6 +88,8 @@
#define kMKNetworkKitDefaultImageHeadRequestDuration 3600*24*1 // 1 day (HEAD requests with eTag are sent only after expiry of this. Not that these are not RFC compliant, but needed for performance tuning)
#define kMKNetworkKitDefaultImageCacheDuration 3600*24*7 // 1 day

#define MKNETWORKCACHE_DEFAULT_DISK_SIZE 50*1024*1024 //50 MB

// if your server takes longer than 30 seconds to provide real data,
// you should hire a better server developer.
// on iOS (or any mobile device), 30 seconds is already considered high.
Expand Down