Mitchell
Mitchell

Reputation: 313

iOS RestKit issue: Invalid parameter not satisfying: responseDescriptors

I am trying to use RestKit to retrieve a listing of events and I keep getting this:

2013-05-20 10:52:56.708 EventApp[3380:c07] I restkit:RKLog.m:34 RestKit logging initialized...
2013-05-20 10:52:56.773 EventApp[3380:c07] *** Assertion failure in    -[RKObjectRequestOperation initWithRequest:responseDescriptors:], /Users/mitchell/Desktop/eventapp/take2/EventApp/Pods/RestKit/Code/Network/RKObjectRequestOperation.m:158
2013-05-20 10:52:56.774 EventApp[3380:c07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: responseDescriptors' 

I have been scratching my head for days on this one. As I have a fair amount of gaps in my iOS dev skills(about one project every year) it would greatly help if someone can just lead me in the right direction here using some laymen terms.

Please consider that I am looking to use enqueueObjectRequestOperation specifically for batching multiple requests. I have just pieced together bits of my code for translation here.

Here is what my datamodel looks like:

Here is what my datamodel looks like:

Here is the what the JSON file looks like:

[{
"id":1,
"farm_id":1,
"all_day": "NO",
"from": "2013-05-08T18:45:38Z",
"to": "2013-05-08T18:45:38Z",
"name": "event 1",
"desc": "some description",
"photo": "some.png",
"price": "price"
}]

Here is my code:

NSManagedObjectContext *context;

RKObjectManager *objectManager;
if (self.eventContext == nil) {
    NSError *error = nil;
    NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"FarmApp" ofType:@"momd"]];
    RKEntityMapping *entityMapping;
    // NOTE: Due to an iOS 5 bug, the managed object model returned is immutable.
    NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];

    RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];

    // Initialize the Core Data stack
    [managedObjectStore createPersistentStoreCoordinator];

    NSPersistentStore __unused *eventPersistentStore = [managedObjectStore addInMemoryPersistentStore:&error];
    NSAssert(eventPersistentStore, @"Failed to add persistent store: %@", error);

    [managedObjectStore createManagedObjectContexts];

    // Set the default store shared instance
    [RKManagedObjectStore setDefaultStore:managedObjectStore];

    // Configure the object manager
    RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:@"http://sandbox.bm.com"]];

    [RKObjectManager setSharedManager:objectManager];

    [objectManager setRequestSerializationMIMEType:@"application/json"];
    [objectManager setAcceptHeaderWithMIMEType:@"text/plain"];

    objectManager.managedObjectStore = managedObjectStore;
        entityMapping = [RKEntityMapping mappingForEntityForName:@"Event" inManagedObjectStore:managedObjectStore];
        [entityMapping addAttributeMappingsFromDictionary:@{
         @"id":             @"eventID",
         @"farm_id":        @"farm",
         @"all_day":        @"allDay",
         @"from":           @"from",
         @"to":             @"to",
         @"name":           @"name",
         @"desc":           @"desc",
         @"photo":          @"photo",
         @"price":          @"price"
         }];        
    RKResponseDescriptor *successDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:nil keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];

    [objectManager addResponseDescriptor:successDescriptor];

    RKResponseDescriptor *errorDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:nil keyPath:@"errors" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassClientError)];

    [objectManager addResponseDescriptor:errorDescriptor];
    self.eventContext = managedObjectStore.mainQueueManagedObjectContext;

}
context = self.eventContext;
NSString* url = [NSString stringWithFormat:@"http://sandbox.bm.com/farmapp/%@.json", @"events"];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
RKObjectRequestOperation *operation = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:[objectManager responseDescriptors]];
[operation setCompletionBlockWithSuccess:nil failure:^(RKObjectRequestOperation *operation, NSError *error) {
    NSLog(@"Loaded this error: %@", [error localizedDescription]);
}];
NSArray *operations = [NSArray arrayWithObjects:operation, nil];
for (int i = 0; i < [operations count]; i++ ) {
    [[RKObjectManager sharedManager] enqueueObjectRequestOperation:[operations objectAtIndex:i]];
}

Can someone out there help me?

Here is the final solution

if (self.eventContext == nil) {
    NSManagedObjectContext *context;
    RKObjectManager *objectManager;
    NSError *error = nil;
    NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"FarmApp" ofType:@"momd"]];
    RKEntityMapping *entityMapping;
    // NOTE: Due to an iOS 5 bug, the managed object model returned is immutable.
    NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];

    RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];

    // Initialize the Core Data stack
    [managedObjectStore createPersistentStoreCoordinator];

    NSPersistentStore __unused *eventPersistentStore = [managedObjectStore addInMemoryPersistentStore:&error];
    NSAssert(eventPersistentStore, @"Failed to add persistent store: %@", error);

    [managedObjectStore createManagedObjectContexts];

    // Set the default store shared instance
    [RKManagedObjectStore setDefaultStore:managedObjectStore];

    // Configure the object manager
    objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:@"http://sandbox.bm.com"]];

    [RKObjectManager setSharedManager:objectManager];

    [objectManager setRequestSerializationMIMEType:@"application/json"];
    [objectManager setAcceptHeaderWithMIMEType:@"text/plain"];

    objectManager.managedObjectStore = managedObjectStore;
    entityMapping = [RKEntityMapping mappingForEntityForName:@"Event" inManagedObjectStore:managedObjectStore];
    [entityMapping addAttributeMappingsFromDictionary:@{
     @"id":             @"eventID",
     //@"farm_id":        @"farm",-->cannot create relationship this way
     @"farm_id" :       @"farmID",//farmID attribute needs to be added to Event's model
     @"all_day":        @"allDay",
     @"from":           @"from",
     @"to":             @"to",
     @"name":           @"name",
     @"desc":           @"desc",
     @"photo":          @"photo",
     @"price":          @"price"
     }];

    [entityMapping addConnectionForRelationship:@"farm" connectedBy:@"farmID"];

    RKResponseDescriptor *successDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:nil keyPath:@"events" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];

    [objectManager addResponseDescriptor:successDescriptor];

    RKResponseDescriptor *errorDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:nil keyPath:@"errors" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassClientError)];

    [objectManager addResponseDescriptor:errorDescriptor];

    self.eventContext = managedObjectStore.mainQueueManagedObjectContext;
    context = self.eventContext;
    NSString* url = [NSString stringWithFormat:@"http://sandbox.bm.com/farmapp/%@.json", @"events"];
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
    RKManagedObjectRequestOperation *operation = [objectManager managedObjectRequestOperationWithRequest:request managedObjectContext:managedObjectStore.mainQueueManagedObjectContext success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
            NSLog(@"Success");
        } failure:^(RKObjectRequestOperation *operation, NSError *error) {
                NSLog(@"Failure");
        }];
    NSArray *operations = [NSArray arrayWithObjects:operation, nil];
    for (int i = 0; i < [operations count]; i++ ) {
        [[RKObjectManager sharedManager] enqueueObjectRequestOperation:[operations objectAtIndex:i]];
    }

}

Upvotes: 0

Views: 4035

Answers (2)

JD_
JD_

Reputation: 381

As @JoelH. suggests in one of his comments, you need to use RKManagedObjectRequestOperation instead of RKObjectRequestOperation.

For example :

RKManagedObjectRequestOperation *operation = [objectmanager managedObjectRequestOperationWithRequest:request managedObjectContext:managedObjectStore.mainQueueManagedObjectContext success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
        NSLog(@"Success");
    } failure:^(RKObjectRequestOperation *operation, NSError *error) {
        NSLog(@"Failure");
}];

Besides, I think the way you are mapping

@"farm_id": @"farm"

is not correct.

If you want to build the relationship between Event and Farm, you need to use one of these methods : relationshipMappingFromKeyPath:toKeyPath:withMapping: or addConnectionForRelationship:connectedBy:

If the Farm object already exists and you only want to map the new Event, I would go for addConnectionForRelationship:connectedBy:

For example :

RKEntityMapping* eventMapping = [RKEntityMapping mappingForEntityForName:@"Event" inManagedObjectStore:managedObjectStore];
[entityMapping addAttributeMappingsFromDictionary:@{
 @"id":             @"eventID",
 //@"farm_id":        @"farm",-->cannot create relationship this way
 @"farm_id" :       @"farmID",//farmID attribute needs to be added to Event's model
 @"all_day":        @"allDay",
 @"from":           @"from",
 @"to":             @"to",
 @"name":           @"name",
 @"desc":           @"desc",
 @"photo":          @"photo",
 @"price":          @"price"
 }];

[eventMapping addConnectionForRelationship:@"farm" connectedBy:@"farmID"];   

It would also require to add a farmID attribute in your Event model, as RestKit does not allow relationship connection without intermediary attributes yet.

Upvotes: 1

Joel H.
Joel H.

Reputation: 2784

The RestKit docs (Mapping Without KVC) seem to imply that if you don't have a KVC label on the top level(e.g. events:[]), a pathPattern: is required for the parser to know which mapping to use. Since both your keyPath: and pathPattern: are nil for your successDescriptor, I have two suggestions:

  1. If changing the JSON structure is possible, change the top level to { events:[...] } and change keyPath:nil to keyPath:@"events" to reflect this change.

  2. If it's not possible, change pathPattern:nil to pathPattern:@"/farmapp" Notice, I set pathPattern: to match your resource URL. This second suggestion might not work if you have other types of resources branching from that URL as well.

Also, I noticed that your errorDescriptor uses the same mapping(entityMapping) as your successDescriptor. I don't know if that's what you intended, but if your error response is supposed to be some different error object, I suggest changing that.

Hope this helps!

Upvotes: 0

Related Questions