I am using RestKit to map the following JSON to core data.
This is my model:
this is my methods:
RKEntityMapping *userMapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([User class]) inManagedObjectStore:managedObjectStore];
userMapping.identificationAttributes = #[#"id"];
[userMapping addAttributeMappingsFromDictionary:#{
#"birthday":#"birthday",
#"email":#"email",
#"facebook_token":#"facebook_token",
#"first_name":#"first_name",
#"gender":#"gender",
#"interested_in":#"interested_in",
#"last_name":#"last_name",
#"password":#"password",
#"m8_status":#"m8_status",
#"status":#"status",
#"languages": #"languages",
#"hometown":#"hometown",
#"location":#"location",
#"avatar_url":#"avatar_url",
#"mood":#"mood"
}];
RKEntityMapping *findUserMapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([FindUser class]) inManagedObjectStore:managedObjectStore];
findUserMapping.identificationAttributes = #[#"id"];
[findUserMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"users" toKeyPath:#"users" withMapping:userMapping]];
[manager addResponseDescriptorsFromArray:#[
[RKResponseDescriptor responseDescriptorWithMapping:findUserMapping
pathPattern:#"/v1/users"
keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]
]];
This is getting objects from example JSON:
{
"users": [
{
"avatar_url": null,
"birthday": "04/11/1984",
"email": "antonina.duminskaya#wildermancruickshank.co.uk", "first_name": "Antonina",
"gender": "female",
"hometown": null,
"id": "5264ee384d61632c4b2f0000",
"interested_in": [
"male",
"female" ],
"languages": [], "last_name": "Duminskaya", "location": null, "m8_status": null,
"mood": null,
"status": null
} ]
}
here:
[objectManager getObjectsAtPath:#"/v1/users" parameters:#{#"session_id":session.id} success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
FindUser *findUsers = [[self.fetchedFindUsersResultsController fetchedObjects] lastObject];
NSLog(#"%#", findUsers.users);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
}];
I have message in log:
Relationship 'users' fault on managed object (0x8e964a0) <FindUser: 0x8e964a0> (entity: FindUser; id: 0x8da0790 <x-coredata://083BB731-FDD7-4A35-A174-724F50862A02/FindUser/p1> ; data: {
id = nil;
users = "<relationship fault: 0x8deb260 'users'>";
})
And In mappingResults I have all results about json
Core Data is representing the relationship initially as fault saving memory by not loading all the object graph at once.
https://developer.apple.com/library/ios/documentation/cocoa/conceptual/CoreData/Articles/cdFaultingUniquing.html
Also, do not use the reserved word 'id' as the Restkit identification attribute for your managed objects. Try using findUserId and userId and update your mappings accordingly.
Related
I have JSON of following kind
{
"Client": {
"FirstName": "String",
"LastName": "String",
"Email": "String",
"Password": "String",
"PhoneNumber": "String",
"Token": "String"
},
"ErrorMessage": "String",
"FriendlyMessage": "String"
}
I know that while mapping if I specify a key path then RestKit matches fields from object specified, in this case Client, to the code I am using for that is as follows
[responseMapping addAttributeMappingsFromDictionary:#{
#"FirstName": #"FirstName",
#"LastName": #"LastName",
#"Email": #"Email",
#"PhoneNumber": #"PhoneNumber",
#"Token": #"Token",
#"Password": #"Password",
#"ErrorMessage": #"ErrorMessage"}];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping
method:RKRequestMethodPOST
pathPattern:nil
keyPath:#"Client"
statusCodes:[NSIndexSet indexSetWithIndex:200]];
But I have two additional fields to map which aren't under client object. How do I map ErrorMessage and FriendlyMessage fields ?
Generally you wouldn't want to map them into your client object, you would want to map them into some other object. You would usually use a different object and response mapping, where the response mapping uses a different key path.
If for some reason you did want them in the same object then you could look at using the #metadata / #parent to get to the parent (https://github.com/RestKit/RestKit/pull/1435).
I have the following JSON structure which I want to map with RestKit in CoreData:
[
{
"id": 1,
"type": "folder",
"name": "Folder 1",
"children": [
{
"id": 1,
"type": "document",
"name": "Document 1"
},
{
"id": 2,
"type": "folder",
"name": "Folder 2",
"children": [
{
"id" : 2,
"type": "document",
"name": "Document 2"
}
]
}
]
}
]
It's a typical filesystem structure where a folder can contain other folders or documents.
I have two CoreData entities: Folder and Document.
The Folder entity has the following relationships:
Here is my Mapping Code:
RKEntityMapping* foldersMapping = [RKEntityMapping mappingForEntityForName:#"Folder" inManagedObjectStore:managedObjectStore];
foldersMapping.identificationAttributes = #[ #"objectId" ];
[foldersMapping addAttributeMappingsFromDictionary:#{#"id" : #"objectId",
#"name" : #"name"}];
RKEntityMapping* documentsMapping = [RKEntityMapping mappingForEntityForName:#"Document" inManagedObjectStore:managedObjectStore];
documentsMapping.identificationAttributes = #[ #"objectId" ];
[documentsMapping addAttributeMappingsFromDictionary:#{#"id" : #"objectId",
#"name" : #"name"}];
In my dynamic mapping I use the type of the object to decide if it is a document or a folder.
RKDynamicMapping* dynamicMapping = [RKDynamicMapping new];
[dynamicMapping setObjectMappingForRepresentationBlock:^RKObjectMapping *(id representation) {
if ([[representation valueForKey:#"type"] isEqualToString:#"Folder"]) {
return foldersMapping;
} else if ([[representation valueForKey:#"type"] isEqualToString:#"Document"]) {
return documentsMapping;
}
return nil;
}];
Now I added the dynamic mapping as relationship mapping to the folder mapping for the property children two times.
[foldersMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"children" toKeyPath:#"children" withMapping:dynamicMapping]];
[foldersMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"children" toKeyPath:#"documents" withMapping:dynamicMapping]];
And here is my Problem: All this works, but I got the following Warnings:
RestKitNesterFolderTestProject[9969:3a03] W
restkit.object_mapping:RKMappingOperation.m:554 WARNING: Failed
mapping nested object: The operation couldn’t be completed. (Cocoa
error 1550.)
I think the Problem is that I doubled the mapping for the relationship children, so RestKit tries to map each child two times, once as Folder and once as Document.
Any help is appreciated.
I am searching a way to map 2 different arrays with the following JSON
"establishments": [
{
"id": 1,
"name": "Boudin Sourdough Bakery and Cafe",
"address": "1 Stockton St",
"zip_code": "94108",
"city": "San Francisco",
"country": "us",
"phone_number": "",
"position": {
"latitude": 37.78590500000001,
"longitude": -122.406289
},
"distance_from": 13.75783097391759
}
],
"places": [
{
"id": 3,
"name": "Private Place",
"position": {
"latitude": 37.78583400000001,
"longitude": -122.406417
},
"is_private": false,
"distance_from": 0
}
]
I have 2 different mappings. One for establishment and another for place. I found some help with the documentation but it's working only for PUT or POST requests.
Currently, I have the following request
[session.objectManager getObjectsAtPathForRouteNamed:APICallSearchSearchPlaceRouteName object:nil parameters:params success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSArray * resultPlaces=mappingResult.array;
[delegate APICallDidSearch:resultPlaces];
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSError* myError=[self catchCustomErrorInRKError:error setDomain:#"Search"];
if (myError.code == NSURLErrorCancelled) return;
[delegate APICallDidFailSearch:myError];
}];
Mapping & Mapping Descriptor
RKEntityMapping *establishmentMapping = [APICallEstablishment RKGetEstablishmentMappingForManagedObjectStore:self.appDelegate.managedObjectStore];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:establishmentMapping
method:RKRequestMethodGET
pathPattern:APICallSearchSearchPlacePattern
keyPath:#"establishments"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[session.objectManager addResponseDescriptor:responseDescriptor];
I tried to adapt the example to my case with that (just before my request written above)
RKManagedObjectRequestOperation *operation = [session.objectManager appropriateObjectRequestOperationWithObject:establishmentMapping method:RKRequestMethodGET path:#"/whatever" parameters:nil];
operation.targetObject = nil;
//Does it still exist ? (XCode says an error)
//operation.targetObjectID = nil;
[session.objectManager enqueueObjectRequestOperation:operation];
Thank you in advance for your help.
Using session.objectManager getObjectsAtPathForRouteNamed... is a good option (better than trying to use RKManagedObjectRequestOperation directly).
You need to create another response descriptor, like responseDescriptor, but for places key path and mapping.
This is the JSON return, I'm trying to map the "result"->"id" and add it to the Slides Entity in Core Data, everything so far is mapping I just don't know how to access that parent id and map it into the Slides Entity
{
"result": {
"id": 47,
"type": "Slidedeck",
"name": "Brendan Demo",
"version": "0.24",
"slides": [
{
"id": 767,
"label": "",
"order": 1,
"notes": "",
"file": "slide-38.jpg",
"url": "http://api.testapp.com/v4/resources/767/download/slide",
"thumb": "http://api.testapp.com/v4/resources/767/download/slide?thumb=true",
"components": {
"hotspots": []
}
},
{
"id": 768,
"label": "",
"order": 2,
"notes": "",
"file": "slide-54.png",
"url": "http://api.testapp.com/v4/resources/768/download/slide",
"thumb": "http://api.testapp.com/v4/resources/768/download/slide?thumb=true",
"components": {
"hotspots": []
}
}
]
},
"status": {
"code": 200,
"message": "OK"
}
}
My Entity Mapping and RKResponsedescriptor is as follows:
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:#"Slides"
inManagedObjectStore:[RKManagedObjectStore defaultStore]];
[mapping addAttributeMappingsFromDictionary:#{#"id": #"slideID",
#"label": #"slideLabel",
#"order": #"slideOrder",
#"notes": #"slideNotes",
#"file": #"slideFile",
#"url": #"slideURL",
#"thumb": #"slideThumb"
}];
slidesResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:resourcesEntityMapping method:RKRequestMethodAny pathPattern:[NSString stringWithFormat:#"%#%#", VERSION, #"/resources/:slideDeckID"] keyPath:#"result.slides" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
Any ideas? I've omitted all my previous attempts to accomplish this to keep the code clear. So currently there is no failed attempt code here to grab that parent result id.
EDIT:
I've added the #parent to the RKEntityMapping as mentioned
[mapping addAttributeMappingsFromDictionary:#{#"#parent.id":#"slideDeckID",
#"id": #"slideID",
#"label": #"slideLabel",
#"order": #"slideOrder",
#"notes": #"slideNotes",
#"file": #"slideFile",
#"url": #"slideURL",
#"thumb": #"slideThumb"
}];
also tried:
[mapping addAttributeMappingsFromDictionary:#{#"#parent.slide.id":#"slideDeckID",
#"id": #"slideID",
#"label": #"slideLabel",
#"order": #"slideOrder",
#"notes": #"slideNotes",
#"file": #"slideFile",
#"url": #"slideURL",
#"thumb": #"slideThumb"
}];
but I always get the following trace messages:
Did not find mappable attribute value keyPath '#parent.id'
or
Did not find mappable attribute value keyPath '#parent.result.id'
Any ideas what I'm doing wrong.
EDIT
Checked my logs, when I set the keypath to the following:
reourcesResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:resourcesEntityMapping method:RKRequestMethodGET pathPattern:[NSString stringWithFormat:#"%#%#", VERSION, #"/resources/:slideDeckID"] keyPath:#"result.slides" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
RestKit logs the following:
restkit.object_mapping:RKMapperOperation.m:231 Asked to map source object {
components = {
hotspots = (
);
};
file = "slide-54.png";
id = 768;
label = "";
notes = "";
order = 2;
thumb = "http://api.idetailapp.com/v4/resources/768/download/slide?thumb=true";
url = "http://api.idetailapp.com/v4/resources/768/download/slide";
} with mapping <RKEntityMapping:0x8e30af0 objectClass=Resources propertyMappings=(
"<RKAttributeMapping: 0x8e31b50 thumb => slideThumb>",
"<RKAttributeMapping: 0x8e31a80 #parent.id => slideDeckID>",
"<RKAttributeMapping: 0x8e31aa0 id => slideID>",
"<RKAttributeMapping: 0x8e31ad0 label => slideLabel>",
"<RKAttributeMapping: 0x8e31bb0 order => slideOrder>",
"<RKAttributeMapping: 0x8e31a50 notes => slideNotes>",
"<RKAttributeMapping: 0x8e31c00 file => slideFile>",
"<RKAttributeMapping: 0x8e31cd0 url => slideURL>"
)>
Even though my response json contains the parent id element, it doesn't seem to be there to parse:
{
result = {
id = 47;
name = "Russell Demo";
slides = (
{
components = {
hotspots = (
);
};
file = "slide-38.jpg";
id = 767;
label = "";
notes = "";
order = 1;
thumb = "http://api.idetailapp.com/v4/resources/767/download/slide?thumb=true";
url = "http://api.idetailapp.com/v4/resources/767/download/slide";
},
{
components = {
hotspots = (
);
};
file = "slide-54.png";
id = 768;
label = "";
notes = "";
order = 2;
thumb = "http://api.idetailapp.com/v4/resources/768/download/slide?thumb=true";
url = "http://api.idetailapp.com/v4/resources/768/download/slide";
}
);
type = Slidedeck;
version = "0.24";
};
status = {
code = 200;
message = OK;
};
}
I've checked my RestKit version and get the same results when working with Development and Master branch. Verified that #parent is provided in this release in the RKMappingOperation class. Anything I'm missing here?
Edit:
Added the following relationship to my slides Entity mapping, but I'm still not able to get the container "result" data
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:#"Slides"
inManagedObjectStore:[RKManagedObjectStore defaultStore]];
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"result"
toKeyPath:#"slides"
withMapping:mapping]];
[mapping addAttributeMappingsFromDictionary:#{#"#parent.id":#"slideDeckID",
#"id": #"slideID",
#"label": #"slideLabel",
#"order": #"slideOrder",
#"notes": #"slideNotes",
#"file": #"slideFile",
#"url": #"slideURL",
#"thumb": #"slideThumb"
}];
The latest version of RestKit defines the #parent metadata key which is available during mapping. So you can add a mapping like #parent.id : #"..." to extract the result id while mapping the slide content.
I have the following JSON structure:
{
"category": "MyCategory"
"objects": [
{ "id": 1, "name": "A" },
{ "id": 2, "name": "B" },
{ "id": 3, "name": "C" }
]
}
I'm mapping each object to a separate Core Data entity like this:
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:#"Object" inManagedObjectStore:manager.managedObjectStore];
mapping.identificationAttributes = #[ #"id" ];
[mapping addAttributeMappingsFromDictionary:#{ #"name": #"name" }];
How do I configure the mapping to store their shared category on each object?
The thing I would like is to be able to traverse upwards in the JSON like this:
[mapping addAttributeMappingsFromDictionary:#{ #"name": #"name", #"PARENT.category": #"category" }];
As of now (RestKit 0.20.1), there is no way to do this by using the mapping engine.
FUTURE
There is a new metadata feature under development making it possible to access the parent object:
[mapping addAttributeMappingsFromDictionary:#{ #"name": #"name", #"#metadata.parentObject.category": #"category" }];
PRESENT
I'm modifying my deserialized response using willMapDeserializedResponseBlock. I've added a category on RKObjectManager to make it easy to modify the response:
https://gist.github.com/gunnarblom/5677324