Response Descriptor for RestKit JSON Metadata - ios

I have a JSON response that returns me a list of objects, and also a timestamp value as "MetaData". The response looks something like this --
{
"access_time": 1416467865510,
"profiles" : [
{
"user_id": "bbb91ae431b",
"email": "bob#foo.corp",
"first_name": "Bob",
"last_name": "Burroughs",
"primary_phone": "16507001212"
},
{
"user_id": "ddd8d8d8d8d",
"email": "don#foo.corp",
"first_name": "Don",
"last_name": "Darko",
"primary_phone": "14154001212"
}
]
}
My RestKit descriptor code looks something like this. And this is working well, I am getting all objects.
RKEntityMapping *contactMapping = [RKEntityMapping mappingForEntityForName:#"Contact" inManagedObjectStore: managedObjectStore];
[contactMapping addAttributeMappingsFromDictionary:#{
#"user_id" : #"userId",
#"email" : #"email",
#"first_name" : #"firstName",
#"last_name" : #"lastName",
#"primary_phone" : #"primaryPhone"
}];
contactMapping.identificationAttributes = #[#"userId"];
RKResponseDescriptor *contactResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:contactMapping method:RKRequestMethodAny pathPattern:nil keyPath:#"profiles" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
The above thing works well for me.
However, I wanted to have access to the access_time field above too. How do I get access to that? I am thinking of storing that value in NSUserDefaults for later use since it is not a field that is a part of the User/Contact object. How do I do it?

try this:
[contactMapping addAttributeMappingsFromDictionary:#{
#"user_id" : #"userId",
#"email" : #"email",
#"first_name" : #"firstName",
#"last_name" : #"lastName",
#"primary_phone" : #"primaryPhone",
#"#parent.access_time" : #"accessTime",
}];
You can read more here

You could create a relationship between the parent JSON object (that contains "access_time") and the child "profiles" objects with RKRelationshipMapping.
Then instead of having your response descriptor directly accessing the keyPath:#"profiles", you can set it to keyPath:nil and access the whole JSON object including access_time and associated profiles.
You would also need to ensure you had a corresponding Entity and Relationships (in the datamodel) for the parent JSON object (you can call it whatever you like). Then back in the file with the RestKit mappings add the relationships to the parent mapping object with addPropertyMappingsFromArray:.
Then once the request is returned you can iterate through the associated profile objects of the parent JSON (assuming you have XCode create the associated NSManagedObject subclasses) with a simple:
// allObjects returns an NSArray representation of the NSSet
NSArray *profiles = [[parentObject valueForKeyPath:#"profiles"] allObjects];
Hopefully this helps.

Related

How to Rectify the Relationship Fault in CoreData While parsing RESTKIT?

I want to Store the EY_ConnectionUsage Entity value to the EY_Connection entity Cell.I have Added two Entities with Attributes and Created Relationship with name usageData.But this Shows error "<'usageData' Relationship Fault>".This is a Method I wrote to map the RESTKIT value to CoreData.
DataAccessHandler.m
+(void)createConnectionMappingWithStore:(RKManagedObjectStore *)managedObjectStore saveInDelegate:(AppDelegate *)appDelegate{
NSLog(#"appdele==>>%#",appDelegate);
RKEntityMapping *connectionMapping = [RKEntityMapping mappingForEntityForName:#"EY_Connections" inManagedObjectStore:managedObjectStore];
connectionMapping.identificationAttributes = #[#"connectionNumber"];
[connectionMapping addAttributeMappingsFromDictionary:#{ #"ServiceNo" : #"connectionServiceNumber", #"Name" : #"connectionName", #"Region" : #"connectionRegion", #"Phase" : #"connectionPhase",
#"Circle" : #"connectionCircle", #"Section" : #"connectionSection", #"Load" : #"connectionLoad",
#"Distribution" : #"connectionDistribution", #"MeterNo" : #"connectionMeterNumber", #"ConnectionNumber" : #"connectionNumber", #"Address" : #"connectionAddress", #"ServiceStatus" : #"connectionStatus"}];
RKEntityMapping *connectionUsageMapping = [RKEntityMapping mappingForEntityForName:#"EY_ConnectionUsage" inManagedObjectStore:managedObjectStore];
connectionUsageMapping.identificationAttributes = #[#"usageAssessmentDate"];
[connectionUsageMapping addAttributeMappingsFromDictionary:#{ #"assessment_date" : #"usageAssessmentDate", #"reading" : #"usageReading", #"units" : #"usageUnits", #"amount" : #"usageAmount",
#"payment_date" : #"usagePaymentDate", #"status" : #"usageStatus"}];
[connectionMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"usage" toKeyPath:#"usageData" withMapping:connectionUsageMapping]];
RKResponseDescriptor *articleListResponseDescriptor =
[RKResponseDescriptor responseDescriptorWithMapping:connectionMapping
method:RKRequestMethodGET
pathPattern:#"user_history/consumer/data.json"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)
];
[appDelegate createObjectManagerForurl:#"http://sciflare.com/energyly/api/" andAddResponseDescriptor:articleListResponseDescriptor];
[AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
}
<'usageData' Relationship Fault>
This means that the relationship data hasn't been loaded yet, because you haven't tried to use it. Logging the object isn't enough to load the data. The whole point of the faulting system is to prevent too much data being loaded into memory at the same time.
So, basically, it isn't a problem that it's a fault. When you try to use it the data will be populated and everything will be fine.
CoreData says that 'load only needed data'. If we are trying to fetch unwanted data then coredata shows 'FAULT'.
Foe more detail please go through CoreData Fault

Map only on element of a one to many relationships

I'm stuck with the following problem. I have a relationships one_to_many between a Event and Comment. One Event can have many Comment but a Comment has belongs_to only one Event.
Until here, everything is fine. Now, when I'm adding a comment, I would like to map only this new comment. That means I'm using my relationship from Comment to Moment.
I have some troubles with the mapping that I'm not able to solve. My error is at the end of this post after all the description.
I'm receiving this JSON:
"comment": {
"id": 17,
"commentable_id": 12,
"commentable_type": "Moment",
"content": "That's it ! ",
"created_at": "2014-06-20T18:17:42Z",
"updated_at": "2014-06-20T18:17:42Z",
"user_id": 1,
"creator": {
"id": 1,
"email": "test#test.com",
"firstname": "Bobby",
"lastname": "Stouket",
"gender": 0,
"created_at": "2014-04-06T17:48:11Z",
"updated_at": "2014-06-20T18:17:26Z"
}
}
Here is my comment mapping:
RKEntityMapping *commentMapping = [RKEntityMapping mappingForEntityForName:#"Comment" inManagedObjectStore:store];
commentMapping.identificationAttributes = #[ #"commentId"];
[commentMapping addAttributeMappingsFromDictionary:#{
#"id" : #"commentId",
#"updated_at": #"updatedAt",
#"created_at": #"createdAt",
#"user_id": #"userId",
#"commentable_id": #"commentableId",
#"commentable_type": #"commentableType",
#"content": #"content"
}];
RKEntityMapping *userCreatorMapping = [APICallUser RKGetUserMappingOnlyWithAvatarForManagedObjectStore:store];
[commentMapping addConnectionForRelationship:#"creator" connectedBy:#{#"userId": #"userId"}];
[commentMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"creator"
toKeyPath:#"creator"
withMapping:userCreatorMapping]];
Here is my code for my moment mapping (with the association with comments which is working) :
RKEntityMapping *momentMapping = [RKEntityMapping mappingForEntityForName:#"Moment" inManagedObjectStore:store];
momentMapping.identificationAttributes = #[ #"momentId"];
[momentMapping addAttributeMappingsFromDictionary:#{
#"id" : #"momentId",
#"creator.id" : #"creatorId",
#"created_at" : #"createdAt",
#"updated_at" : #"updatedAt"
}];
RKEntityMapping *commentMapping = [APICallComment RKGetCommentMappingForManagedObjectStore:store];
[commentMapping addConnectionForRelationship:#"moment" connectedBy:#{#"commentableId":#"momentId"}];
[momentMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"comments"
toKeyPath:#"comments"
withMapping:commentMapping]];
There is one more thing to know is that a comment can be on a moment or on a photo. According to my JSON, I don't think I need an RKDynamicMapping but I'm not sure.
Here is the code when I'm using my mapping. The request is send successfully and I receive the JSON written before.
KEntityMapping *commentMapping = [APICallComment RKGetCommentMappingForManagedObjectStore:self.appDelegate.managedObjectStore];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:commentMapping
method:RKRequestMethodPOST
pathPattern:APICallCommentCreateCommentsRouteName
keyPath:#"comment"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[session.objectManager addResponseDescriptor:responseDescriptor];
//session.objectManager.requestSerializationMIMEType=RKMIMETypeJSON;
Error Domain=org.restkit.RestKit.ErrorDomain Code=1001 "No mappable object representations were found at the key paths searched." UserInfo=0xb8a9150 {DetailedErrors=(), NSLocalizedFailureReason=The mapping operation was unable to find any nested object representations at the key paths searched: comments, device, devices
The representation inputted to the mapper was found to contain nested object representations at the following key paths: comment
This likely indicates that you have misconfigured the key paths for your mappings., NSLocalizedDescription=No mappable object representations were found at the key paths searched., keyPath=null}
Edit:
Here is the result of the code line session.objectManager.requestDescriptor. It's really weird. I can see only 1 object in the NSArray. When I print it I can read:
Printing description of $1:
<__NSArrayI 0xbd61010>(
<RKRequestDescriptor: 0xbd12bb0 method=(POST) objectClass=BasicLocation rootKeyPath=position : <RKObjectMapping:0xbd40b70 objectClass=NSMutableDictionary propertyMappings=(
"<RKAttributeMapping: 0xbd545d0 latitude => lat>",
"<RKAttributeMapping: 0xbd58430 longitude => lng>"
)>>
)
Nowhere I've written that positionshould be the rootKeyPath and my other attributes are not here (content, commentableType, userId, createdAt, updatedAt, commentId).
Thank you for your help.
You create:
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:commentMapping
method:RKRequestMethodPOST
pathPattern:APICallCommentCreateCommentsRouteName
keyPath:#"comment"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
but you can't ever add it to the object manager, because it only understands comments, device, devices.
That would seem to be your main issue.
You wouldn't usually do this:
[commentMapping addConnectionForRelationship:#"creator" connectedBy:#{#"userId": #"userId"}];
[commentMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"creator"
toKeyPath:#"creator"
withMapping:userCreatorMapping]];
because you are supplying 2 different mappings for exactly the same content and relationship where you only need one because the user information is nested inside the comment information. So, you can remove the foreign key mapping (addConnectionForRelationship:).
The mapping was good but the mistake comes from here:
[RKResponseDescriptor responseDescriptorWithMapping:commentMapping
method:RKRequestMethodGET
pathPattern:HERE
keyPath:#"comment"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
I didn't write the path pattern. This variable changed and everything works perfectly.

Wrong RKResponseDescriptor - nested object found but not mappable

I have the following entity mapping and descriptor:
RKEntityMapping *responseUserMapping = [APICallUser RKGetUserMappingForManagedObjectStore:self.appDelegate.managedObjectStore];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseUserMapping
[session.objectManager addResponseDescriptor:responseDescriptor];
method:RKRequestMethodPOST
pathPattern:APICallUserCreatePattern
keyPath:#"user"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
Here is the description of the method RKGetUserMappingForManagedObjectStore
+ (RKEntityMapping *) RKGetUserMappingForManagedObjectStore:(RKManagedObjectStore *) store{
RKEntityMapping *userMapping = [RKEntityMapping mappingForEntityForName:#"User" inManagedObjectStore:store];
userMapping.identificationAttributes = #[ #"userId" ];
[userMapping addAttributeMappingsFromDictionary:#{
#"id" : #"userId",
#"email" : #"email",
#"firstname" : #"firstName",
#"lastname" : #"lastName",
#"gender" : #"gender",
#"time_zone" : #"timeZone",
#"created_at" : #"createdAt",
#"nickname" : #"pseudo",
#"facebook_id" : #"facebookId",
#"facebook_link_asked_at" : #"lastQueryForFacebookLinkDate",
#"birthday" : #"birthDate",
#"city" : #"city",
#"country" : #"country",
#"sign_in_count" : #"signInCount",
#"facebook_token" : #"facebookToken",
#"facebook_token_expires_at" : #"facebookExpiration",
#"avatar.id" : #"avatarPhotoId"
}];
RKEntityMapping *photoMapping = [APICallPhoto RKGetPhotoMappingForManagedObjectStore:store];
photoMapping.setNilForMissingRelationships = YES;
[userMapping addConnectionForRelationship:#"avatarPhoto" connectedBy:#{#"avatarPhotoId" : #"photoId"}];
//[photoMapping addConnectionForRelationship:#"avatarUsers" connectedBy:#{ #"photoId": #"avatarPhotoId" }];
[userMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"avatar" toKeyPath:#"avatarPhoto" withMapping:photoMapping]];
return userMapping;
}
And the code for the method RKGetPhotoMappingForManagedObjectStore
+ (RKEntityMapping *) RKGetPhotoMappingForManagedObjectStore:(RKManagedObjectStore *) store{
RKEntityMapping *photoMapping = [RKEntityMapping mappingForEntityForName:#"Photo" inManagedObjectStore:store];
photoMapping.identificationAttributes = #[ #"photoId" ];
[photoMapping addAttributeMappingsFromDictionary:#{
#"id" : #"photoId",
#"moment_id" : #"momentId",
#"user_id" : #"userId",
#"title" : #"title",
#"description" : #"photoDescription",
#"file.thumb_url" : #"thumbnailDistURL",
#"file.mini_url" : #"miniDistURL",
#"file.little_url" : #"littleDistURL",
#"file.medium_url" : #"mediumDistURL",
#"file.public_url" : #"originalDistURL"
}];
/*RKEntityMapping *momentMapping = [APICallMoment RKGetMomentMappingForManagedObjectStore:store];
[momentMapping addConnectionForRelationship:#"photos" connectedBy:#{ #"momentId": #"momentId" }];
[photoMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"moment" toKeyPath:#"moment" withMapping:momentMapping]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:photoMapping method:RKRequestMethodAny pathPattern:nil keyPath:#"photos" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[[RKObjectManager sharedManager] addResponseDescriptor:responseDescriptor];
*/
return photoMapping;
}
This is my json that my app received:
{
"user": {
"id": 38,
"email": "test#test.com",
"firstname": "bob",
"lastname": "tonny",
"gender": 0,
"created_at": "2014-04-19T11:00:55Z",
"updated_at": "2014-04-19T11:00:55Z",
"nickname": "bobby",
"facebook_id": null,
"birthday": "1990-02-14",
"city": "",
"country": "",
"facebook_token": null,
"facebook_token_expires_at": null,
"time_zone": "Europe/Paris",
"facebook_link_asked_at": null,
"sign_in_count": 0,
"confirmed": false,
"badge": {
"permanent": 0,
"contextual": 0
},
"avatar": null
}
}
You can see that there is no relation with device or devices here. But I have the exact following error:
error=Error Domain=org.restkit.RestKit.ErrorDomain Code=1001 "No mappable object representations were found at the key paths searched." UserInfo=0xd0c1770 {DetailedErrors=(
), NSLocalizedFailureReason=The mapping operation was unable to find any nested object representations at the key paths searched: device, devices
The representation inputted to the mapper was found to contain nested object representations at the following key paths: user
This likely indicates that you have misconfigured the key paths for your mappings., NSLocalizedDescription=No mappable object representations were found at the key paths searched., keyPath=null}
I'm currently not able to find where the problem comes from.
The only relation between User and Device is set on the .xcdatamodelId like you can see on the following pictures:
relationship for User
relationship for Device
I took a lot at the descriptors: session.objectManager.responseDescriptors. There are several descriptors but none about any devices.
If anyone can just see where I'm missing something, I would really like to know it.
Thank you in advance.
I found the solution. It was not obvious at all. After adding my descriptor to the objectManager, I did that:
session.objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
That solved completely my problem. I don't know why it's important to precise here because I already set this property a long time ago on my code..
Edit: It WAS working ! I didn't touch anything but now it's not working.
Edit 2: I finally got it :
I was using
`[session.objectManager addResponseDescriptor:responseDescriptor];
session.objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
[[RKObjectManager sharedManager] managedObjectRequestOperationWithRequest:request managedObjectContext:session.objectManager.managedObjectStore.mainQueueManagedObjectContext success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)`
I changed [RKObjectManager sharedManager] by my variable session.objectManager and everything work fine !
I hope it will help someone else.

Restkit addConnectionForRelationship struggling

I have two entities: Contact and Listing. A User can mark a specific Listing as its Favourite.
I set up the Relationships correctly and I'm able to add a Listing as a Favourite for the User.
The problem for me is the API side. It gives me only the ids of the relationship: contact_id and the listing_id.
How can I set up RestKit to map the relationships defined in the Favourites object I get from the server which only gives me the two object ids of a contact and a listing?
Restkit Version: 0.20.3
JSON for Favorite:
{
"contact_id": "1",
"created_at": "2013-11-06T15:02:21.056Z",
"id": "2",
"listing_id": "3",
"updated_at": "2013-11-06T15:02:21.056Z"
}
JSON for Contact:
{
"id": 1,
"favorites": [
{
"contact_id": "1",
"created_at": "2013-11-06T15:02:21.056Z",
"id": "2",
"listing_id": "3",
"updated_at": "2013-11-06T15:02:21.056Z"
}
],
"first_name": Max,
}
//////////
// Contact
//////////
RKEntityMapping *contactMapping = [RKEntityMapping mappingForEntityForName:#"PBContact" inManagedObjectStore:managedObjectStore];
contactMapping.identificationAttributes = #[ #"object_id" ];
[contactMapping addAttributeMappingsFromDictionary:#{ #"id": #"object_id" }];
[contactMapping addAttributeMappingsFromArray:#[ #"given_name", #"address", #"birthday",
#"city", #"company_name", #"country",
#"email", #"family_name", #"gender",
#"mobile_number", #"note", #"phone_number",
#"state", #"zip_code", #"created_at", #"updated_at" ]];
//////////
// Listing
//////////
RKEntityMapping *listingMapping = [RKEntityMapping mappingForEntityForName:#"PBListing" inManagedObjectStore:managedObjectStore];
listingMapping.identificationAttributes = #[ #"object_id" ];
[listingMapping addAttributeMappingsFromDictionary:#{ #"id": #"object_id", #"description": #"descriptions" }];
[listingMapping addAttributeMappingsFromArray:#[ #"address", #"name", #"bathrooms", #"bedrooms",
#"city", #"country", #"price", #"title",
#"zip_code", #"latitute", #"longitude", #"status",
#"total_area", #"year_built", #"property_type", #"listing_type",
#"size", #"lot_size", #"parking_spaces", #"view",
#"state", #"note", #"monthly_rent", #"created_at", #"updated_at" ]];
//////////
// Relationships
//////////
[contactMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"favorites" toKeyPath:#"favoriteListings" withMapping:listingMapping]];
[listingMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"favorites" toKeyPath:#"prospects" withMapping:contactMapping]];
Response Descriptor
RKResponseDescriptor *contactResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:contactMapping
method:RKRequestMethodAny
pathPattern:#"api/v1/contacts"
keyPath:nil
statusCodes:statusCodes];
You don't need (and should delete):
[listingMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"favorites" toKeyPath:#"prospects" withMapping:contactMapping]];
because the relationship is already being configured from the other side on the contactMapping. There shouldn't need to be any other changes by the look of it.
It isn't entirely clear why there are 3 ids:
"contact_id": "1",
"id": "2",
"listing_id": "3",
I'm assuming that contact_id always matches the outer contact that the favourites are nested in and isn't required. And that id is the useful piece of information (so your mapping is correct).
In your Core Data model, favoriteListings and prospects should be the inverse of each other.
Based on your latest comments, I think you need to create a new and different listing mapping. This mapping only contains #"listing_id" : #"object_id". This will either link to an existing listing entity or create a placeholder that can be filled in later. This is the mapping that you should use as the relationship on contactMapping.

How to map a JSON array with RestKit

I have json string like this format :
[{"image":"/0001.jpg","link":"/index.php"},
{"image":"/0001.jpg","link":"/index.php"}]
it does not have a key in the top level.
[mapping mapKeyPath:#"image" toAttribute:#"image"];
mapping like this won't work , it give me the error:
restkit.object_mapping:RKObjectMapper.m:81 Adding mapping error: Could not find an object mapping for keyPath: ''
How to map this type of json ?
Use
[[RKObjectManager sharedManager].mappingProvider addObjectMapping:myObject];
You should check "Mapping without KVC" section on Restkit Object Mapping Docs.
Here is an example from the docs:
[
{ "title": "RestKit Object Mapping Intro",
"body": "This article details how to use RestKit object mapping...",
"author": {
"name": "Blake Watters",
"email": "blake#restkit.org"
},
"publication_date": "7/4/2011"
}
]
And you map that like this:
// Our familiar articlesMapping from earlier
RKObjectMapping* articleMapping = [RKObjectMapping mappingForClass:[Article class]];
[articleMapping mapKeyPath:#"title" toAttribute:#"title"];
[articleMapping mapKeyPath:#"body" toAttribute:#"body"];
[articleMapping mapKeyPath:#"author" toAttribute:#"author"];
[articleMapping mapKeyPath:#"publication_date" toAttribute:#"publicationDate"];
[[RKObjectManager sharedManager].mappingProvider addObjectMapping:articleMapping];
For me the solution was:
add this response descriptor, use the object inside de array
[manager addResponseDescriptor:[ObjectInsideTheArray getResponseDescriptor]];
and in the success response
NSArray *response = (NSArray *)[mappingResult array]; //instead of firstObject

Resources