I'm stuck with a pagination issue.
When I go to /?PageSize=:perPage&Page=:page" I will get a json response like this:
Which I would like to map using the following paginator:
/* BBActivityPaginator */
RKObjectMapping *activityPaginationMapping = [RKObjectMapping mappingForClass:[BBActivityPaginator class]];
[activityPaginationMapping addAttributeMappingsFromDictionary:#{
#"Page" :#"currentPage",
#"PageSize" :#"perPage",
#"TotalResultCount" :#"objectCount"
}];
[activityPaginationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"PagedListItems" toKeyPath:#"activities" withMapping:activityMapping]];
[manager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:activityPaginationMapping
pathPattern:#"/?PageSize=:perPage&Page=:page"
keyPath:#"Model.Activities"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];
which is related to this mapping:
/* BBActivity */
RKObjectMapping *activityMapping = [RKObjectMapping mappingForClass:[BBActivity class]];
[activityMapping addAttributeMappingsFromDictionary:#{
#"Id" :#"identifier",
#"CreatedDateTime" :#"createdOn",
#"Description" :#"description",
#"CreatedDateTimeOrder" :#"order",
#"Type" :#"type",
#"DeletedActivityItem.Message" :#"deleted"
}];
[activityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"User" toKeyPath:#"user" withMapping:userMapping]];
[activityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"ObservationAdded.Observation" toKeyPath:#"observation" withMapping:observationMapping]];
[activityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"SightingNoteAdded.SightingNote" toKeyPath:#"observationNote" withMapping:observationNoteMapping]];
[activityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"SightingNoteAdded.Sighting" toKeyPath:#"observationNoteObservation" withMapping:observationMapping]];
[activityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"IdentificationAdded.Sighting" toKeyPath:#"identificationObservation" withMapping:observationMapping]];
[activityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"IdentificationAdded.Identification" toKeyPath:#"identification" withMapping:identificationMapping]];
[activityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"PostAdded.Post" toKeyPath:#"post" withMapping:postMapping]];
[manager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:activityMapping
pathPattern:nil
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];
... For brevity I won't add all the mappings...
When I want to make my call to the paginator, I'm wiring up this:
-(void)setPaginatorForStream:(NSString*)streamName {
[BBLog Log:#"BBStreamController.setPaginatorForStream:"];
[BBLog Debug:#"streamName:" withMessage:streamName];
__weak typeof(self) weakSelf = self;
NSString *streamUrl = [NSString stringWithFormat:#"http://api.blahblah.org.au/%#?PageSize=:perPage&Page=:currentPage&X-Requested-With=XMLHttpRequest", streamName];
if (!self.paginator) {
paginationMapping = [RKObjectMapping mappingForClass:[BBActivityPaginator class]];
RKResponseDescriptor *activitiesResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:[RKObjectMapping mappingForClass:[BBActivityPaginator class]]
pathPattern:nil
keyPath:nil
statusCodes:[NSIndexSet indexSetWithIndex:200]];
self.paginator = [[BBActivityPaginator alloc]initWithRequest:[NSURLRequest requestWithURL:[[NSURL alloc]initWithString:streamUrl]]
paginationMapping:paginationMapping
responseDescriptors:[[NSArray alloc]initWithObjects:activitiesResponseDescriptor, nil]
andDelegate:weakSelf];
}
self.paginator.perPage = 20;
[self.paginator setCompletionBlockWithSuccess:^(RKPaginator *paginator, NSArray *objects, NSUInteger page) {
[weakSelf.tableItems addObjectsFromArray:objects];
[weakSelf.tableView reloadData];
} failure:^(RKPaginator *paginator, NSError *error) {
NSLog(#"Failure: %#", error);
}];
Which is being activated in this method:
-(void)loadRequest {
[BBLog Log:#"BBStreamController.loadRequest"];
self.fetchBatch++;
[self.paginator loadPage:self.fetchBatch];
//[self.paginator setPaginatorLoading:YES];
self.loading = YES;
}
from the class initialisation:
-(BBStreamController*)initWithGroup:(NSString*)groupIdentifier
andDelegate:(id<BBStreamProtocol>)delegate {
[BBLog Log:#"BBStreamController.initWithGroup:andDelegate:"];
self = [self init];
if(self) {
_controller = delegate;
groupId = groupIdentifier;
[self setPaginatorForStream:groupIdentifier];
[self loadRequest];
}
[self loadView];
return self;
}
And getting nothing but pain...
Output:
E restkit:RKPaginator.m:207 Paginator didn't map info to compute page count. Assuming no pages.
2013-05-06 17:48:09.487 BowerBird[26570:5807] W restkit.object_mapping:RKMapperOperation.m:98 Adding mapping error: No mappable values found for any of the attributes or relationship mappings
Any one familiar with the finer details of RestKit 0.2.x's new pagination?
Your pathPattern and keyPath information needs to be set on the activitiesResponseDescriptor definition, not some paginator response descriptor that isn't actually used for anything.
The response descriptor is for the overall response and describes how to:
Tell that we have a match : pathPattern
Find the data to process : keyPath
Create the response objects : data mapping
The paginator mapping is additional to this and is only used to extract the page data from the response being processed.
Move the path pattern and key path to the response descriptor used for the request. The path pattern should also contain "PagedListItems" by the looks of it.
Related
Hello friends in stackoverflow:
I am having problem mapping data from the api using restkit.I really want to know where my mistake is.
Json format:
{
data: {
-current_condition: [1]
0: {
cloudcover: "16"
humidity: "59"
- weatherDesc: [1]
0: {
value: "Clear"
}
- weather: [5]
0: {
tempMinC: "10"
tempMinF: "50"
weatherCode: "119"
- weatherDesc: [1]
0: {
value: "Cloudy"
}
.......
}
here is my code how to do the mapping (I tried to mapping the 'cloudcover,humidity',and both 'weatherDesc' in current_condition and weather)
-(void)configureRestKit{
NSURL *baseURL = [NSURL URLWithString:#"http://www.raywenderlich.com"];
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:baseURL];
RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client];
RKObjectMapping *currentMapping = [RKObjectMapping mappingForClass:[CurrentCondition class]];
[currentMapping addAttributeMappingsFromArray:#[#"cloudcover",#"humidity",#"weatherDesc"]];
[currentMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"current_condition" toKeyPath:#"current_condition" withMapping:currentMapping]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:currentMapping method:RKRequestMethodGET pathPattern:#"/demos/weather_sample/weather.php" keyPath:#"data.current_condition" statusCodes:[NSIndexSet indexSetWithIndex:200]];
[objectManager addResponseDescriptor:responseDescriptor];
//weahter Desc
RKObjectMapping *weatherMapping = [RKObjectMapping mappingForClass:[Weather class]];
[weatherMapping addAttributeMappingsFromDictionary:#{#"weatherDesc": #"myweatherDesc"}];
[weatherMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"weather" toKeyPath:#"weather" withMapping:weatherMapping]];
RKResponseDescriptor *weatherresponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:weatherMapping method:RKRequestMethodGET pathPattern:#"/demos/weather_sample/weather.php" keyPath:#"data.weather" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:weatherresponseDescriptor];
}
-(void)loadCurrentCondition{
NSDictionary *queryParams = #{#"format": #"json"};
[[RKObjectManager sharedManager] getObjectsAtPath:#"/demos/weather_sample/weather.php" parameters:queryParams success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
_myArr = mappingResult.array;
[self Humidity];
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"The error is :%#",error);
}];
}
-(void)Humidity{
restkitCurrentCondition *rkCC = [_myArr objectAtIndex:0];
NSLog(#"///////////////////////the humidity is: %ld",rkCC.humidity.longValue);
NSLog(#"//////////////////// the cloudcover is: %ld",rkCC.cloudcover.longValue);
NSLog(#"/////////////// the weatherDesc is %#",rkCC.weatherDesc[0][#"value"]);
NSLog(#"///////// the weatherDesc in weather is %#",rkCC.restkitweather.myweatherDesc[0][#"value"]);
NSLog(#"///////// the weatherDesc in weather is %#",rkCC.restkitweather.myweatherDesc);
}
here is what I get:
2014-07-16 14:21:36.076 myRestSample[3783:60b] I restkit:RKLog.m:33 RestKit logging initialized...
2014-07-16 14:21:36.154 myRestSample[3783:60b] I
restkit.network:RKObjectRequestOperation.m:150 GET
'http://www.raywenderlich.com/demos/weather_sample/weather.php?format=json'
2014-07-16 14:21:36.289 myRestSample[3783:3a0f] I
restkit.network:RKObjectRequestOperation.m:220 GET
'http://www.raywenderlich.com/demos/weather_sample/weather.php?format=json' (200 OK / 6
objects) [request=0.1323s mapping=0.0024s total=0.1525s]
2014-07-16 14:21:36.289 myRestSample[3783:60b] -[Weather humidity]: unrecognized
selector sent to instance 0x8f8eb30
I tried to change the descritor's keypath to 'nil',looks like this
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:currentMapping
method:RKRequestMethodGET
pathPattern:#"/demos/weather_sample/weather.php" keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
The Weather and CurrentCondition are NSObject and there are four properties in CurrentCondition(cloudcover,humidity, NSArray *weaterDesc,Weather *restkitweather).There are only two properties in Weather.h (NSArray *myweatherDesc,*weatherDesc)
It seems I have already got 6 objects but why I got '[Weather humidity] Unreconized'.
Can anyone help??please..
'[Weather humidity] Unreconized'
This means that you have a Weather Instance, but you are treating it as a current conditions instance.
As per my answer to your other question you should not be using _myArr = mappingResult.array; because you can't be sure what the array contains. Instead you should be using the dictionary and extracting the correct type of object for your needs based on the key path.
I'll try to explain how I want the mapping done:
TOP lvl json object contains Players object which is an array of Player objects
each Player object contains an array, I want each of those objects in the array to be of an Event object (custom object).
now since I have a mapping of the Player object and i'm getting the array filled, tho instead of Event objects(which is what I want), i'm getting NSDictionary objects. thing is that I do have a mapping of my Event class. my issue is getting the RestKit to map these into the array.
I've tried adding responseDescriptors of an Event class tho i've had no luck.
Here is the Player object mapping
RKObjectMapping* playerMapping = [RKObjectMapping mappingForClass:[Player class]];
[playerMapping addAttributeMappingsFromDictionary:#{
...more here
#"activeEvents" : #"activeEvents"
}];
here is the request method
NSURL *taskURL = [NSURL URLWithString:kAppWebApiURLPath];
// Set object manager with base url
RKObjectManager *objectManager = [RKObjectManager sharedManager];
objectManager = [RKObjectManager managerWithBaseURL:taskURL];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
[objectManager.HTTPClient setDefaultHeader:#"Authorization" value:kAppWebAPIKey];
[objectManager.HTTPClient setDefaultHeader:#"Content-Type" value:#"application/json"];
RKRequestDescriptor * requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[MappingProvider inverseLoginMapping] objectClass:[LoginInfo class] rootKeyPath:nil method:RKRequestMethodPOST];
[objectManager addRequestDescriptor:requestDescriptor];
RKResponseDescriptor *playersResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:[MappingProvider playerMapping] method:RKRequestMethodGET pathPattern:nil keyPath:#"players" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:playersResponseDescriptor];
NSLog(#"%#",loginInfo.iOSDeviceToken);
[objectManager postObject:loginInfo path:kAppWebApiLoginPath parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
{}...
Update, I now need one step further of mapping, My player object contains an array of events which I successfully mapped using
[playerMapping addRelationshipMappingWithSourceKeyPath:#"activeEvents" mapping:[MappingProvider eventMapping]];
yet now each of those Event objects contains an array of Players, so its like Players -> Events -> Players.
Here is the Mapping for both Event and Player objects :
RKObjectMapping* eventMapping = [RKObjectMapping mappingForClass:[Event class]];
[eventMapping addAttributeMappingsFromDictionary:#{
#"stuffhere" : #"stuffz"
}];
RKObjectMapping* playerMapping = [RKObjectMapping mappingForClass:[Player class]];
[playerMapping addAttributeMappingsFromDictionary:#{
#"name": #"name",
#"activeEvents" : #"activeEvents"
}];
[eventMapping addRelationshipMappingWithSourceKeyPath:#"activeEvents/players" mapping:playerMapping];
now I don't get a recursive function, but how do I state in code to make that relationship
mapping of the json array to assign to my local array property ?
Remove #"activeEvents" : #"activeEvents" from the mapping and replace it with:
[playerMapping addRelationshipMappingWithSourceKeyPath:#"activeEvents" mapping:eventMapping];
You should also only have one response descriptor because the data is nested.
I have a request gives me two part of data, the json looks like this
{
"banner_content":
[
{
"activi_id":"1",
"activi_pic":
},
{
"activi_id":"2",
"activi_pic":
},
],
"categories":
[
{...},
{...},
]
}
When I write responseDescriptor to map the data, I have found I must define a model which contains banner_content array and categories array (I don't want to )
or else I have to write two responseDescriptors to do, when the request is done, I have to get the two parts of data from (RKMappingResult *)mappingResult
then get array like [mappingResult objectForKey:#"banner_content"] and [mappingResult objectForKey:#"categories"]
it's weird
below is my code
File:RCategory.m
#implementation RCategory
+ (NSDictionary *)_mapping {
return #{#"title" : #"title"};
}
+ (RKObjectMapping *)mapping {
// Setup our object mappings
RKObjectMapping *categoryMapping = [RKObjectMapping mappingForClass:[self class]];
[categoryMapping addAttributeMappingsFromDictionary:[[self class] _mapping]];
RKObjectMapping *itemMapping = [RCategoryItem mapping];
RKRelationshipMapping* relationShipMapping = [RKRelationshipMapping relationshipMappingFromKeyPath:#"content"
toKeyPath:#"items"
withMapping:itemMapping];
[categoryMapping addPropertyMapping:relationShipMapping];
return categoryMapping;
}
#end
File:RAd.m ignored
File:viewController.m
- (void)loadCategory {
// Load the object model via RestKit
RKObjectManager *objectManager = [RKObjectManager sharedManager];
RKObjectMapping *categoryMapping = [RCategory mapping];
RKResponseDescriptor *categoryResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:categoryMapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:#"category"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
RKObjectMapping *adsMapping = [RAd mapping];
RKResponseDescriptor *adResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:adsMapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:#"banner_content"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
//** DO I MUST ADD TWO RESPONSE_DESCRIPTOR FOR ONE REQUEST??**
[objectManager addResponseDescriptor:categoryResponseDescriptor];
[objectManager addResponseDescriptor:adResponseDescriptor];
[objectManager getObjectsAtPath:RPATH(CATEGORY_PATH)
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSArray* statuses = [mappingResult array];
[self.categories addObjectsFromArray:statuses];
if ([self isViewLoaded]) {
[self.tableView reloadData];
}
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
NSLog(#"Hit error: %#", error);
}];
}
Does anybody have a better way to solve my problem?
BTW I write -mapping in the Model Class, It's the easiest and best way I can think out
I would like to know how do you write the mapping.
You need 2 response descriptors because the response has 2 logically separate parts. This is fine. If you were using Core Data then you wouldn't be concerned as all your objects would simply be saved to the context and you can fetch them as required. With object mapping, if you don't care about the grouping then you can get an array of all objects from the mapping result.
Creating your mappings from data returned by the model objects is fine - but it does limit you because you can only have one source key per destination key. What happens when you have a different response for the same object with a different key that means the same as some other key (hopefully you don't, but it all depends on the server API).
So the problem is that when I'm trying to load entity from here I'm not getting things to work. My Pathpatterns seems to be wrong.
Here's my Mapping and Descriptor:
RKEntityMapping *statsMapping = [RKEntityMapping mappingForEntityForName:#"Stat" inManagedObjectStore:managedObjectStore];
[statsMapping addAttributeMappingsFromDictionary:#{
#"sort_id" : #"sortID",
#"id" : #"statID",
#"deleted" : #"deletedFlag",
#"created_at": #"createdAt",
#"updated_at": #"updatedAt"
}];
statsMapping.identificationAttributes = #[ #"statID" ];
[statsMapping addAttributeMappingsFromArray:#[ #"title"]];
RKEntityMapping *featuresMapping = [RKEntityMapping mappingForEntityForName:#"Feature" inManagedObjectStore:managedObjectStore];
[featuresMapping addAttributeMappingsFromDictionary:#{
#"sort_id" : #"sortID",
#"id" : #"featureID",
#"deleted" : #"deletedFlag",
#"created_at": #"createdAt",
#"updated_at": #"updatedAt",
}];
featuresMapping.identificationAttributes = #[ #"featureID" ];
[featuresMapping addAttributeMappingsFromArray:#[ #"title", #"value"]];
[statsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"features" toKeyPath:#"features" withMapping:featuresMapping]];
RKResponseDescriptor *statsDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:statsMapping
pathPattern: #"/api/cars/:carID/features.json"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptorsFromArray:#[newsDescriptor, catalogDescriptor, statsDescriptor]];
So when I use pathPattern:nil it works, but if no answer is returned by url it just tries to put another responsedescriptor to the response and gives me random data :)
The question is, if I have the car ID in the middle of the pattern, how should I declare it?
Thank you!
Edit1: This is how I do request:
- (void)getStats:(NSNumber *)carID
{
[[RKObjectManager sharedManager] getObjectsAtPath:[NSString stringWithFormat:#"api/cars/%#/features.json", carID]
parameters:#{#"auth_token" : [Constants authToken]}
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
RKLogInfo(#"Load complete: Stats loaded");
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
RKLogError(#"Load failed with error: %#", error);
[self showError:error withDelay:3];
}];
}
From your trace log there is some problem with your managed objects, not your mappings. Your entities are obviously defined in the model and with the correct names and with the appropriate attributes. So it looks like either you created classes for the entities but wrongly or that the object manager isn't being provided with an / the correct object store reference.
Your log contains CoreData: error: Failed to call designated initializer on NSManagedObject class 'Stat' and [<Stat 0xa68b7c0> valueForUndefinedKey:]: the entity (null) is not key value coding-compliant for the key "statID". which are both issues to do with the entity creation / referencing. It isn't clear how that happens based on the code posted.
Try changing your path pattern to remove the leading slash:
#"api/cars/:carID/features.json"
when defining your statsDescriptor as that can cause the pattern to not match.
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);
}];
}
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.