RestKit one-to-many relationship - ios

I am having a problem with one-to-many relationships in my CoreData-backed entities.
My JSON looks like this:
[
{
"uuid": "0",
"title": "humor",
"providers": [{
"provider": {
"uuid": "1",
"title": "dilbert",
"sections": [{
"section": { "uuid":"1990", "title": "daily"},
"section": { "uuid":"1991", "title": "weekly"},
}],
},
"provider": {
"uuid": "2",
"title": "wizard of id",
},
"provider": {
"uuid": "3",
"title": "bc",
},
},],
},
{
"uuid": "22",
"title": "photo",
},
]
In my case, the top level object is a category, which can have 0..* providers, which can have 0..* sections.
Update: Just to wrap this up, the root of this problem is that the JSON is malformed. There should not be trailing commas in the dictionaries.
My RestKit mappings are:
RKEntityMapping *sectionMapping = [RKEntityMapping mappingForEntityForName:#"DHSection" inManagedObjectStore:managedObjectStore];
[sectionMapping addAttributeMappingsFromDictionary:#{
#"section.uuid" : #"uuid",
#"section.title" : #"title",
}];
sectionMapping.identificationAttributes = #[#"uuid"];
RKEntityMapping *providerMapping = [RKEntityMapping mappingForEntityForName:#"DHProvider" inManagedObjectStore:managedObjectStore];
[providerMapping addAttributeMappingsFromDictionary:#{
#"provider.uuid" : #"uuid",
#"provider.title" : #"title",
}];
providerMapping.identificationAttributes = #[#"uuid"];
RKRelationshipMapping *sectionRelationship = [RKRelationshipMapping relationshipMappingFromKeyPath:#"provider.sections" toKeyPath:#"sections" withMapping:sectionMapping];
[sectionRelationship setAssignmentPolicy:RKUnionAssignmentPolicy];
[providerMapping addPropertyMapping:sectionRelationship];
RKEntityMapping *categoryMapping = [RKEntityMapping mappingForEntityForName:#"DHCategory" inManagedObjectStore:managedObjectStore];
[categoryMapping addAttributeMappingsFromDictionary:#{
#"uuid" : #"uuid",
#"title" : #"title",
}];
categoryMapping.identificationAttributes = #[#"uuid"];
RKRelationshipMapping *providerRelationship = [RKRelationshipMapping relationshipMappingFromKeyPath:#"providers" toKeyPath:#"providers" withMapping:providerMapping];
[providerRelationship setAssignmentPolicy:RKUnionAssignmentPolicy];
[categoryMapping addPropertyMapping:providerRelationship];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:categoryMapping method:RKRequestMethodGET pathPattern:#"category/list.json" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:responseDescriptor];
[objectManager addFetchRequestBlock:^NSFetchRequest *(NSURL *URL) {
RKPathMatcher *pathMatcher = [RKPathMatcher pathMatcherWithPattern:#"category/list.json"];
NSDictionary *argsDict = nil;
BOOL match = [pathMatcher matchesPath:[URL relativePath] tokenizeQueryStrings:NO parsedArguments:&argsDict];
if (match) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"DHCategory"];
fetchRequest.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"uuid" ascending:YES]];
return fetchRequest;
}
return nil;
}];
When RestKit parses the response, it correctly parses two categories, but it only loads the first provider and section.
Update 1 Nov 2013: The updated mapping above corrected the relationship mapping for sections to map from key path "provider.sections" vs "sections".
The problem may likely stem from the difference between the response and what is being mapped.
2013-11-01 10:40:06.670 xyz[58006:1003] D restkit.object_mapping:RKMapperOperation.m:377 Executing mapping operation for representation: (
{
providers = (
{
provider = {
sections = (
{
section = {
title = daily;
uuid = 1990;
};
}
);
title = dilbert;
uuid = 1;
};
}
);
title = humor;
uuid = 0;
},
...
while the response was
response.body=[
{
"uuid": "0",
"title": "humor",
"providers": [
{
"provider": {
"uuid": "1",
"title": "dilbert",
"sections": [{
"section": { "uuid":"1990", "title": "daily"},
"section": { "uuid":"1991", "title": "weekly"},
}
],
},
"provider": {
"uuid": "3",
"title": "wizard of id",
},
"provider": {
"uuid": "2",
"title": "bc",
},
},
],
},
...
Any ideas on how to fix or debug this?
Thanks,
Mike

Related

How to save object in Core Data having same value?

I am saving objects in core data which is in the below format.
{
"propertyId": 1,
"name": "some_name_1",
"userName": "some_email_1"
},
{
"propertyId": 1,
"name": "some_name_2",
"userName": "some_email_2"
},
{
"propertyId": 2,
"name": "some_name_3",
"userName": "some_email_3"
},
{
"propertyId": 2,
"name": "some_name_4",
"userName": "some_email_4"
},
{
"propertyId": 3,
"name": "some_name_5",
"userName": "some_email_5"
},
{
"propertyId": 3,
"name": "some_name6_",
"userName": "some_email_6"
}
Now, the problem is that only the last object from the group of the same propertyId is getting saved in the Core Data.
This is the code I got from the previous developer. Mapping is used to store data.
RKEntityMapping *rkEntityMapping = [RKEntityMapping mappingForEntityForName:#"PropertyAssignee" inManagedObjectStore:self.managedObjectStore];
rkEntityMapping.identificationAttributes = #[#"propertyId"];
[rkEntityMapping addAttributeMappingsFromDictionary:#{
#"propertyId": #"propertyId",
#"name": #"name",
#"userName": #"userName"}];
[rkEntityMapping addConnectionForRelationship:#"property" connectedBy:#{#"propertyId": #"objId"}];
RKObjectMapping *syncMapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
[syncMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"propertyAssgineeResponse" toKeyPath:#"propertyAssgineeResponse" withMapping:propertAssigneeMapping]];
RKResponseDescriptor *responseDescriptorProperties = [RKResponseDescriptor responseDescriptorWithMapping:syncMapping method:RKRequestMethodAny pathPattern:#"/api/sync" keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]];
[self.objectManager addResponseDescriptor:responseDescriptorProperties];
I am not getting any clue of why this is happening.

How to map objects in Restkit when response for a key changes between dictionary and string?

I am having difficulty mapping a object in Restkit which is nested. The response is as follows:
{
"status": "SUCCESS",
"message": "Newsfeeds loaded successfully",
"links": {
"self": ""
},
"newsfeeds": [
{
"type": "post",
"content": {
"id": "39ff4ea6-71d4-4d9c-872b-94d878ec54fd",
"owner_id": "75c72102-d7dc-44c6-bb62-c48b487897c4",
"owner_firstname": "Goran",
"owner_lastname": "Don",
"owner_profilephoto": "88f5930b-e779-47ac-86b7-4f31cd0d82aa.jpg",
"contents": [
{
"type": "text",
"content": "Being the richest man in the cemetery doesn't matter to me. Going to bed at night saying we've done something wonderful, that's what matters to me.\n Sometimes when you innovate, you make mistakes. It is best to admit them quickly, and get on with improving your other innovations"
},
{
"type": "url",
"content": {
"title": "Beautiful landscape view Of New Zealand and Australia is available here",
"position": "landscape",
"url": "39ff4ea6-71d4-4d9c-872b-94d878ec54fd/Img_62151fbd-119f-4a84-91a9-f37bfa6293db.jpg"
}
}
],
"postdatetime": "May 18 2016 12:18:54.442 UTC",
"selfLike": false,
"likes": 0,
"comments": 0,
"starred": "false"
}
},
{
"type": "post",
"content": {
"id": "7117d132-1811-4ac7-bfaf-bbb397eefb10",
"owner_id": "75c72102-d7dc-44c6-bb62-c48b487897c4",
"owner_firstname": "Goran",
"owner_lastname": "Don",
"owner_profilephoto": "88f5930b-e779-47ac-86b7-4f31cd0d82aa.jpg",
"contents": [
{
"type": "text",
"content": "Hi this is a test"
}
],
"postdatetime": "May 18 2016 11:42:24.978 UTC",
"selfLike": false,
"likes": 0,
"comments": 0,
"starred": "false"
}
},
{
"type": "post",
"content": {
"id": "fbeb20cf-c87b-40db-9753-9fa32aac921a",
"owner_id": "75c72102-d7dc-44c6-bb62-c48b487897c4",
"owner_firstname": "Goran",
"owner_lastname": "Don",
"owner_profilephoto": "88f5930b-e779-47ac-86b7-4f31cd0d82aa.jpg",
"contents": [
{
"type": "url",
"content": {
"title": "",
"position": "potrait",
"url": "fbeb20cf-c87b-40db-9753-9fa32aac921a/Img_23771f1c-bb22-4335-ae3f-4e83d97c5c3c.jpg"
}
},
{
"type": "url",
"content": {
"title": "",
"position": "landscape",
"url": "fbeb20cf-c87b-40db-9753-9fa32aac921a/Img_a77e8349-b531-4d9d-944e-88971298c6fa.jpg"
}
}
],
"postdatetime": "May 18 2016 07:39:07.937 UTC",
"selfLike": true,
"likes": 1,
"comments": 0,
"starred": "false"
}
},
{
"type": "post",
"content": {
"id": "71e005a1-71ec-4131-8131-bfd38337ec6a",
"owner_id": "75c72102-d7dc-44c6-bb62-c48b487897c4",
"owner_firstname": "Goran",
"owner_lastname": "Don",
"owner_profilephoto": "88f5930b-e779-47ac-86b7-4f31cd0d82aa.jpg",
"contents": [
{
"type": "url",
"content": {
"title": "",
"position": "potrait",
"url": "post/71e005a1-71ec-4131-8131-bfd38337ec6a/Img_600f6fd0-14f9-4bf3-af6e-f36b7ffedbc6.jpg"
}
}
],
"postdatetime": "May 18 2016 07:27:53.278 UTC",
"selfLike": false,
"likes": 0,
"comments": 0,
"starred": "false"
}
}
]
}
From the above, need to reach "content" within "contents" array. "content" sometimes return string and sometimes dictionary. I am trying to map as follows:
RKObjectMapping * newsFeedMappingObject = [RKObjectMapping mappingForClass:[NewsFeed class]];
[newsFeedMappingObject addAttributeMappingsFromDictionary:#{#"type" : #"type",}];
RKObjectMapping * responseMappingObject = [RKObjectMapping mappingForClass:[ResponseMessage class]];
[responseMappingObject addAttributeMappingsFromDictionary:#{uStatus: #"status",uMessage: #"message",}];
[responseMappingObject addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"newsfeeds"
toKeyPath:#"newsfeeds"
withMapping:newsFeedMappingObject]];
RKObjectMapping * contentMappingObject = [RKObjectMapping mappingForClass:[Content class]];
[contentMappingObject addAttributeMappingsFromDictionary:#{#"id": #"contentId"}];
[newsFeedMappingObject addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"content"
toKeyPath:#"cont"
withMapping:contentMappingObject]];
RKObjectMapping * postContentMappingObject = [RKObjectMapping mappingForClass:[PostContent class]];
[postContentMappingObject addAttributeMappingsFromDictionary:#{#"type": #"type",}];
[contentMappingObject addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"contents"
toKeyPath:#"contents"
withMapping:postContentMappingObject]];
RKObjectMapping * postMappingObject = [RKObjectMapping mappingForClass:[Post class]];
[postMappingObject addAttributeMappingsFromDictionary:#{#"title":#"title"}];
[postContentMappingObject addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"content"
toKeyPath:#"post"
withMapping:postMappingObject]];
When I run it the app crashes with log as:** * Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFString 0x7fa302826d30> valueForUndefinedKey:]: this class is not key value coding-compliant for the key title.'
Trying my hands on dynamic mapping. But I think I'm doing it wrong:
RKObjectMapping * newsFeedMappingObject = [RKObjectMapping mappingForClass:[NewsFeed class]];
[newsFeedMappingObject addAttributeMappingsFromDictionary:#{
#"type" : #"type",
}];
RKObjectMapping * responseMappingObject = [RKObjectMapping mappingForClass:[ResponseMessage class]];
[responseMappingObject addAttributeMappingsFromDictionary:#{
uStatus: #"status",
uMessage: #"message",
}];
[responseMappingObject addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"newsfeeds"
toKeyPath:#"newsfeeds"
withMapping:newsFeedMappingObject]];
RKObjectMapping * contentMappingObject = [RKObjectMapping mappingForClass:[Content class]];
[contentMappingObject addAttributeMappingsFromDictionary:#{
#"id": #"contentId",
}];
[newsFeedMappingObject addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"content"
toKeyPath:#"cont"
withMapping:contentMappingObject]];
RKObjectMapping * postContentMappingObject = [RKObjectMapping mappingForClass:[PostContent class]];
[postContentMappingObject addAttributeMappingsFromDictionary:#{
#"type": #"type",
}];
[contentMappingObject addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"contents"
toKeyPath:#"contents"
withMapping:postContentMappingObject]];
RKObjectMapping * postMappingObject = [RKObjectMapping mappingForClass:[Post class]];
[postMappingObject addAttributeMappingsFromDictionary:#{
#"title" : #"title"
}];
RKDynamicMapping* dynamicMapping = [RKDynamicMapping new];
[postMappingObject addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"post" toKeyPath:#"content" withMapping:dynamicMapping]];
// Connect a response descriptor for our dynamic mapping
RKResponseDescriptor *dynamicResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:dynamicMapping method:RKRequestMethodAny pathPattern:nil keyPath:#"content" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[[RKObjectManager sharedManager] addResponseDescriptor:dynamicResponseDescriptor];
// Option 2: Configure the dynamic mapping via a block
[dynamicMapping setObjectMappingForRepresentationBlock:^RKObjectMapping *(id representation) {
if ([[representation valueForKey:#"content"] isKindOfClass:[NSDictionary class]]) {
return postMappingObject;
}
return nil;
}];
[postContentMappingObject addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"content"
toKeyPath:#"post"
withMapping:postMappingObject]];
NSString *urlString = [NSString stringWithFormat:#"%#%#%#&userId=%#&startDate=%#&count=%d&scanForward=%#",uServiceUrl,uGetNewsFeedsRequest,accountId,userId,startDate,count,isForward];
NSString* encodedUrl = [urlString stringByAddingPercentEncodingWithAllowedCharacters:
[NSCharacterSet URLQueryAllowedCharacterSet]];
NSURL *getNewsRequestUrl = [NSURL URLWithString:encodedUrl];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMappingObject method:RKRequestMethodAny pathPattern:nil keyPath:#"" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[self getWithUrlString:getNewsRequestUrl withMappingObject:responseMappingObject andResponseDescriptors:#[responseDescriptor, dynamicResponseDescriptor] withSuccess:successBlock withFail:failBlock];
You need to change postMappingObject to be an instance of RKDynamicMapping which checks the data that it's provided with to see what it is and returns an appropriate mapping that will actually be used to process that data. The check is to determine if the payload is a string or a dictionary.

RestKit sideload JSON association without CoreData

I am trying to get RestKit to use some sideloaded associations in my JSON, the JSON looks like the following:
{
"companies": [
{
"id": 1,
"name": "420...",
"street": "420 Kush St.",
"city": "Seattle",
"state": "WA",
"zip_code": "12345"
}
],
"user": {
"id": 1,
"email": "test#example.com",
"token": "1234567890",
"company_id": 1
}
}
I am not using the CoreData integration and need the company attribute of my user to fill in with the company found in the "companies" key of the above JSON. My current user mapping looks like:
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[GBUser class]];
[mapping addAttributeMappingsFromDictionary:#{
#"id": #"userId",
#"email": #"email"
}];
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
return [RKResponseDescriptor responseDescriptorWithMapping:mapping method:RKRequestMethodAny pathPattern:#"/api/v0/me" keyPath:#"user" statusCodes:statusCodes];
The simplest option is to write a couple of lines of code in the success block to associate the objects in the mapping result. You would use 2 response descriptors to map the users and companies separately and then combine them after the mapping has completed.

RestKit object mapping of list within list

the json api has a list of posts, and each post has a list of users that like this post.
post, user each has an entity in core data.
how RestKit do object mapping.
{
posts:[
{
"postid": 1,
"posttext": "post1",
"likeusers":[
{
"uid": 1,
"username": "user1"
},
{
"uid": 2,
"username": "user2"
}
]
},
{
"postid": 2,
"posttext": "post2",
"likeusers":[
{
"uid": 1,
"username": "user1"
},
{
"uid": 2,
"username": "user2"
}
]
}
]
}
Here is how to do the mapping:
// User mapping
RKEntityMapping *userMapping = [RKEntityMapping mappingForEntityForName:#"User"
inManagedObjectStore:yourManagedObjectStore];
userMapping.identificationAttributes = #[#"userID"];
[userMapping addAttributeMappingsFromDictionary:#{
#"uid": #"userID",
#"username": #"username",
}];
// Post mapping
RKEntityMapping *postMapping = [RKEntityMapping mappingForEntityForName:#"Post"
inManagedObjectStore:yourManagedObjectStore];
postMapping.identificationAttributes = #[#"postID"];
[postMapping addAttributeMappingsFromDictionary:#{
#"postid": #"postID",
#"posttext": #"postText",
}];
[postMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"likeusers"
toKeyPath:#"likeUsers" withMapping:userMapping]];
Hope it helps.

Relationship Mapping Restkit

I have two tables say Articles and Authors. Each Article have many authors and same author can be come in multiple Articles. My Json response is looks like this.
{
"Articles": [
{
"artName": "ABC",
"artId": "1",
"Authors": [
{
"autName": "James",
"autId": "200",
"email": "james#xyz.com"
},
{
"autName": "Mark",
"autId": "201",
"email": "mark#xyz.com"
},
{
"autName": "Robert",
"autId": "202",
"email": "robert#xyz.com"
}
]
},
{
"artName": "BCD",
"artId": "2",
"Authors": [
{
"autName": "James",
"autId": "200",
"email": "james#xyz.com"
},
{
"autName": "Ben",
"autId": "204",
"email": "ben#xyz.com"
},
{
"autName": "Rayon",
"autId": "205",
"email": "rayon#xyz.com"
}
]
}
]
}
Mapping:
RKObjectManager *manager = [[RestKit sharedDataManager] objectManager];
RKEntityMapping *ArticleMapping = [RKEntityMapping mappingForEntityForName:#"Articles" inManagedObjectStore:manager.managedObjectStore];
ArticleMaping.identificationAttributes = #[#"artId"];
ArticleMaping addAttributeMappingsFromDictionary:#{
#"artId": #"artId",
}];
RKEntityMapping *AuthorMapping = [RKEntityMapping mappingForEntityForName:#"Authors" inManagedObjectStore:manager.managedObjectStore];
AuthorMapping.identificationAttributes = #[#"autId"];
AuthorMapping addAttributeMappingsFromDictionary:#{
#"autId": #"autId",
#"autName" : #"autName",
#"email" : #"email"
}];
[ArticleMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"Authors" toKeyPath:#"Authors" withMapping:AuthorMapping]];
I have made a relationship for Articles and Authors. It is mapping correctly, but when i try to fetch the authors for "artName" ABC. i am getting only 2 authors. but when i try to fetch data for "artName" BCD. i am getting 3 authors. when checked in DB, relationship key for record "James" is overwritten by record of BCD.
How can i access all the authors for both the articles.
Note: I am using Restkit 0.20
Thank you
The relationship between the 2 objects needs to be many to many or the new assignment will explicitly break the old assignment.
When you create your RKRelationshipMapping, ensure that you set the assignmentPolicy to RKUnionAssignmentPolicy. The default is RKSetAssignmentPolicy which could be removing the existing setting.

Resources