Skip to content

Instantly share code, notes, and snippets.

@ajayjapan
Created August 27, 2012 17:59
Show Gist options
  • Save ajayjapan/3490871 to your computer and use it in GitHub Desktop.
Save ajayjapan/3490871 to your computer and use it in GitHub Desktop.
// AFIncrementalStore.m
//
// Copyright (c) 2012 Mattt Thompson (http://mattt.me)
//
// 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 "AFIncrementalStore.h"
#import "AFHTTPClient.h"
NSString * AFIncrementalStoreUnimplementedMethodException = @"com.alamofire.incremental-store.exceptions.unimplemented-method";
static NSString * const kAFIncrementalStoreResourceIdentifierAttributeName = @"__af_resourceIdentifier";
@interface AFIncrementalStore ()
- (NSManagedObjectContext *)backingManagedObjectContext;
- (NSManagedObjectID *)objectIDForEntity:(NSEntityDescription *)entity
withResourceIdentifier:(NSString *)resourceIdentifier;
- (NSManagedObjectID *)objectIDForBackingObjectForEntity:(NSEntityDescription *)entity
withResourceIdentifier:(NSString *)resourceIdentifier;
@end
@implementation AFIncrementalStore {
@private
NSCache *_propertyValuesCache;
NSCache *_relationshipsCache;
NSCache *_backingObjectIDByObjectID;
NSMutableDictionary *_registeredObjectIDsByResourceIdentifier;
NSPersistentStoreCoordinator *_backingPersistentStoreCoordinator;
NSManagedObjectContext *_backingManagedObjectContext;
}
@synthesize HTTPClient = _HTTPClient;
@synthesize backingPersistentStoreCoordinator = _backingPersistentStoreCoordinator;
+ (NSString *)type {
@throw([NSException exceptionWithName:AFIncrementalStoreUnimplementedMethodException reason:NSLocalizedString(@"Unimplemented method: +type. Must be overridden in a subclass", nil) userInfo:nil]);
}
+ (NSManagedObjectModel *)model {
@throw([NSException exceptionWithName:AFIncrementalStoreUnimplementedMethodException reason:NSLocalizedString(@"Unimplemented method: +model. Must be overridden in a subclass", nil) userInfo:nil]);
}
- (BOOL)loadMetadata:(NSError *__autoreleasing *)error {
if (!_propertyValuesCache) {
NSMutableDictionary *mutableMetadata = [NSMutableDictionary dictionary];
[mutableMetadata setValue:[[NSProcessInfo processInfo] globallyUniqueString] forKey:NSStoreUUIDKey];
[mutableMetadata setValue:NSStringFromClass([self class]) forKey:NSStoreTypeKey];
[self setMetadata:mutableMetadata];
_propertyValuesCache = [[NSCache alloc] init];
_relationshipsCache = [[NSCache alloc] init];
_backingObjectIDByObjectID = [[NSCache alloc] init];
_registeredObjectIDsByResourceIdentifier = [[NSMutableDictionary alloc] init];
NSManagedObjectModel *model = [self.persistentStoreCoordinator.managedObjectModel copy];
for (NSEntityDescription *entity in model.entities) {
NSAttributeDescription *resourceIdentifierProperty = [[NSAttributeDescription alloc] init];
[resourceIdentifierProperty setName:kAFIncrementalStoreResourceIdentifierAttributeName];
[resourceIdentifierProperty setAttributeType:NSStringAttributeType];
[resourceIdentifierProperty setIndexed:YES];
[entity setProperties:[entity.properties arrayByAddingObject:resourceIdentifierProperty]];
}
_backingPersistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
return YES;
} else {
return NO;
}
}
- (NSManagedObjectContext *)backingManagedObjectContext {
if (!_backingManagedObjectContext) {
_backingManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
_backingManagedObjectContext.persistentStoreCoordinator = _backingPersistentStoreCoordinator;
_backingManagedObjectContext.retainsRegisteredObjects = YES;
}
return _backingManagedObjectContext;
}
- (NSManagedObjectID *)objectIDForEntity:(NSEntityDescription *)entity
withResourceIdentifier:(NSString *)resourceIdentifier {
NSManagedObjectID *objectID = [_registeredObjectIDsByResourceIdentifier objectForKey:resourceIdentifier];
if (objectID == nil) {
objectID = [self newObjectIDForEntity:entity referenceObject:resourceIdentifier];
}
return objectID;
}
- (NSManagedObjectID *)objectIDForBackingObjectForEntity:(NSEntityDescription *)entity
withResourceIdentifier:(NSString *)resourceIdentifier
{
if (!resourceIdentifier) {
return nil;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:[entity name]];
fetchRequest.resultType = NSManagedObjectIDResultType;
fetchRequest.fetchLimit = 1;
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"%K = %@", kAFIncrementalStoreResourceIdentifierAttributeName, resourceIdentifier];
NSError *error = nil;
NSArray *results = [[self backingManagedObjectContext] executeFetchRequest:fetchRequest error:&error];
if (error) {
NSLog(@"Error: %@", error);
return nil;
}
return [results lastObject];
}
- (NSManagedObject *)findOrCreateBackingObjectWithResourceIdentifier:(NSString *)resourceIdentifier
ofEntity:(NSEntityDescription *)entity
attributes:(NSDictionary *)attributes
backingContext:(NSManagedObjectContext *)backingContext
childContext:(NSManagedObjectContext *)childContext
objectID:(NSManagedObjectID *)objectID
{
NSManagedObject *backingObject = (objectID != nil) ? [backingContext existingObjectWithID:objectID error:nil] : [NSEntityDescription insertNewObjectForEntityForName:entity.name inManagedObjectContext:backingContext];
[backingObject setValue:resourceIdentifier forKey:kAFIncrementalStoreResourceIdentifierAttributeName];
[backingObject setValuesForKeysWithDictionary:attributes];
return backingObject;
}
- (NSManagedObject *)findAndUpdateObjectWithResourceIdentifier:(NSString *)resourceIdentifier
ofEntity:(NSEntityDescription *)entity
attributes:(NSDictionary *)attributes
backingContext:(NSManagedObjectContext *)backingContext
childContext:(NSManagedObjectContext *)childContext
objectID:(NSManagedObjectID *)objectID
{
NSManagedObject *managedObject = [childContext existingObjectWithID:[self objectIDForEntity:entity withResourceIdentifier:resourceIdentifier] error:nil];
[managedObject setValuesForKeysWithDictionary:attributes];
if (objectID == nil) {
[childContext insertObject:managedObject];
}
return managedObject;
}
- (void)reusableAttributesForRepresentation:(NSDictionary *)representation
ofEntity:(NSEntityDescription *)entity
fromResponse:(NSHTTPURLResponse *)response
success:(void (^)(NSString *resourceIdentifier, NSDictionary *attributes, NSDictionary *relationshipRepresentations))success {
NSString *resourceIdentifier = [self.HTTPClient resourceIdentifierForRepresentation:representation ofEntity:entity fromResponse:response];
NSDictionary *attributes = [self.HTTPClient attributesForRepresentation:representation ofEntity:entity fromResponse:response];
NSDictionary *relationshipRepresentations = [self.HTTPClient representationsForRelationshipsFromRepresentation:representation ofEntity:entity fromResponse:response];
success(resourceIdentifier, attributes, relationshipRepresentations);
}
- (void)relationshipFunction:(NSDictionary *)relationshipRepresentations
ofEntity:(NSEntityDescription *)entity
fromResponse:(NSHTTPURLResponse *)response
backingContext:(NSManagedObjectContext *)backingContext
childContext:(NSManagedObjectContext *)childContext
backingObject:(NSManagedObject *)backingObject
managedObject:(NSManagedObject *)managedObject
{
for (NSString *relationshipName in relationshipRepresentations) {
id relationshipRepresentationOrArrayOfRepresentations = [relationshipRepresentations objectForKey:relationshipName];
NSRelationshipDescription *relationship = [[entity relationshipsByName] valueForKey:relationshipName];
if (relationship) {
if ([relationship isToMany]) {
id mutableManagedRelationshipObjects = [relationship isOrdered] ? [NSMutableOrderedSet orderedSet] : [NSMutableSet set];
id mutableBackingRelationshipObjects = [relationship isOrdered] ? [NSMutableOrderedSet orderedSet] : [NSMutableSet set];
for (NSDictionary *relationshipRepresentation in relationshipRepresentationOrArrayOfRepresentations) {
[self reusableAttributesForRepresentation:relationshipRepresentation ofEntity:relationship.destinationEntity fromResponse:response success:^(NSString *theResourceIdentifier, NSDictionary *theAttributes, NSDictionary *relationshipRepresentations) {
NSManagedObjectID *relationshipObjectID = [self objectIDForBackingObjectForEntity:relationship.destinationEntity withResourceIdentifier:theResourceIdentifier];
NSManagedObject *backingRelationshipObject = [self findOrCreateBackingObjectWithResourceIdentifier:theResourceIdentifier ofEntity:relationship.destinationEntity attributes:theAttributes backingContext:backingContext childContext:childContext objectID:relationshipObjectID];
[mutableBackingRelationshipObjects addObject:backingRelationshipObject];
NSManagedObject *managedRelationshipObject = [self findAndUpdateObjectWithResourceIdentifier:theResourceIdentifier ofEntity:relationship.destinationEntity attributes:theAttributes backingContext:backingContext childContext:childContext objectID:relationshipObjectID];
[mutableManagedRelationshipObjects addObject:managedRelationshipObject];
[self relationshipFunction:relationshipRepresentations ofEntity:relationship.destinationEntity fromResponse:response backingContext:backingContext childContext:childContext backingObject:backingRelationshipObject managedObject:managedRelationshipObject];
}];
}
[backingObject setValue:mutableBackingRelationshipObjects forKey:relationship.name];
[managedObject setValue:mutableManagedRelationshipObjects forKey:relationship.name];
} else {
[self reusableAttributesForRepresentation:relationshipRepresentationOrArrayOfRepresentations ofEntity:relationship.destinationEntity fromResponse:response success:^(NSString *theResourceIdentifier, NSDictionary *theAttributes, NSDictionary *relationshipRepresentations) {
NSManagedObjectID *relationshipObjectID = [self objectIDForBackingObjectForEntity:relationship.destinationEntity withResourceIdentifier:theResourceIdentifier];
NSManagedObject *backingRelationshipObject = [self findOrCreateBackingObjectWithResourceIdentifier:theResourceIdentifier ofEntity:relationship.destinationEntity attributes:theAttributes backingContext:backingContext childContext:childContext objectID:relationshipObjectID];
[backingObject setValue:backingRelationshipObject forKey:relationship.name];
NSManagedObject *managedRelationshipObject = [self findAndUpdateObjectWithResourceIdentifier:theResourceIdentifier ofEntity:relationship.destinationEntity attributes:theAttributes backingContext:backingContext childContext:childContext objectID:relationshipObjectID];
[managedObject setValue:managedRelationshipObject forKey:relationship.name];
[self relationshipFunction:relationshipRepresentations ofEntity:relationship.destinationEntity fromResponse:response backingContext:backingContext childContext:childContext backingObject:backingRelationshipObject managedObject:managedRelationshipObject];
}];
}
}
}
}
- (void)createObjectRepresentation:(NSDictionary *)representation
ofEntity:(NSEntityDescription *)entity
fromResponse:(NSHTTPURLResponse *)response
backingContext:(NSManagedObjectContext *)backingContext
childContext:(NSManagedObjectContext *)childContext
{
[self reusableAttributesForRepresentation:representation ofEntity:entity fromResponse:response success:^(NSString *resourceIdentifier, NSDictionary *attributes, NSDictionary *relationshipRepresentations) {
NSManagedObjectID *objectID = [self objectIDForBackingObjectForEntity:entity withResourceIdentifier:resourceIdentifier];
NSManagedObject *backingObject = [self findOrCreateBackingObjectWithResourceIdentifier:resourceIdentifier ofEntity:entity attributes:attributes backingContext:backingContext childContext:childContext objectID:objectID];
NSManagedObject *managedObject = [self findAndUpdateObjectWithResourceIdentifier:resourceIdentifier ofEntity:entity attributes:attributes backingContext:backingContext childContext:childContext objectID:objectID];
[self relationshipFunction:relationshipRepresentations ofEntity:entity fromResponse:response backingContext:backingContext childContext:childContext backingObject:backingObject managedObject:managedObject];
}];
}
- (id)executeRequest:(NSPersistentStoreRequest *)persistentStoreRequest
withContext:(NSManagedObjectContext *)context
error:(NSError *__autoreleasing *)error
{
if (persistentStoreRequest.requestType == NSFetchRequestType) {
NSFetchRequest *fetchRequest = (NSFetchRequest *)persistentStoreRequest;
NSURLRequest *request = [self.HTTPClient requestForFetchRequest:fetchRequest withContext:context];
if ([request URL]) {
AFHTTPRequestOperation *operation = [self.HTTPClient HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
id representationOrArrayOfRepresentations = [self.HTTPClient representationOrArrayOfRepresentationsFromResponseObject:responseObject];
NSArray *representations = nil;
if ([representationOrArrayOfRepresentations isKindOfClass:[NSArray class]]) {
representations = representationOrArrayOfRepresentations;
} else {
representations = [NSArray arrayWithObject:representationOrArrayOfRepresentations];
}
NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
childContext.parentContext = context;
childContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
[[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:childContext queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
[context mergeChangesFromContextDidSaveNotification:note];
}];
NSManagedObjectContext *backingContext = [self backingManagedObjectContext];
[childContext performBlock:^{
NSEntityDescription *entity = fetchRequest.entity;
for (NSDictionary *representation in representations) {
[self createObjectRepresentation:representation ofEntity:entity fromResponse:operation.response backingContext:backingContext childContext:childContext];
}
if (![backingContext save:error] || ![childContext save:error]) {
NSLog(@"Error: %@", *error);
}
}];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Error: %@", error);
}];
operation.successCallbackQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
[self.HTTPClient enqueueHTTPRequestOperation:operation];
}
NSManagedObjectContext *backingContext = [self backingManagedObjectContext];
NSArray *results = nil;
NSFetchRequestResultType resultType = fetchRequest.resultType;
switch (resultType) {
case NSManagedObjectResultType: {
fetchRequest = [fetchRequest copy];
fetchRequest.entity = [NSEntityDescription entityForName:fetchRequest.entityName inManagedObjectContext:backingContext];
fetchRequest.resultType = NSDictionaryResultType;
fetchRequest.propertiesToFetch = @[ kAFIncrementalStoreResourceIdentifierAttributeName ];
results = [backingContext executeFetchRequest:fetchRequest error:error];
NSMutableArray *mutableObjects = [NSMutableArray arrayWithCapacity:[results count]];
for (NSString *resourceIdentifier in [results valueForKeyPath:kAFIncrementalStoreResourceIdentifierAttributeName]) {
NSManagedObjectID *objectID = [self objectIDForEntity:fetchRequest.entity withResourceIdentifier:resourceIdentifier];
NSManagedObject *object = [context objectWithID:objectID];
[mutableObjects addObject:object];
}
return mutableObjects;
}
case NSManagedObjectIDResultType:
case NSDictionaryResultType:
case NSCountResultType:
return [backingContext executeFetchRequest:fetchRequest error:error];
default:
goto _error;
}
} else {
switch (persistentStoreRequest.requestType) {
case NSSaveRequestType:
return @[];
default:
goto _error;
}
}
return nil;
_error: {
NSMutableDictionary *mutableUserInfo = [NSMutableDictionary dictionary];
[mutableUserInfo setValue:[NSString stringWithFormat:NSLocalizedString(@"Unsupported NSFetchRequestResultType, %d", nil), persistentStoreRequest.requestType] forKey:NSLocalizedDescriptionKey];
if (error) {
*error = [[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:0 userInfo:mutableUserInfo];
}
return nil;
}
}
#pragma mark -
- (NSIncrementalStoreNode *)newValuesForObjectWithID:(NSManagedObjectID *)objectID
withContext:(NSManagedObjectContext *)context
error:(NSError *__autoreleasing *)error
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:[[objectID entity] name]];
fetchRequest.resultType = NSDictionaryResultType;
fetchRequest.fetchLimit = 1;
fetchRequest.includesSubentities = NO;
fetchRequest.propertiesToFetch = [[[NSEntityDescription entityForName:fetchRequest.entityName inManagedObjectContext:context] attributesByName] allKeys];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"%K = %@", kAFIncrementalStoreResourceIdentifierAttributeName, [self referenceObjectForObjectID:objectID]];
NSArray *results = [[self backingManagedObjectContext] executeFetchRequest:fetchRequest error:error];
NSDictionary *attributeValues = [results lastObject] ?: [NSDictionary dictionary];
NSIncrementalStoreNode *node = [[NSIncrementalStoreNode alloc] initWithObjectID:objectID withValues:attributeValues version:1];
if ([self.HTTPClient respondsToSelector:@selector(shouldFetchRemoteAttributeValuesForObjectWithID:inManagedObjectContext:)] && [self.HTTPClient shouldFetchRemoteAttributeValuesForObjectWithID:objectID inManagedObjectContext:context]) {
if (attributeValues) {
NSManagedObjectContext *backingManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
backingManagedObjectContext.parentContext = context;
backingManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
NSURLRequest *request = [self.HTTPClient requestWithMethod:@"GET" pathForObjectWithID:objectID withContext:context];
if ([request URL]) {
AFHTTPRequestOperation *operation = [self.HTTPClient HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, NSDictionary *representation) {
NSManagedObject *managedObject = [backingManagedObjectContext existingObjectWithID:objectID error:error];
NSMutableDictionary *mutablePropertyValues = [attributeValues mutableCopy];
[mutablePropertyValues addEntriesFromDictionary:[self.HTTPClient attributesForRepresentation:representation ofEntity:managedObject.entity fromResponse:operation.response]];
[managedObject setValuesForKeysWithDictionary:mutablePropertyValues];
if (![backingManagedObjectContext save:error]) {
NSLog(@"Error: %@", *error);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Error: %@, %@", operation, error);
}];
[self.HTTPClient enqueueHTTPRequestOperation:operation];
}
}
}
return node;
}
- (id)newValueForRelationship:(NSRelationshipDescription *)relationship
forObjectWithID:(NSManagedObjectID *)objectID
withContext:(NSManagedObjectContext *)context
error:(NSError *__autoreleasing *)error
{
if ([self.HTTPClient respondsToSelector:@selector(shouldFetchRemoteValuesForRelationship:forObjectWithID:inManagedObjectContext:)] && [self.HTTPClient shouldFetchRemoteValuesForRelationship:relationship forObjectWithID:objectID inManagedObjectContext:context]) {
NSURLRequest *request = [self.HTTPClient requestWithMethod:@"GET" pathForRelationship:relationship forObjectWithID:objectID withContext:context];
if ([request URL] && ![[context existingObjectWithID:objectID error:nil] hasChanges]) {
NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
childContext.parentContext = context;
childContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
NSManagedObjectContext *backingContext = [self backingManagedObjectContext];
[[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:childContext queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
[context mergeChangesFromContextDidSaveNotification:note];
}];
AFHTTPRequestOperation *operation = [self.HTTPClient HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
id representationOrArrayOfRepresentations = [self.HTTPClient representationOrArrayOfRepresentationsFromResponseObject:responseObject];
NSArray *representations = nil;
if ([representationOrArrayOfRepresentations isKindOfClass:[NSArray class]]) {
representations = representationOrArrayOfRepresentations;
} else {
representations = [NSArray arrayWithObject:representationOrArrayOfRepresentations];
}
[childContext performBlock:^{
NSManagedObject *managedObject = [childContext existingObjectWithID:[self objectIDForEntity:[objectID entity] withResourceIdentifier:[self referenceObjectForObjectID:objectID]] error:nil];
NSManagedObject *backingObject = [backingContext existingObjectWithID:[self objectIDForBackingObjectForEntity:[objectID entity] withResourceIdentifier:[self referenceObjectForObjectID:objectID]] error:nil];
id mutableBackingRelationshipObjects = [relationship isOrdered] ? [NSMutableOrderedSet orderedSetWithCapacity:[representations count]] : [NSMutableSet setWithCapacity:[representations count]];
id mutableManagedRelationshipObjects = [relationship isOrdered] ? [NSMutableOrderedSet orderedSetWithCapacity:[representations count]] : [NSMutableSet setWithCapacity:[representations count]];
NSEntityDescription *entity = relationship.destinationEntity;
for (NSDictionary *representation in representations) {
NSString *relationshipResourceIdentifier = [self.HTTPClient resourceIdentifierForRepresentation:representation ofEntity:entity fromResponse:operation.response];
NSManagedObjectID *relationshipObjectID = [self objectIDForBackingObjectForEntity:relationship.destinationEntity withResourceIdentifier:relationshipResourceIdentifier];
NSDictionary *relationshipAttributes = [self.HTTPClient attributesForRepresentation:representation ofEntity:entity fromResponse:operation.response];
NSManagedObject *backingRelationshipObject = (relationshipObjectID != nil) ? [backingContext existingObjectWithID:relationshipObjectID error:nil] : [NSEntityDescription insertNewObjectForEntityForName:[relationship.destinationEntity name] inManagedObjectContext:backingContext];
[backingRelationshipObject setValuesForKeysWithDictionary:relationshipAttributes];
[mutableBackingRelationshipObjects addObject:backingRelationshipObject];
NSManagedObject *managedRelationshipObject = [childContext existingObjectWithID:[self objectIDForEntity:relationship.destinationEntity withResourceIdentifier:relationshipResourceIdentifier] error:nil];
[managedRelationshipObject setValuesForKeysWithDictionary:relationshipAttributes];
[mutableManagedRelationshipObjects addObject:managedRelationshipObject];
if (relationshipObjectID == nil) {
[childContext insertObject:managedRelationshipObject];
}
}
if ([relationship isToMany]) {
[managedObject setValue:mutableManagedRelationshipObjects forKey:relationship.name];
[backingObject setValue:mutableBackingRelationshipObjects forKey:relationship.name];
} else {
[managedObject setValue:[mutableManagedRelationshipObjects anyObject] forKey:relationship.name];
[backingObject setValue:[mutableBackingRelationshipObjects anyObject] forKey:relationship.name];
}
if (![backingContext save:error] || ![childContext save:error]) {
NSLog(@"Error: %@", *error);
}
}];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Error: %@, %@", operation, error);
}];
operation.successCallbackQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
[self.HTTPClient enqueueHTTPRequestOperation:operation];
}
}
NSManagedObjectID *backingObjectID = [self objectIDForBackingObjectForEntity:[objectID entity] withResourceIdentifier:[self referenceObjectForObjectID:objectID]];
NSManagedObject *backingObject = (backingObjectID == nil) ? nil : [[self backingManagedObjectContext] existingObjectWithID:backingObjectID error:nil];
if (backingObject && ![backingObject hasChanges]) {
id backingRelationshipObject = [backingObject valueForKeyPath:relationship.name];
if ([relationship isToMany]) {
NSMutableArray *mutableObjects = [NSMutableArray arrayWithCapacity:[backingRelationshipObject count]];
for (NSString *resourceIdentifier in [backingRelationshipObject valueForKeyPath:kAFIncrementalStoreResourceIdentifierAttributeName]) {
NSManagedObjectID *objectID = [self objectIDForEntity:relationship.destinationEntity withResourceIdentifier:resourceIdentifier];
[mutableObjects addObject:objectID];
}
return mutableObjects;
} else {
NSString *resourceIdentifier = [backingRelationshipObject valueForKeyPath:kAFIncrementalStoreResourceIdentifierAttributeName];
NSManagedObjectID *objectID = [self objectIDForEntity:relationship.destinationEntity withResourceIdentifier:resourceIdentifier];
return objectID ?: [NSNull null];
}
} else {
if ([relationship isToMany]) {
return [NSArray array];
} else {
return [NSNull null];
}
}
}
#pragma mark - NSIncrementalStore
- (void)managedObjectContextDidRegisterObjectsWithIDs:(NSArray *)objectIDs {
[super managedObjectContextDidRegisterObjectsWithIDs:objectIDs];
for (NSManagedObjectID *objectID in objectIDs) {
[_registeredObjectIDsByResourceIdentifier setObject:objectID forKey:[self referenceObjectForObjectID:objectID]];
}
}
- (void)managedObjectContextDidUnregisterObjectsWithIDs:(NSArray *)objectIDs {
[super managedObjectContextDidUnregisterObjectsWithIDs:objectIDs];
for (NSManagedObjectID *objectID in objectIDs) {
[_registeredObjectIDsByResourceIdentifier removeObjectForKey:[self referenceObjectForObjectID:objectID]];
}
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment