I have get API which returns following data
{
"id": "45832",
"name": "test name",
"parentId":"xyz",
//other fields
}
I am able to save this data using restkit. I have another api to get changes which returns only updated fields. for example
{
"id": "45832",
"name": "test name2",
//other updated fields
}
I want this data to update existing object in db. But in my case it sets parentId and other missing attributes to null. I have set identification attribute in entity mapping.
EDIT:
Here is the class for managed object with entity mapping.
#import "ZEntity.h"
#import "ZContent.h"
#import "ZThumb.h"
#implementation ZEntity
#dynamic availableFrom;
#dynamic dueDate;
#dynamic entityId;
#dynamic name;
#dynamic perCompleted;
#dynamic state;
#dynamic type;
#dynamic thumb;
#dynamic contents;
#dynamic completedOn;
#dynamic totalScore;
#dynamic parent;
#dynamic settings;
+ (RKEntityMapping *)map{
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([self class]) inManagedObjectStore:[RKManagedObjectStore defaultStore]];
mapping.persistentStore = [RKManagedObjectStore defaultStore].persistentStoreCoordinator.persistentStores.firstObject;
NSAssert(mapping.persistentStore != nil, #"Requires Persistent Store");
mapping.identificationAttributes = #[#"entityId"];
[mapping addAttributeMappingsFromDictionary:#{
#"id" : #"entityId",
#"type" : #"type",
#"name" : #"name",
#"dueDate" : #"dueDate",
#"state" : #"state",
#"availableFrom" :#"availableFrom",
#"percCompletion" : #"perCompleted",
#"completedOn" : #"completedOn",
#"totalScore" : #"totalScore"
}];
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"thumb" toKeyPath:#"thumb" withMapping:[ZThumb map]]];
return mapping;
}
#end
Response descriptor
RKResponseDescriptor* entitiesD = [RKResponseDescriptor responseDescriptorWithMapping:[ZEntity map]
method:RKRequestMethodAny pathPattern:nil keyPath:#"objects"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
Json response
{
"total": 9,
"start": 1,
"count": 10,
"objects": [{
"id": "636488166759829858",
"type": "UPDATE",
"name": "copy1",
"canReattempt": false,
"score": 0,
"totalScore": 300,
"thumb": {
"id": "THUMB_QUPDATE4",
"expiry": 1472545125,
"url_180_120": "//s3-ap-southeast-1.amazonaws.com/mtgame-cdn.mindtickle.com/allaboard4.0/thumb/Q-Update-Thumbnail_04.png?AWSAccessKeyId=AKIAJYDLQOOEIA6NZLPQ&Expires=1472545125&Signature=To8a5SN7CoCiWDmmtXLkyANKulw%3D"
},
"dueDate": 1440845008,
"dueDateObj": {
"dueOn": 1440845008,
"dueDateExpiryAction": "NONE"
},
"state": "ACTIVE",
"percCompletion": 0.0,
"availableFrom": 1440585808,
"completedOn": 0,
"startTime": null,
"expiryTime": null,
"certificateCutOffScore": -1,
"lastPublishTime": 1440585653,
"completionCertificateEnabled": null
}]
}
In earlier calls I have set parent field for Entity class which is a relationship (I have verified this through sqlite browser too). In this json there is no parent returned. Once this call finished, parent is set to null. I have to keep existing parent value.
Related
I am having trouble mapping an array of objects (comments) in array of parent objects (requests) from JSON via RestKit's mapping functionality.
All my data returns properly, but for some reason the comment objects are never populated!
See my code below:
request.json:
{
"data": {
"priorityRequests": [
{
"id": 123456,
"title": "Request 1",
"comments": [
{
"author": "John Smith",
"content": "This is a comment"
}, {
"author": "Jane Smith",
"content": "This is another comment"
}
]
}, {
"id": 654321,
"title": "Request 2",
"comments": [
{
"author": "John Smith",
"content": "This is a comment"
}, {
"author": "Jane Smith",
"content": "This is another comment"
}
]
}
]
}
}
Comment.h/m
#interface Comment : NSObject
#property ( strong, nonatomic ) NSString *author;
#property ( strong, nonatomic ) NSString *content;
#end
#implementation Comment
#end
Request.h/m
#import "Request.h"
#interface Request : NSObject
#property ( strong, nonatomic ) NSString *id;
#property ( strong, nonatomic ) NSString *title;
#property ( strong, nonatomic ) Comment *comments;
#end
#implementation Request
#end
RequestManager.m snippet
RKObjectMapping *requestMapping = [ RKObjectMapping mappingForClass: [ Request class ] ];
[ requestMapping addAttributeMappingsFromDictionary:#{
#"id" : #"id",
#"title" : #"versionNumber"
}];
RKObjectMapping *commentMapping = [ RKObjectMapping mappingForClass: [ Comment class ] ];
[ commentMapping addAttributeMappingsFromDictionary:#{
#"title": #"title",
#"author": #"author"
}];
// Failed attempt 1:
[ requestMapping addPropertyMapping: [ RKRelationshipMapping
relationshipMappingFromKeyPath: #"comments"
toKeyPath: #"comments"
withMapping: commentMapping ]
];
// end
// Failed attempt 2:
RKRelationshipMapping* requests_comments = [ RKRelationshipMapping
relationshipMappingFromKeyPath: #"comments"
toKeyPath: #"comments"
withMapping: commentMapping
];
[ requestMapping addPropertyMapping: requests_comments ];
// end
RequestCommunicator.m snippet
NSDictionary *mappingsDictionary = #{ "data.priorityRequest" : requestMapping };
RKMapperOperation *mapper = [ [ RKMapperOperation alloc ]
initWithRepresentation: parsedData // parsed json as above
mappingsDictionary: mappingsDictionary
];
NSError *mappingError = nil;
BOOL isMapped = [ mapper execute: &mappingError ];
// If no errors, returned array of mapped objects
if (isMapped && !mappingError) {
// All data except for comments here
// _comments = (Comment *) nil
[ self.delegate receivedResponseObject: [ mapper mappingResult ].array ];
... etc.
I found a fix to this issue, and although it might not be everyone's cup of tea, hopefully it can help someone else down the track.
In my Requests NSObject, I changed the mapping from type 'Comment' to 'NSArray':
- #property ( strong, nonatomic ) Comment *comments;
+ #property ( strong, nonatomic ) NSArray *comments;
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 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.
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