Search code examples
iosobjective-crestkitrestkit-0.20

RestKit - Mapping a collection of resources from an index endpoint


I'm using RestKit in my current project to facilitate communcation with a bespoke REST API. I'm running into problems with retrieving a collection of resources from an index endpoint. I am able to retreive the resources however I am unable to have them mapped to the target class.

Here are the details (i've substituted widgets for the actual business object in question).

The endpoint is GET /api/v1/widgets . The JSON returned from the endpoint looks like this:

{"widgets": [
  {
    "widgets": {
      "name": "whatsit",
      "material": "wood",
      "crafted_by": "Hrunkner"
    }
  },
  {
    "widgets": {
      "name": "doodad",
      "material": "carbon fiber",
      "crafted_by": "Zaphod"
    }
  }
]}

I'm aware that the above JSON structure is idiosyncratic with respect to the root node name for each resource, however that is currently beyond my control.

I'm using the following code in a WidgetManager class to perform the request:

AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];

// apiBaseURL equals @"http://localhost:3000/api/v1/"
NSURL *apiBaseURL = [NSURL URLWithString:appDelegate.apiBaseURL]; 
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:apiBaseURL];

NSString *itemsPath = @"widgets";
NSString *keyPath = @"widgets.widgets";
NSIndexSet *statusCodeSet = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);

RKObjectMapping *requestMapping = [[Widget widgetMapping] inverseMapping];

RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping: requestMapping
                                                                               objectClass:[Widget class]
                                                                               rootKeyPath:keyPath
                                                                                    method:RKRequestMethodGET];

RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:requestMapping
                                                                                        method:RKRequestMethodGET
                                                                                   pathPattern:nil
                                                                                       keyPath:keyPath
                                                                                   statusCodes:statusCodeSet];
[manager addRequestDescriptor: requestDescriptor];
[manager addResponseDescriptor:responseDescriptor];

[manager getObjectsAtPath:@"widgets"
               parameters:@{}
                  success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
                      [appDelegate setWidgets:[mappingResult array]];
                  } failure:^(RKObjectRequestOperation *operation, NSError *error) {
                      NSLog(@"FAIL");
                  }];

My [Widget mapping] method looks like this:

RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[Widget class]];
[mapping addAttributeMappingsFromDictionary:@{
                                              @"name": @"name",
                                              @"material": @"material",
                                              @"crafted_by": @"craftedBy"
                                              }];
return mapping;

It seems to me that the use of the inverseMapping is the issue, however when I attempt to use [Widget widgetMapping] instead of [[Widget widgetMapping] inverseMapping] as the request mapping, I get the following error

RKRequestDescriptor objects must be initialized with a mapping whose target class is NSMutableDictionary, got Widget (see [RKObjectMapping requestMapping])

What am I doing wrong here? How should I properly be configuring my request to map each of the returned objects to the Widget class?

Thanks and apologies for any omissions or errors in my question.


Solution

  • It turns out that using the inverse mapping for ONLY the response descriptor allowed the object mapping to proceed successfully. Previously I was attempting to use the same mapping for both the request and response descriptors, which will not work. Therefore changing the assignment to requestMapping to simply [Widget requestmapping] and then passing [requestMapping inverseMapping] to the request descriptor init solved my problem. Naturally this will necessitate renaming the local variable requestMapping, as it is in fact not useful for request descriptors.

    The answer at the following question provided a nudge in the right direction in clarifying the function of mappings in request versus response descriptors: https://stackoverflow.com/a/23217042/757806