Search code examples
iosobjective-cmappingrestkit

How to map a nested array of complex objects with RestKit


I have a series of complex objects:

@interface IDAQuizResponse : NSObject

@property (nonatomic) IDACallResponse *result;
@property (nonatomic) NSString *appId;
@property (nonatomic) IDAQuiz *quiz;

@end


@interface IDAQuiz : NSObject

@property (nonatomic) int quizId;
@property (nonatomic) NSString *state;
@property (nonatomic) NSString *result;
@property (nonatomic) NSArray *questions;

@end

@interface IDAQuestion : NSObject

@property (nonatomic) int questionId;
@property (nonatomic) NSString *question;
@property (nonatomic) NSArray *choices;

@end

@interface IDAChoice : NSObject

@property (nonatomic) NSString *choice;

@end

I am trying to map them using RestKit:

RKObjectMapping* quizResponseMapping = [RKObjectMapping mappingForClass:[IDAQuizResponse class]];
    [quizResponseMapping addAttributeMappingsFromArray:@[ @"appId" ]];

    RKObjectMapping* callResponseMapping = [RKObjectMapping mappingForClass:[IDACallResponse class]];
    [callResponseMapping addAttributeMappingsFromArray:@[ @"httpStatusCode", @"messageCode", @"message", @"moreInfo" ]];
    [quizResponseMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"result" toKeyPath:@"result" withMapping:callResponseMapping]];

    RKObjectMapping* quizMapping = [RKObjectMapping mappingForClass:[IDAQuiz class]];
    [quizMapping addAttributeMappingsFromArray:@[ @"quizId", @"state", @"result" ]];
    [quizResponseMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"quiz" toKeyPath:@"quiz" withMapping:quizMapping]];

    RKObjectMapping* questionMapping = [RKObjectMapping mappingForClass:[IDAQuestion class]];
    [questionMapping addAttributeMappingsFromArray:@[ @"id", @"question" ]];
    [quizMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"questions" toKeyPath:@"questions" withMapping:questionMapping]];

    RKObjectMapping* choiceMapping = [RKObjectMapping mappingForClass:[IDAChoice class]];
    [choiceMapping addAttributeMappingsFromArray:@[ @"choice" ]];
    [questionMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"choices" toKeyPath:@"choices" withMapping:choiceMapping]];

    RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:quizResponseMapping method:RKRequestMethodAny pathPattern:nil keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
    NSURL *URL = [NSURL URLWithString:<fancy_url>];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
    [request setHTTPMethod:@"POST"];
    RKObjectRequestOperation *objectRequestOperation = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors: @[responseDescriptor]];
    [objectRequestOperation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
        RKLogInfo(@"Successfully retrieved data");
        NSLog(@"Successfully retrieved data");
    } failure:^(RKObjectRequestOperation *operation, NSError *error) {
        RKLogError(@"Operation failed with error: %@", error);
        NSLog(@"Operation failed with error: %@", error);
    }];
    [objectRequestOperation start];

Everything maps correctly until I get to the nested array "questions" in the IDAQuiz class. It returns the correct number of objects based on the returned data, but all of the variables are nil (including the nested "choices" array). How do I map nested arrays of complex objects with RestKit?

UPDATE:

2015-08-28 14:01:38.061 [4894:98833] E restkit.object_mapping:RKMappingOperation.m:682 Failed transformation of value at keyPath 'question' to representation of type 'NSString': Error Domain=org.restkit.RKValueTransformers.ErrorDomain Code=3002 "Failed transformation of value

It appears that the mapper is attempting to map my entire object into a NSString instead of using the mapping values I've provided.


Solution

  • The data coming back with the json had a key for each of the question objects:

    result": "GENERATED",
    "questions":[
    {
    "question":{
    

    I created a wrapper with a single property IDAQuestion *question and used that to map. Mapping is now up and running.

    RKObjectMapping* quizResponseMapping = [RKObjectMapping mappingForClass:[IDAQuizResponse class]];
        [quizResponseMapping addAttributeMappingsFromArray:@[ @"appId" ]];
    
        RKObjectMapping* callResponseMapping = [RKObjectMapping mappingForClass:[IDACallResponse class]];
        [callResponseMapping addAttributeMappingsFromArray:@[ @"httpStatusCode", @"messageCode", @"message", @"moreInfo" ]];
        [quizResponseMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"result" toKeyPath:@"result" withMapping:callResponseMapping]];
    
        RKObjectMapping* quizMapping = [RKObjectMapping mappingForClass:[IDAQuiz class]];
        [quizMapping addAttributeMappingsFromArray:@[ @"quizId", @"state", @"result" ]];
        [quizResponseMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"quiz" toKeyPath:@"quiz" withMapping:quizMapping]];
    
        RKObjectMapping *questionWrapperMapping = [RKObjectMapping mappingForClass:[IDAQuestionWrapper class]];
        [quizMapping addRelationshipMappingWithSourceKeyPath:@"questions" mapping:questionWrapperMapping];
    
        RKObjectMapping* questionMapping = [RKObjectMapping mappingForClass:[IDAQuestion class]];
        [questionMapping addAttributeMappingsFromDictionary:@{
                                                              @"questionId": @"id",
                                                              @"question":@"question"
                                                              }];
        [questionWrapperMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"question" toKeyPath:@"question" withMapping:questionMapping]];
    
        RKObjectMapping* choiceMapping = [RKObjectMapping mappingForClass:[IDAChoice class]];
        [choiceMapping addAttributeMappingsFromArray:@[ @"choice" ]];
        [questionMapping addRelationshipMappingWithSourceKeyPath:@"choices" mapping: choiceMapping];