Skip to content

Commit

Permalink
Added loading object without storing in memory
Browse files Browse the repository at this point in the history
  • Loading branch information
belkevich committed Apr 2, 2014
1 parent 8b36ffe commit a8da013
Show file tree
Hide file tree
Showing 13 changed files with 155 additions and 42 deletions.
4 changes: 2 additions & 2 deletions Classes/Private/Managers/APTaskManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@

@interface APTaskManager : NSObject

- (void)addTaskWithURL:(NSURL *)url block:(APTaskCallbackBlock)block
shouldStart:(BOOL *)shouldStart;
- (APStorageTask *)addTaskWithURL:(NSURL *)url storeInMemory:(BOOL)storeInMemory
callbackBlock:(APTaskCallbackBlock)callbackBlock;
- (void)finishTaskWithURL:(NSURL *)url object:(id)object error:(NSError *)error;
- (void)cancelTaskWithURL:(NSURL *)url;
- (void)cancelAllTasks;
Expand Down
17 changes: 9 additions & 8 deletions Classes/Private/Managers/APTaskManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,26 @@ - (id)init

#pragma mark - public

- (void)addTaskWithURL:(NSURL *)url block:(APTaskCallbackBlock)block shouldStart:(BOOL *)shouldStart
- (APStorageTask *)addTaskWithURL:(NSURL *)url storeInMemory:(BOOL)storeInMemory
callbackBlock:(APTaskCallbackBlock)callbackBlock
{
APStorageTask *task;
NSString *key = url.absoluteString;
if (key)
{
BOOL isShouldStartTask = NO;
APStorageTask *task = [_dictionary objectForKeySynchronously:key];
task = [_dictionary objectForKeySynchronously:key];
if (!task)
{
task = [[APStorageTask alloc] init];
task = [[APStorageTask alloc] initWithTaskURL:url];
[_dictionary setObject:task forKey:key];
isShouldStartTask = YES;
}
[task addCallbackBlock:block thread:NSThread.currentThread];
if (shouldStart)
if (storeInMemory)
{
*shouldStart = isShouldStartTask;
task.storeInMemory = storeInMemory;
}
[task addCallbackBlock:callbackBlock thread:NSThread.currentThread];
}
return task;
}

- (void)finishTaskWithURL:(NSURL *)url object:(id)object error:(NSError *)error
Expand Down
5 changes: 5 additions & 0 deletions Classes/Private/Models/APStorageTask.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ typedef void (^APTaskCallbackBlock)(id object, NSError *error);

@interface APStorageTask : NSObject

@property (nonatomic, readonly) NSURL *url;
@property (nonatomic, readonly) BOOL isShouldRun;
@property (atomic, assign) BOOL storeInMemory;

- (id)initWithTaskURL:(NSURL *)url;
- (void)addCallbackBlock:(APTaskCallbackBlock)block thread:(NSThread *)thread;
- (void)performCallbackWithObject:(id)object error:(NSError *)error;

Expand Down
16 changes: 15 additions & 1 deletion Classes/Private/Models/APStorageTask.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,24 @@
#import "NSThread+Block.h"

@interface APStorageTask ()
@property (nonatomic, copy) APTaskCallbackBlock callbackBlock;
@property (atomic, copy) APTaskCallbackBlock callbackBlock;
@end

@implementation APStorageTask

#pragma mark - life cycle

- (id)initWithTaskURL:(NSURL *)url
{
self = [super init];
if (self)
{
_url = url;
_isShouldRun = YES;
}
return self;
}

#pragma mark - public

- (void)addCallbackBlock:(APTaskCallbackBlock)block thread:(NSThread *)thread
Expand All @@ -36,6 +49,7 @@ - (void)addCallbackBlock:(APTaskCallbackBlock)block thread:(NSThread *)thread
previousBlock(object, error);
threadBlock(object, error);
};
_isShouldRun = NO;
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions Classes/Public/APSmartStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ typedef id (^APParsingBlock)(NSData *data, NSURL *url);

+ (instancetype)sharedInstance;
- (void)loadObjectWithURL:(NSURL *)url callback:(void (^)(id object, NSError *))callback;
- (void)loadObjectWithURL:(NSURL *)url storeInMemory:(BOOL)storeInMemory
callback:(void (^)(id object, NSError *))callback;
- (void)reloadObjectWithURL:(NSURL *)url callback:(void (^)(id object, NSError *))callback;
- (void)reloadObjectWithURL:(NSURL *)url storeInMemory:(BOOL)storeInMemory
callback:(void (^)(id object, NSError *))callback;
- (void)removeObjectWithURLFromMemory:(NSURL *)url;
- (void)removeObjectWithURLFromStorage:(NSURL *)url;
- (void)removeAllFromMemory;
Expand Down
54 changes: 36 additions & 18 deletions Classes/Public/APSmartStorage.m
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,18 @@ + (instancetype)sharedInstance

- (void)loadObjectWithURL:(NSURL *)url callback:(void (^)(id object, NSError *))callback
{
BOOL isShouldStart = NO;
[self.taskManager addTaskWithURL:url block:callback shouldStart:&isShouldStart];
if (isShouldStart)
[self loadObjectWithURL:url storeInMemory:YES callback:callback];
}

- (void)loadObjectWithURL:(NSURL *)url storeInMemory:(BOOL)storeInMemory
callback:(void (^)(id object, NSError *))callback
{
APStorageTask *task = [self.taskManager addTaskWithURL:url storeInMemory:storeInMemory
callbackBlock:callback];
if (task.isShouldRun)
{
__weak __typeof(self) weakSelf = self;
[self objectFromStorageWithURL:url callback:^(id object, NSError *error)
[self storageObjectForTask:task callback:^(id object, NSError *error)
{
[weakSelf.taskManager finishTaskWithURL:url object:object error:error];
}];
Expand All @@ -79,8 +85,14 @@ - (void)loadObjectWithURL:(NSURL *)url callback:(void (^)(id object, NSError *))

- (void)reloadObjectWithURL:(NSURL *)url callback:(void (^)(id object, NSError *))callback
{
[self.networkStorage cancelDownloadURL:url];
[self objectFromNetworkWithURL:url callback:callback];
[self reloadObjectWithURL:url storeInMemory:NO callback:callback];
}

- (void)reloadObjectWithURL:(NSURL *)url storeInMemory:(BOOL)storeInMemory
callback:(void (^)(id object, NSError *))callback
{
[self removeObjectWithURLFromStorage:url];
[self loadObjectWithURL:url storeInMemory:storeInMemory callback:callback];
}

- (void)removeObjectWithURLFromMemory:(NSURL *)url
Expand Down Expand Up @@ -162,52 +174,58 @@ - (APNetworkStorage *)networkStorage

#pragma mark - private methods

- (void)objectFromStorageWithURL:(NSURL *)url callback:(void (^)(id object, NSError *error))callback
- (void)storageObjectForTask:(APStorageTask *)task callback:(void (^)(id object, NSError *error))callback
{
__weak __typeof(self) weakSelf = self;
[self objectFromMemoryWithURL:url callback:^(id object)
[self memoryObjectForTask:task callback:^(id object)
{
object ? callback(object, nil) : [weakSelf objectFromFileWithURL:url callback:callback];
object ? callback(object, nil) : [weakSelf fileObjectForTask:task callback:callback];
}];
}

- (void)objectFromMemoryWithURL:(NSURL *)objectURL callback:(void (^)(id object))callback
- (void)memoryObjectForTask:(APStorageTask *)task callback:(void (^)(id object))callback
{
callback([self.memoryStorage objectWithURL:objectURL]);
callback([self.memoryStorage objectWithURL:task.url]);
}

- (void)objectFromFileWithURL:(NSURL *)url callback:(void (^)(id object, NSError *error))callback
- (void)fileObjectForTask:(APStorageTask *)task callback:(void (^)(id object, NSError *error))callback
{
NSURL *url = task.url;
BOOL shouldStoreInMemory = task.storeInMemory;
__weak __typeof(self) weakSelf = self;
[self.fileStorage dataWithURL:url callback:^(NSData *data)
{
// performed in background thread!
if (data)
{
id result = weakSelf.parsingBlock ? weakSelf.parsingBlock(data, url) : data;
[weakSelf.memoryStorage setObject:result forURL:url];
if (shouldStoreInMemory)
{
[weakSelf.memoryStorage setObject:result forURL:url];
}
callback(result, nil);
}
// if no data then load from network
else
{
[weakSelf objectFromNetworkWithURL:url callback:callback];
[weakSelf networkObjectForTask:task callback:callback];
}
}];
}

- (void)objectFromNetworkWithURL:(NSURL *)url callback:(void (^)(id object, NSError *error))callback
- (void)networkObjectForTask:(APStorageTask *)task callback:(void (^)(id object, NSError *error))callback
{
NSURL *url = task.url;
__weak __typeof(self) weakSelf = self;
[self.networkStorage downloadURL:url callback:^(NSString *path, NSError *networkError)
{
// performed in background thread!
NSError *fileError = nil;
// file has been downloaded and moved
if (path && !networkError && [weakSelf.fileStorage moveDataWithURL:url downloadedToPath:path
error:&fileError])
if (path && !networkError &&
[weakSelf.fileStorage moveDataWithURL:url downloadedToPath:path error:&fileError])
{
[weakSelf objectFromFileWithURL:url callback:callback];
[weakSelf fileObjectForTask:task callback:callback];
}
else
{
Expand Down
6 changes: 6 additions & 0 deletions Spec/APSmartStorageSpec.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

/* Begin PBXBuildFile section */
F6841D59D49E4FD4AFFA475C /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DDE27DF55FBD4DDB9FFA4B1C /* libPods.a */; };
FA00E22118EC2DFB00723260 /* APSmartStorage+Memory.m in Sources */ = {isa = PBXBuildFile; fileRef = FA00E22018EC2DFB00723260 /* APSmartStorage+Memory.m */; };
FA68F29A188FC56E007CCE3E /* APSmartStorageBaseSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA68F299188FC56E007CCE3E /* APSmartStorageBaseSpec.mm */; };
FA68F29C188FC57E007CCE3E /* APSmartStorageFileSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA68F29B188FC57E007CCE3E /* APSmartStorageFileSpec.mm */; };
FA68F29E188FC58D007CCE3E /* APSmartStorageRemoveSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA68F29D188FC58D007CCE3E /* APSmartStorageRemoveSpec.mm */; };
Expand All @@ -34,6 +35,8 @@
/* Begin PBXFileReference section */
3623D18215C44D7FA210E476 /* Pods.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.xcconfig; path = Pods/Pods.xcconfig; sourceTree = "<group>"; };
DDE27DF55FBD4DDB9FFA4B1C /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
FA00E21F18EC2DFB00723260 /* APSmartStorage+Memory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "APSmartStorage+Memory.h"; sourceTree = "<group>"; };
FA00E22018EC2DFB00723260 /* APSmartStorage+Memory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "APSmartStorage+Memory.m"; sourceTree = "<group>"; };
FA68F299188FC56E007CCE3E /* APSmartStorageBaseSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = APSmartStorageBaseSpec.mm; sourceTree = "<group>"; };
FA68F29B188FC57E007CCE3E /* APSmartStorageFileSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = APSmartStorageFileSpec.mm; sourceTree = "<group>"; };
FA68F29D188FC58D007CCE3E /* APSmartStorageRemoveSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = APSmartStorageRemoveSpec.mm; sourceTree = "<group>"; };
Expand Down Expand Up @@ -91,6 +94,8 @@
children = (
FA68F2A3188FE84E007CCE3E /* OHHTTPStubs+AllRequests.h */,
FA68F2A4188FE84E007CCE3E /* OHHTTPStubs+AllRequests.m */,
FA00E21F18EC2DFB00723260 /* APSmartStorage+Memory.h */,
FA00E22018EC2DFB00723260 /* APSmartStorage+Memory.m */,
);
name = Categories;
sourceTree = "<group>";
Expand Down Expand Up @@ -357,6 +362,7 @@
buildActionMask = 2147483647;
files = (
FA68F29C188FC57E007CCE3E /* APSmartStorageFileSpec.mm in Sources */,
FA00E22118EC2DFB00723260 /* APSmartStorage+Memory.m in Sources */,
FA6E6FC21887B6C300E8DF46 /* main.m in Sources */,
FAD37B88189110F000825EB6 /* NSFileManager+Storage.m in Sources */,
FA68F2A5188FE84E007CCE3E /* OHHTTPStubs+AllRequests.m in Sources */,
Expand Down
15 changes: 15 additions & 0 deletions Spec/APSmartStorageSpec/APSmartStorage+Memory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// APSmartStorage+Memory.h
// APSmartStorageSpec
//
// Created by Alexey Belkevich on 4/2/14.
// Copyright (c) 2014 alterplay. All rights reserved.
//

#import "APSmartStorage.h"

@interface APSmartStorage (Memory)

- (void)objectFromMemoryWithURL:(NSURL *)objectURL callback:(void (^)(id object))callback;

@end
24 changes: 24 additions & 0 deletions Spec/APSmartStorageSpec/APSmartStorage+Memory.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// APSmartStorage+Memory.m
// APSmartStorageSpec
//
// Created by Alexey Belkevich on 4/2/14.
// Copyright (c) 2014 alterplay. All rights reserved.
//

#import "APSmartStorage+Memory.h"
#import "APStorageTask.h"

@interface APSmartStorage (Private)
- (void)memoryObjectForTask:(APStorageTask *)task callback:(void (^)(id object))callback;
@end

@implementation APSmartStorage (Memory)

- (void)objectFromMemoryWithURL:(NSURL *)objectURL callback:(void (^)(id object))callback
{
APStorageTask *task = [[APStorageTask alloc] initWithTaskURL:objectURL];
[self memoryObjectForTask:task callback:callback];
}

@end
41 changes: 36 additions & 5 deletions Spec/APSmartStorageSpec/APSmartStorageBaseSpec.mm
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,12 @@
//

#import "CedarAsync.h"
#import "APSmartStorage.h"
#import "APSmartStorage+Memory.h"
#import "OHHTTPStubs+AllRequests.h"

using namespace Cedar::Matchers;
using namespace Cedar::Doubles;

@interface APSmartStorage (Private)
- (void)objectFromMemoryWithURL:(NSURL *)objectURL callback:(void (^)(id object))callback;
@end

SPEC_BEGIN(APSmartStorageBaseSpec)

describe(@"APSmartStorage", ^
Expand Down Expand Up @@ -110,6 +106,41 @@ - (void)objectFromMemoryWithURL:(NSURL *)objectURL callback:(void (^)(id object)
in_time(checkObject) should equal(responseObject);
});

it(@"should load object from network write it to file and don't store it in memory", ^
{
__block id loadedObject = [[NSObject alloc] init];
__block id memoryObject = [[NSObject alloc] init];
__block BOOL fileExists = NO;
[storage loadObjectWithURL:objectURL storeInMemory:NO callback:^(id object, NSError *error)
{
loadedObject = object;
fileExists = [NSFileManager.defaultManager fileExistsAtPath:filePath];
// loading object from memory
[storage objectFromMemoryWithURL:objectURL callback:^(id obj)
{
memoryObject = obj;
}];
}];
in_time(fileExists) should equal(YES);
in_time(memoryObject) should be_nil;
in_time(loadedObject) should equal(responseObject);
});

it(@"should load object and store it in memory if at least one call needs to store", ^
{
__block id memoryObject = [[NSObject alloc] init];
[storage loadObjectWithURL:objectURL storeInMemory:NO callback:^(id object, NSError *error)
{
// loading object from memory
[storage objectFromMemoryWithURL:objectURL callback:^(id obj)
{
memoryObject = obj;
}];
}];
[storage loadObjectWithURL:objectURL storeInMemory:YES callback:nil];
in_time(memoryObject) should equal(responseObject);
});

it(@"should parse loaded object with block", ^
{
// parsing block
Expand Down
6 changes: 1 addition & 5 deletions Spec/APSmartStorageSpec/APSmartStorageFileSpec.mm
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,12 @@
//

#import "CedarAsync.h"
#import "APSmartStorage.h"
#import "APSmartStorage+Memory.h"
#import "OHHTTPStubs+AllRequests.h"

using namespace Cedar::Matchers;
using namespace Cedar::Doubles;

@interface APSmartStorage (Private)
- (void)objectFromMemoryWithURL:(NSURL *)objectURL callback:(void (^)(id object))callback;
@end

SPEC_BEGIN(APSmartStorageFileSpec)

describe(@"APSmartStorage", ^
Expand Down
3 changes: 1 addition & 2 deletions Spec/APSmartStorageSpec/APSmartStorageRemoveSpec.mm
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@
//

#import "CedarAsync.h"
#import "APSmartStorage.h"
#import "APSmartStorage+Memory.h"
#import "OHHTTPStubs+AllRequests.h"

using namespace Cedar::Matchers;
using namespace Cedar::Doubles;

@interface APSmartStorage (Private)
- (void)objectFromMemoryWithURL:(NSURL *)objectURL callback:(void (^)(id object))callback;
- (void)didReceiveMemoryWarning:(NSNotification *)notification;
@end

Expand Down
2 changes: 1 addition & 1 deletion Spec/APSmartStorageSpec/APSmartStorageTaskSpec.mm
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
[OHHTTPStubs removeAllStubs];
});

it(@"should call 2 callbacks on first load", ^
it(@"should call 2 callbacks if URL download executed 2 times before complete", ^
{
// loading object and check file and memory
__block id checkObject1 = [[NSObject alloc] init];
Expand Down

0 comments on commit a8da013

Please sign in to comment.