RestKit primary key attribute - ios

I load data from a json file, I save it.
I do it twice ...
I got two entries in my Core Data sqlite database.
Even if I set in the mapping the primaryKeyAttribute.
mapping.primaryKeyAttribute = #"code";
[mapping mapAttributesFromArray :mappedFields];
[[RKObjectManager sharedManager].mappingProvider setMapping:mapping forKeyPath:entityName];
My Json
{ "MyEntity": [ { "code" : "axv2","data" : "content"}]};
Here the callback :
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
NSLog(#"Entries loaded %d",[objects count]);
lastResult = objects;
for(MyEntity * myEntity in lastResult) {
[self saveContext];
}
}
My entity is correctly mapped ... But Restkit allow one to save duplicate entries with the same primary key?
It's weird, I understood that this primary key attribute would avoid this problem.

No, that is not the case, as Core Data keeps its own keys. You can easily solve this problem by checking if your primary key exists and before saving the entity instance in question.

As of the latest RESTKit version (0.23.2) you can define the primary key like this:
[_mapping addAttributeMappingsFromDictionary:#{ #"id" : #"objectId", #"name" : #"name" }];
[_mapping setIdentificationAttributes:#[ #"objectId" ]];
Whereas objectId is you primary key on the core data object.

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

RestKit mapping for JSOG API object

I'm trying to figure out how to realize a mapping of an API response into CoreData objects using RestKit. The API uses JSOG standard. Here is an example:
[
{
"#id": "1",
"name": "Sally",
"friend": {
"#id": "2",
"name": "Bob",
"friend": {
"#id": "3",
"name": "Fred",
"friend": { "#ref": "1" }
}
}
},
{ "#ref": "2" },
{ "#ref": "3" }
]
How would I create an RKEntityMapping for such a JSON? Mapping of simple attributes is trivial, the question is how to setup the relationships here so they work with #ref, also, when the top level user object contains the #ref only.
RKEntityMapping *userMapping = [RKEntityMapping mappingForEntityForName:#"Question"
inManagedObjectStore:[RKObjectManager sharedManager].managedObjectStore];
[userMapping addAttributeMappingsFromDictionary:#
{
#"#id" : #"identifier",
#"name" : #"name"
}];
[userMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"friend"
toKeyPath:#"friend"
withMapping:userMapping]];
My guess is that i could use code below to handle the #ref inside of an object:
[userMapping addConnectionForRelationship:#"friend" connectedBy:#{#"#ref": #"#id"}];
but is it correct?
How would I map either a full object or just a reference to an already-provided object (via #ref) to actual Core Data objects?
How can I map the top elements of the JSON (being a list of User objects) to an actual list of User entities?
Add a relationship using the mapping itself, so it drills recursively. Use foreign key mapping for the refs.
Looking at this again following your comment it may actually be a good candidate for a dynamic mapping instead of foreign key. This would mean creating a new mapping which just uses #"#ref" : #"identifier" for the connections and a dynamic mapping which looks at the keys and decides wether to use this connection mapping or the full userMapping.
Note that both of these mappings need to specify that identifier is the unique key of the object and you should use a memory cache to allow the existing objects to be found instead of creating duplicates.

RestKit - saving JSON content into a string coredata attribute

I need to parse a JSON web service response containing a key whose children are not known.
For example let's have the following JSON response where the keys of the customData attribute are defined at runtime:
{
"score": 996,
"customData": { "key1": "key1value", "key2": "key2value" },
"isRegistered": true,
"allowOpening": "OK"
}
Would it be possible to save the JSON content of customData into a string coredata attribute?
I've tried with a simple mapping like this:
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:[[self class] description] inManagedObjectStore:managedObjectStore];
[mapping addAttributeMappingsFromDictionary:#{
#"score": #"score",
#"customData":#"customData",
#"isRegistered": #"isRegistered",
#"allowOpening": #"allowOpening"}];
but it doesn't work, customData saved by coredata is always empty.
Many thanks,
DAN
Would it be possible to save the JSON content of customData into a string coredata attribute?
No, because it will be deserialised to a dictionary and there is no converter for that to a string.
You can store it as a dictionary. It you could add a relationship mapping with a dynamic mapping which checks what the keys are and defines the mapping on the fly...

RestKit: How to POST a NSManagedObject as JSON without any nesting attributes?

This should be a really easy one, but sadly I haven't found any answer...
What RestKit mapped my object to:
request.body={"user":{"pass":"1234","id":0,"login":"awesome_guy","tier":0}}
What I really want:
{"pass":"1234","id":0,"login":"awesome_guy","tier":0}
Just without the object name "user".
If you have dealt with this issue, it'll take you 5 seconds to answer. If you haven't used RestKit. You do not know the answer. I'm attaching my code anyway:
User Object Mapping:
/* ===========================
* ====User Object Mapping====
* ==========================*/
RKEntityMapping* userObjectMapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([User class]) inManagedObjectStore:objectManager.managedObjectStore];
NSDictionary *userObjectMappingDict = #{
#"id":#"id",
#"login":#"login",
#"firstName":#"firstName",
#"lastName":#"lastName",
#"phoneNumber":#"phoneNumber",
#"email":#"email",
#"tier":#"tier",
#"sessionId":#"sessionId",
#"pass":#"password"
};
userObjectMapping.identificationAttributes = #[#"id"];
[userObjectMapping addAttributeMappingsFromDictionary:userObjectMappingDict];
RKEntityMapping* userBusinessMapping = [RKEntityMapping mappingForEntityForName:#"Business" inManagedObjectStore:objectManager.managedObjectStore];
[userBusinessMapping addAttributeMappingsFromDictionary:#{#"business":#"id"}]; // Nil Key path
[userObjectMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:nil toKeyPath:#"business" withMapping:userBusinessMapping]];
You should have an instance of RKRequestDescriptor that you haven't shown. It's created with requestDescriptorWithMapping:objectClass:rootKeyPath:. You have the root key path set to #"user" and you should set it to nil.

Reskit - Mapping to array

I'm trying to run some unit tests to test my mappings with RestKit v0.20, however I am getting an error that my destination object is nil. I have track this down to the fact that the mapping is failing because the sourceType is an NSArray and my destinationType is an NSNumber. I think this is because my mapping keypaths are incorrect. I am trying to map the songCard JSON to my objet. I have included my JSON and mapping test below.
It Would be great it someone could help me to set the correct keypath.
{"status" : 2000,
"content" : {
"cardList" : [
{
"songCard" : {
"likes" : 2,
"dislikes" : 3
}
}
]
},
"message" : "OK"
}
Unit Test class
- (RKObjectMapping *)songMetadataMapping
{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[SongMetadata class]];
[mapping addAttributeMappingsFromDictionary:#{
#"content.cardList.songCard.likes": #"likes"
}];
return mapping;
}
- (void)testSongMetadataMapping
{
NSString *parsedJSON = [RKTestFixture parsedObjectWithContentsOfFixture:#"songMetadata.json"];
RKMappingTest *test = [RKMappingTest testForMapping:[self songMetadataMapping] sourceObject:parsedJSON destinationObject:nil];
[test addExpectation:[RKPropertyMappingTestExpectation expectationWithSourceKeyPath:#"content.cardList.songCard.likes" destinationKeyPath:#"likes" value:#"2"]];
STAssertTrue([test evaluate], #"Mappings failed");
}
UPDATE
After further debugging I have found that the value 2 in my JSON string is being evaluated as an NSArray, when this should be evaluated as NSNumber. As a quick test I removed the [ ] in my JSON and the value 2 was correctly evaluated as an NSNumber. This doesn't solve my problem though as I have need to identify my JSON as an array of songCard objects
As you have noticed, you cannot use the keypath as you have specified when an array is in play. I can think of two options - the first is a long shot, but does the key path content.cardList[0].songCard.likes work?
Otherwise, consider using the method:
+ (instancetype)expectationWithSourceKeyPath:(NSString *)sourceKeyPath
destinationKeyPath:(NSString *)destinationKeyPath
evaluationBlock:(RKMappingTestExpectationEvaluationBlock)evaluationBlock;
With keypath content.cardList and supplying an evaluation block that 1) checks that the mapping is an array that contains a single object. You can then check that the object contains a songCard object and that has a likes value of 2.

Resources