Multiple RKResponseDescriptors causing crash with Core Data with RestKit

0

I create my RestKit mappings in a singleton for each service call, such as:

- (void)setupMapping
{
RKObjectManager *objectManager = [RKObjectManager sharedManager];
RKEntityMapping *challengesMapping  = [RKEntityMapping    mappingForEntityForName:@"Challenge" inManagedObjectStore:[objectManager managedObjectStore]];

[challengesMapping addAttributeMappingsFromDictionary:@{
 @"uuid": @"uuid",
 @"title": @"title",
 @"description": @"challengeDescription",
 @"icon": @"icon",
 @"active_from": @"activeFrom",
 @"active_to": @"activeTo",
 @"trigger": @"trigger",
 @"show_in_feed": @"showInFeed",
 @"points": @"points",
 @"trigger": @"trigger",
 @"type": @"type",
 @"min_level": @"minLevel"
 }];
 challengesMapping.identificationAttributes = @[ @"uuid" ];

RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:challengesMapping
                                                                                   pathPattern:CHALLENGE_PATH
                                                                                       keyPath:@"challenges"
                                                                                   statusCodes:[NSIndexSet indexSetWithIndex:SUCCESS]];
[objectManager addResponseDescriptor:responseDescriptor];

RKObjectMapping *sessionMapping = [RKObjectMapping mappingForClass:[TimeStamp class]];
[sessionMapping addAttributeMappingsFromArray:@[@"ts"]];

[objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:sessionMapping
                                                                             pathPattern:CHALLENGE_PATH
                                                                                 keyPath:nil
                                                                            statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];

}

And

- (void)setupMapping
{
   RKObjectManager *objectManager = [RKObjectManager sharedManager];
RKEntityMapping *festivalsMapping  = [RKEntityMapping mappingForEntityForName:@"Festival" inManagedObjectStore:[objectManager managedObjectStore]];

[festivalsMapping addAttributeMappingsFromDictionary:@{
 @"uuid": @"uuid",
 @"festival": @"festivalDescription",
 @"start_ts": @"start_ts",
 @"end_ts": @"end_ts",
 @"title": @"title"
 }];
festivalsMapping.identificationAttributes = @[ @"uuid" ];

RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:festivalsMapping
                                                                                   pathPattern:GET_FESTIVALS_PATH
                                                                                       keyPath:@"festivals"
                                                                                   statusCodes:[NSIndexSet indexSetWithIndex:SUCCESS]];
[objectManager addResponseDescriptor:responseDescriptor];
RKObjectMapping* sessionMapping = [RKObjectMapping mappingForClass:[TimeStamp class]];
[sessionMapping addAttributeMappingsFromArray:@[@"ts"]];

[objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:sessionMapping
                                                                             pathPattern:GET_FESTIVALS_PATH
                                                                                 keyPath:nil
                                                                            statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];
}

The first server call for the first mapping (challenges) works fine, but when I make call two (festivals mapping) I then get the error: "Object's persistent store is not reachable from this NSManagedObjectContext's coordinator". I understand this to be a possible threading issue in Core Data, but I cannot find the cause in my code.

I get the following info in the console:

(lldb) po $r0 error: Couldn't materialize struct: Couldn't read r0 (materialize) Errored out in Execute, couldn't PrepareToExecuteJITExpression (lldb) register read General Purpose Registers: r4 = 0x00000000 r5 = 0x00066e95 MyAppmain + 1 at main.m:14 r6 = 0x00000000 r7 = 0x2fd9ccf8 r8 = 0x2fd9cd10 r10 = 0x00000000 r11 = 0x00000000 r12 = 0x00000148 sp = 0x2fd9ccd4 lr = 0x00066f09 MyAppmain + 117 at main.m:16 pc = 0x00066f09 MyApp`main + 117 at main.m:16 cpsr = 0x00000010 5 registers were unavailable.

EDIT

Here's the full example of one of the service/mapping classes. I have seen a similar pattern used before, i.e. using the GCD singleton. I also don't think the TimeStamp is duplicate per the comment below because the pathPatterns are different. Correct? I did try removing them but the same issue. Which is expected because they're not backed by Core Data

#import "ChallengeService.h"

static ChallengeService __strong *defaultService = nil;

#define CHALLENGE_PATH @"/api/challenges"

@implementation ChallengeService

+ (ChallengeService *)defaultService
{
static dispatch_once_t pred;
dispatch_once(&pred, ^{
    defaultService = [[self alloc] initWithPath:CHALLENGE_PATH];
    [defaultService setupMapping];
});

return defaultService;
}

- (void)setupMapping
{
RKObjectManager *objectManager = [RKObjectManager sharedManager];
RKEntityMapping *challengesMapping  = [RKEntityMapping     mappingForEntityForName:@"Challenge" inManagedObjectStore:[objectManager managedObjectStore]];

[challengesMapping addAttributeMappingsFromDictionary:@{
 @"uuid": @"uuid",
 @"title": @"title",
 @"description": @"challengeDescription",
 @"icon": @"icon",
 @"active_from": @"activeFrom",
 @"active_to": @"activeTo",
 @"trigger": @"trigger",
 @"show_in_feed": @"showInFeed",
 @"points": @"points",
 @"trigger": @"trigger",
 @"type": @"type",
 @"min_level": @"minLevel"
 }];
 challengesMapping.identificationAttributes = @[ @"uuid" ];

RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor    responseDescriptorWithMapping:challengesMapping
                                                                                      pathPattern:CHALLENGE_PATH
                                                                                           keyPath:@"challenges"
                                                                                   statusCodes:[NSIndexSet indexSetWithIndex:SUCCESS]];
[objectManager addResponseDescriptor:responseDescriptor];

RKObjectMapping *sessionMapping = [RKObjectMapping mappingForClass:[TimeStamp class]];
[sessionMapping addAttributeMappingsFromArray:@[@"ts"]];

[objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:sessionMapping
                                                                             pathPattern:CHALLENGE_PATH
                                                                                 keyPath:nil
                                                                            statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]]; }

- (void)getChallengesFromDate:(NSDate *)date
                onSuccess:(DidSucceedBlock)successBlock
                  onError:(DidFailWithErrorBlock)failBlock
{    
[defaultService getWithData:nil
                   fromDate:date
                     onLoad:^(id object) {
                         successBlock(object);
                     } onError:^(NSError *error) {
    failBlock(error);
}];

}
ios
core-data
restkit
asked on Stack Overflow Apr 22, 2013 by Shocks • edited Apr 22, 2013 by Shocks

1 Answer

0

First, you can't add the same mapping to the same object manager multiple times. Consider using multiple different object managers (one for each set of requests, I.e. one for each singleton) or defining the common mappings up-front.

Then, add details to your question about when you are creating and configuring each of these singletons. Are any of them run on a background thread or is all configuration done up-front when the app is started. If you know the app will be using all of the configurations every time it's used then do everything up-front as there is little cost to building the configuration.

You need to be careful with your defaultService as currently you will confuse the accessor method with the static variable. Define the static variable inside the method and name it _defaultService. Then, you should move the setup method into initWithPath as it's part of the initialisation. Ensure that you're calling the accessor method properly and that you aren't calling it (at least the first time, and probably every time) from a background thread.

answered on Stack Overflow Apr 22, 2013 by Wain • edited Apr 22, 2013 by Wain

User contributions licensed under CC BY-SA 3.0