Hook the RKManagedObjectMappingOperationDataSource into the Managed Object Context save lifecycle to avoid the creation of duplicate objects during sequential mapping operations

This commit is contained in:
Blake Watters
2012-12-18 22:00:35 -05:00
parent 97b60e64b1
commit 862e84a515
12 changed files with 226 additions and 108 deletions

View File

@@ -213,7 +213,7 @@
}
NSDictionary *mappingDictionary = @{ (keyPath ?: [NSNull null]) : mapping };
RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithObject:parsedData mappingsDictionary:mappingDictionary];
RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:parsedData mappingsDictionary:mappingDictionary];
mapper.mappingOperationDataSource = self.mappingOperationDataSource;
__block RKMappingResult *mappingResult;
[self.managedObjectContext performBlockAndWait:^{

View File

@@ -136,11 +136,21 @@ extern NSString * const RKObjectMappingNestingAttributeKeyName;
if (self) {
self.managedObjectContext = managedObjectContext;
self.managedObjectCache = managedObjectCache;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(updateCacheWithChangesFromContextWillSaveNotification:)
name:NSManagedObjectContextWillSaveNotification
object:managedObjectContext];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (id)mappingOperation:(RKMappingOperation *)mappingOperation targetObjectForRepresentation:(NSDictionary *)representation withMapping:(RKObjectMapping *)mapping
{
NSAssert(representation, @"Mappable data cannot be nil");
@@ -239,4 +249,35 @@ extern NSString * const RKObjectMappingNestingAttributeKeyName;
return YES;
}
// NOTE: In theory we should be able to use the userInfo dictionary, but the dictionary was coming in empty (12/18/2012)
- (void)updateCacheWithChangesFromContextWillSaveNotification:(NSNotification *)notification
{
NSSet *objectsToAdd = [[self.managedObjectContext insertedObjects] setByAddingObjectsFromSet:[self.managedObjectContext updatedObjects]];
__block BOOL success;
__block NSError *error = nil;
[self.managedObjectContext performBlockAndWait:^{
success = [self.managedObjectContext obtainPermanentIDsForObjects:[objectsToAdd allObjects] error:&error];
}];
if (! success) {
RKLogWarning(@"Failed obtaining permanent managed object ID's for %ld objects: the managed object cache was not updated and duplicate objects may be created.", (long) [objectsToAdd count]);
RKLogError(@"Obtaining permanent managed object IDs failed with error: %@", error);
return;
}
// Update the cache
if ([self.managedObjectCache respondsToSelector:@selector(didFetchObject:)]) {
for (NSManagedObject *managedObject in objectsToAdd) {
[self.managedObjectCache didFetchObject:managedObject];
}
}
if ([self.managedObjectCache respondsToSelector:@selector(didDeleteObject::)]) {
for (NSManagedObject *managedObject in [self.managedObjectContext deletedObjects]) {
[self.managedObjectCache didDeleteObject:managedObject];
}
}
}
@end

View File

@@ -287,7 +287,7 @@ static dispatch_queue_t RKResponseMapperSerializationQueue() {
- (RKMappingResult *)performMappingWithObject:(id)sourceObject error:(NSError **)error
{
RKObjectMappingOperationDataSource *dataSource = [RKObjectMappingOperationDataSource new];
self.mapperOperation = [[RKMapperOperation alloc] initWithObject:sourceObject mappingsDictionary:self.responseMappingsDictionary];
self.mapperOperation = [[RKMapperOperation alloc] initWithRepresentation:sourceObject mappingsDictionary:self.responseMappingsDictionary];
self.mapperOperation.mappingOperationDataSource = dataSource;
if (NSLocationInRange(self.response.statusCode, RKStatusCodeRangeForClass(RKStatusCodeClassSuccessful))) {
self.mapperOperation.targetObject = self.targetObject;
@@ -327,7 +327,7 @@ static inline NSManagedObjectID *RKObjectIDFromObjectIfManaged(id object)
self.operationQueue = [NSOperationQueue new];
[self.managedObjectContext performBlockAndWait:^{
// Configure the mapper
self.mapperOperation = [[RKMapperOperation alloc] initWithObject:sourceObject mappingsDictionary:self.responseMappingsDictionary];
self.mapperOperation = [[RKMapperOperation alloc] initWithRepresentation:sourceObject mappingsDictionary:self.responseMappingsDictionary];
self.mapperOperation.delegate = self.mapperDelegate;
// Configure a data source to defer execution of connection operations until mapping is complete

View File

@@ -37,7 +37,7 @@
## Mappings Dictionary
The mappings dictionary describes how to object map the source object. The keys of the dictionary are key paths into the `sourceObject` and the values are `RKMapping` objects describing how to map the representations at the corresponding key path. This dictionary based approach enables a single document to contain an arbitrary number of object representations that can be mapped independently. Consider the following example JSON structure:
The mappings dictionary describes how to object map the source object. The keys of the dictionary are key paths into the `representation` and the values are `RKMapping` objects describing how to map the representations at the corresponding key path. This dictionary based approach enables a single document to contain an arbitrary number of object representations that can be mapped independently. Consider the following example JSON structure:
{ "tags": [ "hacking", "phreaking" ], "authors": [ "Captain Crunch", "Emmanuel Goldstein" ], "magazine": { "title": "2600 The Hacker Quarterly" } }
@@ -52,7 +52,7 @@
### The NSNull Key
A mapping set for the key `[NSNull null]` value has special significance to the mapper operation. When a mapping is encountered with the a null key, the entire `sourceObject` is processed using the given mapping. This provides support for mapping content that does not have an outer nesting attribute.
A mapping set for the key `[NSNull null]` value has special significance to the mapper operation. When a mapping is encountered with the a null key, the entire `representation` is processed using the given mapping. This provides support for mapping content that does not have an outer nesting attribute.
## Data Source
@@ -60,7 +60,7 @@
## Target Object
If a `targetObject` is configured on the mapper operation, all mapping work on the `sourceObject` will target the specified object. For transient `NSObject` mappings, this ensures that the properties of an existing object are updated rather than an new object being created for the mapped representation. If an array of representations is being processed and a `targetObject` is provided, it must be a mutable collection object else an exception will be raised.
If a `targetObject` is configured on the mapper operation, all mapping work on the `representation` will target the specified object. For transient `NSObject` mappings, this ensures that the properties of an existing object are updated rather than an new object being created for the mapped representation. If an array of representations is being processed and a `targetObject` is provided, it must be a mutable collection object else an exception will be raised.
## Core Data
@@ -75,11 +75,11 @@
/**
Initializes the operation with a source object and a mappings dictionary.
@param object An `NSDictionary` or `NSArray` of `NSDictionary` object representations to be mapped into local domain objects.
@param representation An `NSDictionary` or `NSArray` of `NSDictionary` object representations to be mapped into local domain objects.
@param mappingsDictionary An `NSDictionary` wherein the keys are mappable key paths in `object` and the values are `RKMapping` objects specifying how the representations at its key path are to be mapped.
@return The receiver, initialized with the given object and and dictionary of key paths to mappings.
*/
- (id)initWithObject:(id)object mappingsDictionary:(NSDictionary *)mappingsDictionary;
- (id)initWithRepresentation:(id)representation mappingsDictionary:(NSDictionary *)mappingsDictionary;
///------------------------------------------
/// @name Accessing Mapping Result and Errors
@@ -100,14 +100,14 @@
///-------------------------------------
/**
The source object representation against which the mapping is performed.
The representation of one or more objects against which the mapping is performed.
Either an `NSDictionary` or an `NSArray` of `NSDictionary` objects.
*/
@property (nonatomic, strong, readonly) id sourceObject;
@property (nonatomic, strong, readonly) id representation;
/**
A dictionary of key paths to `RKMapping` objects specifying how object representations in the `sourceObject` are to be mapped.
A dictionary of key paths to `RKMapping` objects specifying how object representations in the `representation` are to be mapped.
Please see the above discussion for in-depth details about the mappings dictionary.
*/
@@ -130,6 +130,8 @@
*/
@property (nonatomic, weak) id<RKMapperOperationDelegate> delegate;
- (BOOL)execute:(NSError **)error;
@end
///--------------------------------------
@@ -177,7 +179,7 @@
@param mapper The mapper operation performing the mapping.
@param dictionaryOrArrayOfDictionaries The `NSDictictionary` or `NSArray` of `NSDictionary` object representations that was found at the `keyPath`.
@param keyPath The key path that the representation was read from in the `sourceObject`. If the `keyPath` was `[NSNull null]` in the `mappingsDictionary`, it will be given as `nil` to the delegate.
@param keyPath The key path that the representation was read from in the `representation`. If the `keyPath` was `[NSNull null]` in the `mappingsDictionary`, it will be given as `nil` to the delegate.
*/
- (void)mapper:(RKMapperOperation *)mapper didFindRepresentationOrArrayOfRepresentations:(id)dictionaryOrArrayOfDictionaries atKeyPath:(NSString *)keyPath;
@@ -194,11 +196,11 @@
///----------------------------------------------
/**
Tells the delegate that the mapper is about to start a mapping operation to map a representation found in the `sourceObject`.
Tells the delegate that the mapper is about to start a mapping operation to map a representation found in the `representation`.
@param mapper The mapper operation performing the mapping.
@param mappingOperation The mapping operation that is about to be started.
@param keyPath The key path that was mapped. A `nil` key path indicates that the mapping matched the entire `sourceObject`.
@param keyPath The key path that was mapped. A `nil` key path indicates that the mapping matched the entire `representation`.
*/
- (void)mapper:(RKMapperOperation *)mapper willStartMappingOperation:(RKMappingOperation *)mappingOperation forKeyPath:(NSString *)keyPath;
@@ -207,7 +209,7 @@
@param mapper The mapper operation performing the mapping.
@param mappingOperation The mapping operation that has finished.
@param keyPath The key path that was mapped. A `nil` key path indicates that the mapping matched the entire `sourceObject`.
@param keyPath The key path that was mapped. A `nil` key path indicates that the mapping matched the entire `representation`.
*/
- (void)mapper:(RKMapperOperation *)mapper didFinishMappingOperation:(RKMappingOperation *)mappingOperation forKeyPath:(NSString *)keyPath;
@@ -216,7 +218,7 @@
@param mapper The mapper operation performing the mapping.
@param mappingOperation The mapping operation that has failed.
@param keyPath The key path that was mapped. A `nil` key path indicates that the mapping matched the entire `sourceObject`.
@param keyPath The key path that was mapped. A `nil` key path indicates that the mapping matched the entire `representation`.
@param error The error that occurred during the execution of the mapping operation.
*/
- (void)mapper:(RKMapperOperation *)mapper didFailMappingOperation:(RKMappingOperation *)mappingOperation forKeyPath:(NSString *)keyPath withError:(NSError *)error;

View File

@@ -42,18 +42,18 @@ static NSString *RKDelegateKeyPathFromKeyPath(NSString *keyPath)
@property (nonatomic, strong, readwrite) NSError *error;
@property (nonatomic, strong, readwrite) RKMappingResult *mappingResult;
@property (nonatomic, strong) NSMutableArray *mappingErrors;
@property (nonatomic, strong) id sourceObject;
@property (nonatomic, strong) id representation;
@property (nonatomic, strong, readwrite) NSDictionary *mappingsDictionary;
@end
@implementation RKMapperOperation
- (id)initWithObject:(id)object mappingsDictionary:(NSDictionary *)mappingsDictionary;
- (id)initWithRepresentation:(id)representation mappingsDictionary:(NSDictionary *)mappingsDictionary;
{
self = [super init];
if (self) {
self.sourceObject = object;
self.representation = representation;
self.mappingsDictionary = mappingsDictionary;
self.mappingErrors = [NSMutableArray new];
self.mappingOperationDataSource = [RKObjectMappingOperationDataSource new];
@@ -117,16 +117,17 @@ static NSString *RKDelegateKeyPathFromKeyPath(NSString *keyPath)
#pragma mark - Mapping Primitives
- (id)mapObject:(id)mappableObject atKeyPath:(NSString *)keyPath usingMapping:(RKMapping *)mapping
// Maps a singular object representation
- (id)mapRepresentation:(id)representation atKeyPath:(NSString *)keyPath usingMapping:(RKMapping *)mapping
{
NSAssert([mappableObject respondsToSelector:@selector(setValue:forKeyPath:)], @"Expected self.object to be KVC compliant");
NSAssert([representation respondsToSelector:@selector(setValue:forKeyPath:)], @"Expected self.object to be KVC compliant");
id destinationObject = nil;
if (self.targetObject) {
destinationObject = self.targetObject;
RKObjectMapping *objectMapping = nil;
if ([mapping isKindOfClass:[RKDynamicMapping class]]) {
objectMapping = [(RKDynamicMapping *)mapping objectMappingForRepresentation:mappableObject];
objectMapping = [(RKDynamicMapping *)mapping objectMappingForRepresentation:representation];
} else if ([mapping isKindOfClass:[RKObjectMapping class]]) {
objectMapping = (RKObjectMapping *)mapping;
} else {
@@ -142,15 +143,15 @@ static NSString *RKDelegateKeyPathFromKeyPath(NSString *keyPath)
return nil;
} else {
// There is more than one mapping present. We are likely mapping secondary key paths to new objects
destinationObject = [self objectWithMapping:mapping andData:mappableObject];
destinationObject = [self objectForRepresentation:representation withMapping:mapping];
}
}
} else {
destinationObject = [self objectWithMapping:mapping andData:mappableObject];
destinationObject = [self objectForRepresentation:representation withMapping:mapping];
}
if (mapping && destinationObject) {
BOOL success = [self mapFromObject:mappableObject toObject:destinationObject atKeyPath:keyPath usingMapping:mapping];
BOOL success = [self mapRepresentation:representation toObject:destinationObject atKeyPath:keyPath usingMapping:mapping];
if (success) {
return destinationObject;
}
@@ -163,28 +164,29 @@ static NSString *RKDelegateKeyPathFromKeyPath(NSString *keyPath)
return nil;
}
- (NSArray *)mapCollection:(NSArray *)mappableObjects atKeyPath:(NSString *)keyPath usingMapping:(RKMapping *)mapping
// Map a collection of object representations
- (NSArray *)mapRepresentations:(id)representations atKeyPath:(NSString *)keyPath usingMapping:(RKMapping *)mapping
{
NSAssert(mappableObjects != nil, @"Cannot map without an collection of mappable objects");
NSAssert(representations != nil, @"Cannot map without an collection of mappable objects");
NSAssert(mapping != nil, @"Cannot map without a mapping to consult");
NSArray *objectsToMap = mappableObjects;
NSArray *objectsToMap = representations;
if (mapping.forceCollectionMapping) {
// If we have forced mapping of a dictionary, map each subdictionary
if ([mappableObjects isKindOfClass:[NSDictionary class]]) {
if ([representations isKindOfClass:[NSDictionary class]]) {
RKLogDebug(@"Collection mapping forced for NSDictionary, mapping each key/value independently...");
objectsToMap = [NSMutableArray arrayWithCapacity:[mappableObjects count]];
for (id key in mappableObjects) {
NSDictionary *dictionaryToMap = [NSDictionary dictionaryWithObject:[mappableObjects valueForKey:key] forKey:key];
objectsToMap = [NSMutableArray arrayWithCapacity:[representations count]];
for (id key in representations) {
NSDictionary *dictionaryToMap = [NSDictionary dictionaryWithObject:[representations valueForKey:key] forKey:key];
[(NSMutableArray *)objectsToMap addObject:dictionaryToMap];
}
} else {
RKLogWarning(@"Collection mapping forced but mappable objects is of type '%@' rather than NSDictionary", NSStringFromClass([mappableObjects class]));
RKLogWarning(@"Collection mapping forced but representations is of type '%@' rather than NSDictionary", NSStringFromClass([representations class]));
}
}
// Ensure we are mapping onto a mutable collection if there is a target
NSMutableArray *mappedObjects = self.targetObject ? self.targetObject : [NSMutableArray arrayWithCapacity:[mappableObjects count]];
NSMutableArray *mappedObjects = self.targetObject ? self.targetObject : [NSMutableArray arrayWithCapacity:[representations count]];
if (NO == [mappedObjects respondsToSelector:@selector(addObject:)]) {
NSString *errorMessage = [NSString stringWithFormat:
@"Cannot map a collection of objects onto a non-mutable collection. Unexpected destination object type '%@'",
@@ -194,12 +196,12 @@ static NSString *RKDelegateKeyPathFromKeyPath(NSString *keyPath)
}
for (id mappableObject in objectsToMap) {
id destinationObject = [self objectWithMapping:mapping andData:mappableObject];
id destinationObject = [self objectForRepresentation:mappableObject withMapping:mapping];
if (! destinationObject) {
continue;
}
BOOL success = [self mapFromObject:mappableObject toObject:destinationObject atKeyPath:keyPath usingMapping:mapping];
BOOL success = [self mapRepresentation:mappableObject toObject:destinationObject atKeyPath:keyPath usingMapping:mapping];
if (success) {
[mappedObjects addObject:destinationObject];
}
@@ -209,7 +211,7 @@ static NSString *RKDelegateKeyPathFromKeyPath(NSString *keyPath)
}
// The workhorse of this entire process. Emits object loading operations
- (BOOL)mapFromObject:(id)mappableObject toObject:(id)destinationObject atKeyPath:(NSString *)keyPath usingMapping:(RKMapping *)mapping
- (BOOL)mapRepresentation:(id)mappableObject toObject:(id)destinationObject atKeyPath:(NSString *)keyPath usingMapping:(RKMapping *)mapping
{
NSAssert(destinationObject != nil, @"Cannot map without a target object to assign the results to");
NSAssert(mappableObject != nil, @"Cannot map without a collection of attributes");
@@ -239,16 +241,16 @@ static NSString *RKDelegateKeyPathFromKeyPath(NSString *keyPath)
}
}
- (id)objectWithMapping:(RKMapping *)mapping andData:(id)mappableData
- (id)objectForRepresentation:(id)representation withMapping:(RKMapping *)mapping
{
NSAssert([mapping isKindOfClass:[RKMapping class]], @"Expected an RKMapping object");
NSAssert(self.mappingOperationDataSource, @"Cannot find or instantiate objects without a data source");
RKObjectMapping *objectMapping = nil;
if ([mapping isKindOfClass:[RKDynamicMapping class]]) {
objectMapping = [(RKDynamicMapping *)mapping objectMappingForRepresentation:mappableData];
objectMapping = [(RKDynamicMapping *)mapping objectMappingForRepresentation:representation];
if (! objectMapping) {
RKLogDebug(@"Mapping %@ declined mapping for data %@: returned nil objectMapping", mapping, mappableData);
RKLogDebug(@"Mapping %@ declined mapping for representation %@: returned nil objectMapping", mapping, representation);
}
} else if ([mapping isKindOfClass:[RKObjectMapping class]]) {
objectMapping = (RKObjectMapping *)mapping;
@@ -257,27 +259,29 @@ static NSString *RKDelegateKeyPathFromKeyPath(NSString *keyPath)
}
if (objectMapping) {
return [self.mappingOperationDataSource mappingOperation:nil targetObjectForRepresentation:mappableData withMapping:objectMapping];
return [self.mappingOperationDataSource mappingOperation:nil targetObjectForRepresentation:representation withMapping:objectMapping];
}
return nil;
}
- (id)performMappingForObject:(id)mappableValue atKeyPath:(NSString *)keyPath usingMapping:(RKMapping *)mapping
- (id)mapRepresentationOrRepresentations:(id)mappableValue atKeyPath:(NSString *)keyPath usingMapping:(RKMapping *)mapping
{
id mappingResult;
if (mapping.forceCollectionMapping || [mappableValue isKindOfClass:[NSArray class]] || [mappableValue isKindOfClass:[NSSet class]]) {
RKLogDebug(@"Found mappable collection at keyPath '%@': %@", keyPath, mappableValue);
mappingResult = [self mapCollection:mappableValue atKeyPath:keyPath usingMapping:mapping];
mappingResult = [self mapRepresentations:mappableValue atKeyPath:keyPath usingMapping:mapping];
} else {
RKLogDebug(@"Found mappable data at keyPath '%@': %@", keyPath, mappableValue);
mappingResult = [self mapObject:mappableValue atKeyPath:keyPath usingMapping:mapping];
mappingResult = [self mapRepresentation:mappableValue atKeyPath:keyPath usingMapping:mapping];
}
return mappingResult;
}
- (NSMutableDictionary *)performKeyPathMappingUsingMappingDictionary:(NSDictionary *)mappingsByKeyPath
#pragma mark -
- (NSMutableDictionary *)mapSourceRepresentationWithMappingsDictionary:(NSDictionary *)mappingsByKeyPath
{
BOOL foundMappable = NO;
NSMutableDictionary *results = [NSMutableDictionary dictionary];
@@ -285,18 +289,18 @@ static NSString *RKDelegateKeyPathFromKeyPath(NSString *keyPath)
if ([self isCancelled]) return nil;
id mappingResult = nil;
id mappableValue = nil;
id nestedRepresentation = nil;
RKLogTrace(@"Examining keyPath '%@' for mappable content...", keyPath);
if ([keyPath isEqual:[NSNull null]] || [keyPath isEqualToString:@""]) {
mappableValue = self.sourceObject;
nestedRepresentation = self.representation;
} else {
mappableValue = [self.sourceObject valueForKeyPath:keyPath];
nestedRepresentation = [self.representation valueForKeyPath:keyPath];
}
// Not found...
if (mappableValue == nil || mappableValue == [NSNull null] || [self isNullCollection:mappableValue]) {
if (nestedRepresentation == nil || nestedRepresentation == [NSNull null] || [self isNullCollection:nestedRepresentation]) {
RKLogDebug(@"Found unmappable value at keyPath: %@", keyPath);
if ([self.delegate respondsToSelector:@selector(mapper:didNotFindRepresentationOrArrayOfRepresentationsAtKeyPath:)]) {
@@ -310,10 +314,10 @@ static NSString *RKDelegateKeyPathFromKeyPath(NSString *keyPath)
foundMappable = YES;
RKMapping *mapping = [mappingsByKeyPath objectForKey:keyPath];
if ([self.delegate respondsToSelector:@selector(mapper:didFindRepresentationOrArrayOfRepresentations:atKeyPath:)]) {
[self.delegate mapper:self didFindRepresentationOrArrayOfRepresentations:mappableValue atKeyPath:RKDelegateKeyPathFromKeyPath(keyPath)];
[self.delegate mapper:self didFindRepresentationOrArrayOfRepresentations:nestedRepresentation atKeyPath:RKDelegateKeyPathFromKeyPath(keyPath)];
}
mappingResult = [self performMappingForObject:mappableValue atKeyPath:keyPath usingMapping:mapping];
mappingResult = [self mapRepresentationOrRepresentations:nestedRepresentation atKeyPath:keyPath usingMapping:mapping];
if (mappingResult) {
[results setObject:mappingResult forKey:keyPath];
@@ -336,12 +340,12 @@ static NSString *RKDelegateKeyPathFromKeyPath(NSString *keyPath)
- (void)main
{
NSAssert(self.sourceObject != nil, @"Cannot perform object mapping without a source object to map from");
NSAssert(self.representation != nil, @"Cannot perform object mapping without a source object to map from");
NSAssert(self.mappingsDictionary, @"Cannot perform object mapping without a dictionary of mappings");
if ([self isCancelled]) return;
RKLogDebug(@"Performing object mapping sourceObject: %@\n and targetObject: %@", self.sourceObject, self.targetObject);
RKLogDebug(@"Executing mapping operation for representation: %@\n and targetObject: %@", self.representation, self.targetObject);
if ([self.delegate respondsToSelector:@selector(mapperWillStartMapping:)]) {
[self.delegate mapperWillStartMapping:self];
@@ -349,7 +353,7 @@ static NSString *RKDelegateKeyPathFromKeyPath(NSString *keyPath)
// Perform the mapping
BOOL foundMappable = NO;
NSMutableDictionary *results = [self performKeyPathMappingUsingMappingDictionary:self.mappingsDictionary];
NSMutableDictionary *results = [self mapSourceRepresentationWithMappingsDictionary:self.mappingsDictionary];
if ([self isCancelled]) return;
foundMappable = (results != nil);
@@ -359,7 +363,7 @@ static NSString *RKDelegateKeyPathFromKeyPath(NSString *keyPath)
// If we found nothing eligible for mapping in the content, add an unmappable key path error and fail mapping
// If the content is empty, we don't consider it an error
BOOL isEmpty = [self.sourceObject respondsToSelector:@selector(count)] && ([self.sourceObject count] == 0);
BOOL isEmpty = [self.representation respondsToSelector:@selector(count)] && ([self.representation count] == 0);
if (foundMappable == NO && !isEmpty) {
NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
NSLocalizedString(@"Unable to find any mappings for the given content", nil), NSLocalizedDescriptionKey,
@@ -376,4 +380,11 @@ static NSString *RKDelegateKeyPathFromKeyPath(NSString *keyPath)
if (results) self.mappingResult = [[RKMappingResult alloc] initWithDictionary:results];
}
- (BOOL)execute:(NSError **)error
{
[self start];
if (error) *error = self.error;
return self.mappingResult != nil;
}
@end

View File

@@ -20,9 +20,9 @@
@interface RKMapperOperation (Private)
- (id)mapObject:(id)mappableObject atKeyPath:(NSString *)keyPath usingMapping:(RKMapping *)mapping;
- (NSArray *)mapCollection:(NSArray *)mappableObjects atKeyPath:(NSString *)keyPath usingMapping:(RKMapping *)mapping;
- (BOOL)mapFromObject:(id)mappableObject toObject:(id)destinationObject atKeyPath:(NSString *)keyPath usingMapping:(RKMapping *)mapping;
- (id)objectWithMapping:(RKMapping *)objectMapping andData:(id)mappableData;
- (id)mapRepresentation:(id)mappableObject atKeyPath:(NSString *)keyPath usingMapping:(RKMapping *)mapping;
- (NSArray *)mapRepresentations:(NSArray *)mappableObjects atKeyPath:(NSString *)keyPath usingMapping:(RKMapping *)mapping;
- (BOOL)mapRepresentation:(id)mappableObject toObject:(id)destinationObject atKeyPath:(NSString *)keyPath usingMapping:(RKMapping *)mapping;
- (id)objectForRepresentation:(id)representation withMapping:(RKMapping *)mapping;
@end

View File

@@ -148,7 +148,6 @@ id RKTransformedValueWithClass(id value, Class destinationType, NSValueTransform
return nil;
}
// Applies
// Key comes from: [[_nestedAttributeSubstitution allKeys] lastObject]] AND [[_nestedAttributeSubstitution allValues] lastObject];
NSArray *RKApplyNestingAttributeValueToMappings(NSString *attributeName, id value, NSArray *propertyMappings);
NSArray *RKApplyNestingAttributeValueToMappings(NSString *attributeName, id value, NSArray *propertyMappings)