Skip to content
This repository has been archived by the owner on Jan 17, 2023. It is now read-only.

Added AFFetchSaveManager class to observe notifications and fire appropriate blocks #94

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 49 additions & 0 deletions AFIncrementalStore/AFFetchSaveManager.h
@@ -0,0 +1,49 @@
//
// AFFetchSaveManager.h
// Sharely
//
// Created by Adam Price on 10/26/12.
// Copyright (c) 2012 Fuzz Productions. All rights reserved.
//

@class AFHTTPRequestOperation;

typedef void (^AFIncrementalStoreFetchCompletionBlock)(NSFetchRequest *fetchRequest, AFHTTPRequestOperation *operation, NSArray *fetchedObjects);
typedef void (^AFIncrementalStoreSaveCompletionBlock)(NSSaveChangesRequest *saveChangesRequest, AFHTTPRequestOperation *operation, NSArray *insertedObjects, NSArray *updatedObjects, NSArray *deletedObjectIDs);

@interface AFFetchSaveManager : NSObject

/**
`AFFetchSaveManager` is a class designed to manage executing NSFetchRequests with completion blocks and NSManagedObjectContext saves with completion blocks. There is no need to create an instance of this class; the class itself is the observer for notifications and the only public methods are class methods.

## Description

There are only two class methods with which to interface with AFFetchSaveManager. An exception will be raised if you call these methods without a context.

The completion and failure blocks will be associated with the corresponding NSFetchRequest or NSSaveChangesRequest that are executed by AFIncrementalStore.
AFFetchSaveManager acts as a global observer of AFIncrementalStore's NSNotifications, and will execute the appropriate completion block when it gets the corresponding
didFetchRemoteValues: or didSaveRemoteValues: notification.
*/

+ (BOOL)saveContext:(NSManagedObjectContext *)context
error:(NSError *__autoreleasing*)error
completion:(AFIncrementalStoreSaveCompletionBlock)completionBlock;

+ (NSArray *)executeFetchRequest:(NSFetchRequest *)fetchRequest
context:(NSManagedObjectContext *)context
error:(NSError *__autoreleasing*)error
completion:(AFIncrementalStoreFetchCompletionBlock)completionBlock;

///-----------------------------------------
/// @name ManagedObjectContext userInfo keys
///-----------------------------------------

/**
A key in the `userInfo` dictionary of a NSManagedObjectContext, set by AFFetchSaveManager.
The corresponding value is a unique `requestIdentifier` key that is generated in each of AFFetchSaveManager's class methods and used as a key for the pending completion block
of that NSPersistentStoreRequest. This key is immediately removed from the `userInfo` dictionary and attached to the appropriate NSFetchRequest or NSSaveChangesRequest in the
executeRequest:withContext:error method of AFIncrementalStore.
*/
extern NSString * const AFFetchSaveManagerPersistentStoreRequestIdentifierKey;

@end
163 changes: 163 additions & 0 deletions AFIncrementalStore/AFFetchSaveManager.m
@@ -0,0 +1,163 @@
//
// AFFetchSaveManager.m
// Sharely
//
// Created by Adam Price on 10/26/12.
// Copyright (c) 2012 Fuzz Productions. All rights reserved.
//

#import <CoreData/CoreData.h>
#import "AFFetchSaveManager.h"
#import "AFIncrementalStore.h"

NSString * const AFFetchSaveManagerPersistentStoreRequestIdentifierKey = @"AFFetchSaveManagerPersistentStoreRequestIdentifierKey";

static NSMutableDictionary *_fetchRequestBlockDictionary = nil;
static NSMutableDictionary *_saveRequestBlockDictionary = nil;

@implementation AFFetchSaveManager

+ (void)setupObserver
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^
{
[[NSNotificationCenter defaultCenter] addObserver:[self class] selector:@selector(willFetchRemoteValues:) name:AFIncrementalStoreContextWillFetchRemoteValues object:nil];
[[NSNotificationCenter defaultCenter] addObserver:[self class] selector:@selector(didFetchRemoteValues:) name:AFIncrementalStoreContextDidFetchRemoteValues object:nil];
[[NSNotificationCenter defaultCenter] addObserver:[self class] selector:@selector(willSaveRemoteValues:) name:AFIncrementalStoreContextWillSaveRemoteValues object:nil];
[[NSNotificationCenter defaultCenter] addObserver:[self class] selector:@selector(didSaveRemoteValues:) name:AFIncrementalStoreContextDidSaveRemoteValues object:nil];
});
}

#pragma mark -
#pragma mark NSManagedObjectContext public class methods

+ (NSArray *)executeFetchRequest:(NSFetchRequest *)fetchRequest
context:(NSManagedObjectContext *)context
error:(NSError *__autoreleasing*)error
completion:(AFIncrementalStoreFetchCompletionBlock)completionBlock
{
NSParameterAssert(context);

[[self class] setupObserver];

if (completionBlock) {
NSString *requestIdentifier = [[NSProcessInfo processInfo] globallyUniqueString];

[context.userInfo setObject:requestIdentifier forKey:AFFetchSaveManagerPersistentStoreRequestIdentifierKey];

[[[self class] fetchRequestBlockDictionary] setObject:completionBlock forKey:requestIdentifier];
}

return [context executeFetchRequest:fetchRequest error:error];
}

+ (BOOL)saveContext:(NSManagedObjectContext *)context
error:(NSError *__autoreleasing*)error
completion:(AFIncrementalStoreSaveCompletionBlock)completionBlock
{
NSParameterAssert(context);

[[self class] setupObserver];

if (completionBlock) {
NSString *requestIdentifier = [[NSProcessInfo processInfo] globallyUniqueString];

[context.userInfo setObject:requestIdentifier forKey:AFFetchSaveManagerPersistentStoreRequestIdentifierKey];

[[[self class] saveRequestBlockDictionary] setObject:completionBlock forKey:requestIdentifier];
}

return [context save:error];
}

#pragma mark -
#pragma mark Private Getter Methods

+ (NSMutableDictionary *)fetchRequestBlockDictionary
{
if (!_fetchRequestBlockDictionary)
_fetchRequestBlockDictionary = [[NSMutableDictionary alloc] init];
return _fetchRequestBlockDictionary;
}

+ (NSMutableDictionary *)saveRequestBlockDictionary
{
if (!_saveRequestBlockDictionary)
_saveRequestBlockDictionary = [[NSMutableDictionary alloc] init];
return _saveRequestBlockDictionary;
}

#pragma mark -
#pragma mark NSNotifications

+ (void)willFetchRemoteValues:(NSNotification *)inNotification
{

}

+ (void)didFetchRemoteValues:(NSNotification *)inNotification
{
[[self class] executeCompletionBlockWithNotification:inNotification];
}

+ (void)willSaveRemoteValues:(NSNotification *)inNotification
{

}

+ (void)didSaveRemoteValues:(NSNotification *)inNotification
{
[[self class] executeCompletionBlockWithNotification:inNotification];
}

#pragma mark -
#pragma mark Private block handler functions

+ (void)executeCompletionBlockWithNotification:(NSNotification *)inNotification
{
BOOL couldNotFindValidCompletionBlock = NO;

NSPersistentStoreRequest *tmpPersistentStoreRequest = [inNotification.userInfo objectForKey:AFIncrementalStorePersistentStoreRequestKey];
AFHTTPRequestOperation *tmpOperation = [inNotification.userInfo objectForKey:AFIncrementalStoreRequestOperationKey];
if (tmpPersistentStoreRequest && tmpOperation)
{
NSString *requestIdentifier = [tmpPersistentStoreRequest af_requestIdentifier];
if (!requestIdentifier || requestIdentifier.length == 0)
return;

if (tmpPersistentStoreRequest.requestType == NSFetchRequestType)
{
AFIncrementalStoreFetchCompletionBlock tmpFetchCompletionBlock = [[[self class] fetchRequestBlockDictionary] objectForKey:requestIdentifier];
if (!tmpFetchCompletionBlock)
couldNotFindValidCompletionBlock = YES;
else
{
NSArray *tmpFetchedObjects = [inNotification.userInfo objectForKey:AFIncrementalStoreFetchedObjectsKey];
if (tmpFetchCompletionBlock)
tmpFetchCompletionBlock((NSFetchRequest *)tmpPersistentStoreRequest, tmpOperation, tmpFetchedObjects);
}
}
else
{
AFIncrementalStoreSaveCompletionBlock tmpSaveCompletionBlock = [[[self class] saveRequestBlockDictionary] objectForKey:requestIdentifier];
if (!tmpSaveCompletionBlock)
couldNotFindValidCompletionBlock = YES;
else
{
NSArray *tmpInsertedObjects = [inNotification.userInfo objectForKey:AFIncrementalStoreInsertedObjectsKey];
NSArray *tmpUpdatedObjects = [inNotification.userInfo objectForKey:AFIncrementalStoreUpdatedObjectsKey];
NSArray *tmpDeletedObjects = [inNotification.userInfo objectForKey:AFIncrementalStoreDeletedObjectIDsKey];
if (tmpSaveCompletionBlock)
tmpSaveCompletionBlock((NSSaveChangesRequest *)tmpPersistentStoreRequest, tmpOperation, tmpInsertedObjects, tmpUpdatedObjects, tmpDeletedObjects);
}
}
}
else
couldNotFindValidCompletionBlock = YES;

if (couldNotFindValidCompletionBlock)
NSLog(@"Could Not Find Valid Completion Block");
}

@end
57 changes: 57 additions & 0 deletions AFIncrementalStore/AFIncrementalStore.h
Expand Up @@ -23,6 +23,34 @@
#import <CoreData/CoreData.h>
#import "AFNetworking.h"

#import <objc/runtime.h>

@interface NSPersistentStoreRequest (_AFIncrementalStore)

/**
A property set by AFIncrementalStore to uniquely identify a NSFetchRequest or NSSaveChanges request, in order for the AFFetchSaveManager class to correctly identify it via NSNotification, and subsequently fire the appropriate completion block for that request.

@discussion If there is a completion block associated with the NSPersistentStoreRequest, AFFetchSaveManager will have generated this unique identifier upon first dispatch of the request and conveys it to the AFIncrementalStore by attaching it to the `userInfo` dictionary of the NSManagedObjectContext.
*/
@property (readwrite, nonatomic, copy, setter = af_setRequestIdentifier:) NSString *af_requestIdentifier;

@end

static char kAFRequestIdentifierObjectKey;

@implementation NSPersistentStoreRequest (_AFIncrementalStore)
@dynamic af_requestIdentifier;

- (NSString *)af_requestIdentifier {
return (NSString *)objc_getAssociatedObject(self, &kAFRequestIdentifierObjectKey);
}

- (void)af_setRequestIdentifier:(NSString *)requestIdentifier {
objc_setAssociatedObject(self, &kAFRequestIdentifierObjectKey, requestIdentifier, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

@end

@protocol AFIncrementalStoreDelegate;
@protocol AFIncrementalStoreHTTPClient;

Expand Down Expand Up @@ -317,4 +345,33 @@ extern NSString * const AFIncrementalStoreRequestOperationKey;
/**
A key in the `userInfo` dictionary in a `AFIncrementalStoreContextWillFetchRemoteValues` or `AFIncrementalStoreContextDidFetchRemoteValues` notification.
The corresponding value is an `NSPersistentStoreRequest` object representing the associated fetch or save request. */

extern NSString * const AFIncrementalStorePersistentStoreRequestKey;

/**
A key in the `userInfo` dictionary in a `AFIncrementalStoreContextDidFetchRemoteValues` notification.
The corresponding value is an `NSArray` object containing the fetched objects, in the context in which they were requested.
*/

extern NSString * const AFIncrementalStoreFetchedObjectsKey;

/**
A key in the `userInfo` dictionary in a `AFIncrementalStoreContextDidSaveRemoteValues` notification.
The corresponding value is an `NSArray` object containing the inserted objects, in the context in which they were requested.
*/

extern NSString * const AFIncrementalStoreInsertedObjectsKey;

/**
A key in the `userInfo` dictionary in a `AFIncrementalStoreContextDidSaveRemoteValues` notification.
The corresponding value is an `NSArray` object containing the updated objects, in the context in which they were requested.
*/

extern NSString * const AFIncrementalStoreUpdatedObjectsKey;

/**
A key in the `userInfo` dictionary in a `AFIncrementalStoreContextWillSaveRemoteValues` or `AFIncrementalStoreContextDidFetchRemoteValues` notification.
The corresponding value is a NSArray containing the permament object ID's associated with the deleted objects from the originating managed object context. The object ID's are included in the NSNotification because unlike the NSManagedObjects themselves, the IDs are thread-safe.
*/

extern NSString * const AFIncrementalStoreDeletedObjectIDsKey;