|
25 | 25 | #import "RKRequestOperationSubclass.h"
|
26 | 26 | #import "NSManagedObjectContext+RKAdditions.h"
|
27 | 27 |
|
| 28 | +// Graph visitor |
| 29 | +#import "RKResponseDescriptor.h" |
| 30 | +#import "RKEntityMapping.h" |
| 31 | +#import "RKDynamicMapping.h" |
| 32 | +#import "RKRelationshipMapping.h" |
| 33 | + |
28 | 34 | // Set Logging Component
|
29 | 35 | #undef RKLogComponent
|
30 | 36 | #define RKLogComponent RKlcl_cRestKitCoreData
|
31 | 37 |
|
| 38 | +@interface RKNestedManagedObjectKeyPathMappingGraphVisitor : NSObject |
| 39 | + |
| 40 | +@property (nonatomic, readonly) NSSet *keyPaths; |
| 41 | + |
| 42 | +- (id)initWithResponseDescriptors:(NSArray *)responseDescriptors; |
| 43 | + |
| 44 | +@end |
| 45 | + |
| 46 | +@interface RKNestedManagedObjectKeyPathMappingGraphVisitor () |
| 47 | +@property (nonatomic, strong) NSMutableSet *mutableKeyPaths; |
| 48 | +@end |
| 49 | + |
| 50 | +@implementation RKNestedManagedObjectKeyPathMappingGraphVisitor |
| 51 | + |
| 52 | +- (id)initWithResponseDescriptors:(NSArray *)responseDescriptors |
| 53 | +{ |
| 54 | + self = [self init]; |
| 55 | + if (self) { |
| 56 | + self.mutableKeyPaths = [NSMutableSet set]; |
| 57 | + for (RKResponseDescriptor *responseDescriptor in responseDescriptors) { |
| 58 | + [self visitMapping:responseDescriptor.mapping atKeyPath:responseDescriptor.keyPath ?: @""]; |
| 59 | + } |
| 60 | + } |
| 61 | + return self; |
| 62 | +} |
| 63 | + |
| 64 | +- (NSSet *)keyPaths |
| 65 | +{ |
| 66 | + return self.mutableKeyPaths; |
| 67 | +} |
| 68 | + |
| 69 | +- (void)visitMapping:(RKMapping *)mapping atKeyPath:(NSString *)keyPath |
| 70 | +{ |
| 71 | + if ([self.keyPaths containsObject:keyPath]) return; |
| 72 | + |
| 73 | + if ([mapping isKindOfClass:[RKEntityMapping class]]) { |
| 74 | + [self.mutableKeyPaths addObject:keyPath]; |
| 75 | + } else { |
| 76 | + if ([mapping isKindOfClass:[RKDynamicMapping class]]) { |
| 77 | + RKDynamicMapping *dynamicMapping = (RKDynamicMapping *)mapping; |
| 78 | + for (RKMapping *nestedMapping in dynamicMapping.objectMappings) { |
| 79 | + [self visitMapping:nestedMapping atKeyPath:keyPath]; |
| 80 | + } |
| 81 | + } else if ([mapping isKindOfClass:[RKObjectMapping class]]) { |
| 82 | + RKObjectMapping *objectMapping = (RKObjectMapping *)mapping; |
| 83 | + for (RKRelationshipMapping *relationshipMapping in objectMapping.relationshipMappings) { |
| 84 | + NSString *nestedKeyPath = [@[ keyPath, relationshipMapping.destinationKeyPath ] componentsJoinedByString:@"."]; |
| 85 | + [self visitMapping:relationshipMapping.mapping atKeyPath:nestedKeyPath]; |
| 86 | + } |
| 87 | + } |
| 88 | + } |
| 89 | +} |
| 90 | + |
| 91 | +@end |
| 92 | + |
32 | 93 | NSArray *RKArrayOfFetchRequestFromBlocksWithURL(NSArray *fetchRequestBlocks, NSURL *URL)
|
33 | 94 | {
|
34 | 95 | NSMutableArray *fetchRequests = [NSMutableArray array];
|
|
40 | 101 | return fetchRequests;
|
41 | 102 | }
|
42 | 103 |
|
43 |
| -// RKManagedObjectOrArrayOfManagedObjectsInContext(id managedObjectOrArrayOfManagedObjects, NSManagedObjectContext *managedObjectContext); |
44 |
| -// Find the key paths for all entity mappings in the graph whose parent objects are not other managed objects |
45 |
| - |
46 |
| -static NSDictionary *RKDictionaryOfManagedObjectsInContextFromDictionaryOfManagedObjects(NSDictionary *dictionaryOfManagedObjects, NSManagedObjectContext *managedObjectContext) |
| 104 | +// Finds the key paths for all entity mappings in the graph whose parent objects are not other managed objects |
| 105 | +static NSDictionary *RKDictionaryFromDictionaryWithManagedObjectsAtKeyPathsRefetchedInContext(NSDictionary *dictionaryOfManagedObjects, NSSet *keyPaths, NSManagedObjectContext *managedObjectContext) |
47 | 106 | {
|
48 |
| - NSMutableDictionary *newDictionary = [[NSMutableDictionary alloc] initWithCapacity:[dictionaryOfManagedObjects count]]; |
| 107 | + NSMutableDictionary *newDictionary = [dictionaryOfManagedObjects mutableCopy]; |
49 | 108 | [managedObjectContext performBlockAndWait:^{
|
50 | 109 | __block NSError *error = nil;
|
51 |
| - [dictionaryOfManagedObjects enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) { |
| 110 | + |
| 111 | + for (NSString *keyPath in keyPaths) { |
| 112 | + id value = [dictionaryOfManagedObjects valueForKeyPath:keyPath]; |
52 | 113 | if ([value isKindOfClass:[NSArray class]]) {
|
| 114 | + BOOL isMutable = [value isKindOfClass:[NSMutableArray class]]; |
53 | 115 | NSMutableArray *newValue = [[NSMutableArray alloc] initWithCapacity:[value count]];
|
54 | 116 | for (__strong id object in value) {
|
55 | 117 | if ([object isKindOfClass:[NSManagedObject class]]) {
|
|
59 | 121 |
|
60 | 122 | [newValue addObject:object];
|
61 | 123 | }
|
62 |
| - value = [newValue copy]; |
| 124 | + value = (isMutable) ? newValue : [newValue copy]; |
| 125 | + } else if ([value isKindOfClass:[NSSet class]]) { |
| 126 | + BOOL isMutable = [value isKindOfClass:[NSMutableSet class]]; |
| 127 | + NSMutableSet *newValue = [[NSMutableSet alloc] initWithCapacity:[value count]]; |
| 128 | + for (__strong id object in value) { |
| 129 | + if ([object isKindOfClass:[NSManagedObject class]]) { |
| 130 | + object = [managedObjectContext existingObjectWithID:[object objectID] error:&error]; |
| 131 | + NSCAssert(object, @"Failed to find existing object with ID %@ in context %@: %@", [object objectID], managedObjectContext, error); |
| 132 | + } |
| 133 | + |
| 134 | + [newValue addObject:object]; |
| 135 | + } |
| 136 | + value = (isMutable) ? newValue : [newValue copy]; |
| 137 | + } else if ([value isKindOfClass:[NSOrderedSet class]]) { |
| 138 | + BOOL isMutable = [value isKindOfClass:[NSMutableOrderedSet class]]; |
| 139 | + NSMutableOrderedSet *newValue = [NSMutableOrderedSet orderedSet]; |
| 140 | + [(NSOrderedSet *)value enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) { |
| 141 | + if ([object isKindOfClass:[NSManagedObject class]]) { |
| 142 | + object = [managedObjectContext existingObjectWithID:[object objectID] error:&error]; |
| 143 | + NSCAssert(object, @"Failed to find existing object with ID %@ in context %@: %@", [object objectID], managedObjectContext, error); |
| 144 | + } |
| 145 | + |
| 146 | + [newValue setObject:object atIndex:index]; |
| 147 | + }]; |
| 148 | + value = (isMutable) ? newValue : [newValue copy]; |
63 | 149 | } else if ([value isKindOfClass:[NSManagedObject class]]) {
|
64 | 150 | value = [managedObjectContext existingObjectWithID:[value objectID] error:&error];
|
65 | 151 | NSCAssert(value, @"Failed to find existing object with ID %@ in context %@: %@", [value objectID], managedObjectContext, error);
|
66 | 152 | }
|
67 | 153 |
|
68 |
| - [newDictionary setValue:value forKey:key]; |
69 |
| - }]; |
| 154 | + [newDictionary setValue:value forKeyPath:keyPath]; |
| 155 | + } |
70 | 156 | }];
|
71 | 157 |
|
72 | 158 | return newDictionary;
|
@@ -343,9 +429,12 @@ - (void)willFinish
|
343 | 429 | success = [self saveContext:&error];
|
344 | 430 | if (! success) self.error = error;
|
345 | 431 |
|
346 |
| - // Refetch the mapping results from the externally configured context |
347 |
| - NSDictionary *resultsDictionaryFromOriginalContext = RKDictionaryOfManagedObjectsInContextFromDictionaryOfManagedObjects([self.mappingResult dictionary], self.managedObjectContext); |
348 |
| - self.mappingResult = [[RKMappingResult alloc] initWithDictionary:resultsDictionaryFromOriginalContext]; |
| 432 | + // Refetch all managed objects nested at key paths within the results dictionary before returning |
| 433 | + if (self.mappingResult) { |
| 434 | + RKNestedManagedObjectKeyPathMappingGraphVisitor *visitor = [[RKNestedManagedObjectKeyPathMappingGraphVisitor alloc] initWithResponseDescriptors:self.responseDescriptors]; |
| 435 | + NSDictionary *resultsDictionaryFromOriginalContext = RKDictionaryFromDictionaryWithManagedObjectsAtKeyPathsRefetchedInContext([self.mappingResult dictionary], visitor.keyPaths, self.managedObjectContext); |
| 436 | + self.mappingResult = [[RKMappingResult alloc] initWithDictionary:resultsDictionaryFromOriginalContext]; |
| 437 | + } |
349 | 438 | }
|
350 | 439 |
|
351 | 440 | @end
|
0 commit comments