Fairly new to RestKit I run into a problem where I download public gist data.
I created an RKObjectManager and two RKResponseDescriptor objects with a corresponding RKEntityMapping objects like so.
RKEntityMapping *userEntityMapping = [RKEntityMapping mappingForEntityForName:#"User" inManagedObjectStore:managedObjectStore];
userEntityMapping.identificationAttributes = #[#"userID"];
[userEntityMapping addAttributeMappingsFromDictionary:#{
#"id" : #"userID",
#"avatar_url" : #"avatarURL",
#"gravatar_id" : #"gravatarID",
#"url" : #"jsonURL",
#"login" : #"login"}];
RKResponseDescriptor *userResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:userEntityMapping pathPattern:#"/gists/public" keyPath:#"user" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:userResponseDescriptor];
// Gist Entity
RKEntityMapping *gistEntityMapping = [RKEntityMapping mappingForEntityForName:#"Gist" inManagedObjectStore:managedObjectStore];
gistEntityMapping.identificationAttributes = #[ #"gistID" ];
[gistEntityMapping addRelationshipMappingWithSourceKeyPath:#"user" mapping:userEntityMapping];
[gistEntityMapping addAttributeMappingsFromDictionary:#{
#"id": #"gistID",
#"url": #"jsonURL",
#"description": #"descriptionText",
#"public": #"public",
#"created_at": #"createdAt"}];
RKResponseDescriptor *gistResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:gistEntityMapping pathPattern:#"/gists/public" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:gistResponseDescriptor];
The problem arises from the problem that sometimes the user field in the json is null and I don't know how to cope with that.
In fact I got an exception here
static NSDictionary *RKEntityIdentificationAttributesForEntityMappingWithRepresentation(RKEntityMapping *entityMapping, NSDictionary *representation)
and found the array processed here contains the null values, which lead to the exception.
- (NSArray *)mapRepresentations:(id)representations atKeyPath:(NSString *)keyPath usingMapping:(RKMapping *)mapping
Is there a way to handle those situations?
EDIT:
The exception is an
NSCAssert([representation isKindOfClass:[NSDictionary class]], #"Expected a dictionary representation");
in RKManagedObjectMappingOperationDataSource
static NSDictionary *RKEntityIdentificationAttributesForEntityMappingWithRepresentation(RKEntityMapping *entityMapping, NSDictionary *representation)
As far as the JSON is concerned, it looks sometimes like this.
{
"url": "https://api.github.com/gists/5661319",
"forks_url": "https://api.github.com/gists/5661319/forks",
"commits_url": "https://api.github.com/gists/5661319/commits",
"id": "5661319",
"git_pull_url": "https://gist.github.com/5661319.git",
"git_push_url": "https://gist.github.com/5661319.git",
"html_url": "https://gist.github.com/5661319",
"files": {
"header.php": {
"filename": "header.php",
"type": "application/httpd-php",
"language": "PHP",
"raw_url": "https://gist.github.com/raw/5661319/12598e6d41015248235058cda806722b30f81dca/header.php",
"size": 173
}
},
"public": true,
"created_at": "2013-05-28T08:29:35Z",
"updated_at": "2013-05-28T08:29:36Z",
"description": "",
"comments": 0,
"user": null,
"comments_url": "https://api.github.com/gists/5661319/comments"
},
Related
I am creating a response descriptor for json to core data mapping in RestkitManager. The parent object is "level" and it has an array of "sublevel" objects.
RKDynamicMapping *levelMapping = [Level map];
RKResponseDescriptor* levelRd = [RKResponseDescriptor responseDescriptorWithMapping:levelMapping method:RKRequestMethodGET pathPattern:#"entity/:entityId" keyPath:#"summary.levels" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[self.objectManager addResponseDescriptor:levelRd];
In Level class
+ (RKEntityMapping *)mapping {
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([self class]) inManagedObjectStore:[RKManagedObjectStore defaultStore]];
[mapping addAttributeMappingsFromDictionary:#{
#"id" : #"id",
#"name" : #"name",
#"state" : #"state"
}];
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"sublevel" toKeyPath:#"sublevelList" withMapping:[Sublevel map]]];
return mapping;
}
In Sublevel
+ (RKEntityMapping *)map {
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([self class]) inManagedObjectStore:[RKManagedObjectStore defaultStore]];
[mapping addAttributeMappingsFromDictionary:#{
#"id" : #"id",
#"staticNode.obj.name" : #"name"
}];
return mapping;
}
When I try to fetch sublevel on object level, I get it in random order. Sometimes 2nd sublevel get printed first. Is there any way to maintain the order?
As I understand, when mapping is done, I don't have any control over what is getting persisted in the database. Hence, I am not able to assign any order number myself. Apart from that, I have explored metadata.routing.parameters but for this I need to pass parameters in the API call itself - which is not desirable.
Any pointers on how to maintain the order would be helpful.
JSON
{
"entity": {
"id": 1,
"name": "name"
},
"settings": {
"key": "value"
}
}
Entity Model
Attributes: id, name
Relationship: settings(one to one)
Settings Model
Attributes: key
Relationship: entity(reverse relationship, one to one)
EntityMapping
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([self class]) inManagedObjectStore:[RKManagedObjectStore defaultStore]];
mapping.persistentStore = [RKManagedObjectStore defaultStore].persistentStoreCoordinator.persistentStores.firstObject;
mapping.identificationAttributes = #[#"id"];
[mapping addAttributeMappingsFromDictionary:#{
#"id" : #"id",
#"name" : #"name"
}];
Had settings been inside entity in json response, I would add this
[mapping addPropertyMapping:
[RKRelationshipMapping relationshipMappingFromKeyPath:#"settings"
toKeyPath:#"settings" withMapping:[Settings map]]
];
But my response id different than this, so how should I map correctly?
Both entity and settings are in a container dictionary, so you can change your response descriptor to not drill down so far and then use key paths to drill into the entity part and have direct access to the settings part:
[mapping addAttributeMappingsFromDictionary:#{
#"entity.id" : #"id",
#"entity.name" : #"name"
}];
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"settings" toKeyPath:#"settings" withMapping:[Settings map]]];
I have the following JSON:
"Menus":{
"Id" : "3",
"Name" : "Cheese Burger",
"Items": []
}
I map the response to Core Data like so:
+ (RKEntityMapping *)menuMapping {
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:#"Menu" inManagedObjectStore:[[CoreDataManager sharedInstance] objectStore]];
mapping.assignsNilForMissingRelationships = YES;
mapping.assignsDefaultValueForMissingAttributes = YES;
[mapping addAttributeMappingsFromDictionary:#{
#"Id": #"remoteID",
#"Name": #"name",
}];
mapping.identificationAttributes = #[ #"remoteID" ];
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"Items"
toKeyPath:#"products"
withMapping:[MappingProvider productMapping]]];
return mapping;
}
+ (RKEntityMapping *)productMapping {
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:#"Product" inManagedObjectStore:[[CoreDataManager sharedInstance] objectStore]];
mapping.assignsNilForMissingRelationships = YES;
[mapping addAttributeMappingsFromDictionary:#{
#"Id": #"remoteID",
#"Name": #"name",
#"Description" : #"info",
#"Price": #"price"
}];
mapping.identificationAttributes = #[ #"remoteID" ];
return mapping;
}
How would I manage to validate if the Items array is empty or not, and create the Menu object in Core Data based on if the Items (the products) contains values? I have tried using the assignsNilForMissingRelationships but it does not seem to work in this case.
Use KVC validation to analyse the incoming value and (modify or) reject it.
I had a JSON response of this type:
{
"d": { "id": "1", "user": "test"}
}
which I was parsing with Restkit with the following code:
#interface ODataUser : NSObject<ODataObject>
#property (nonatomic, copy) NSString * id;
#property (nonatomic, copy) NSString * user;
-(NSString*)getId;
-(NSString*)getUser;
#end
RKObjectMapping *map = [RKObjectMapping mappingForClass:[ODataUser
class]]; [mapping addAttributeMappingsFromDictionary: #{ #"id" :
#"id", #"user" : #"user" } ];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:map method:RKRequestMethodGET pathPattern:nil keyPath:#"d" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
However, now my response has changed to something like this:
{
"d": { "results": [ {"id": "1", "user": "test"} ] }
}
How can I reflect those changes on the response on my code?
Change your response descriptor to use:
keyPath:#"d.results"
as this will navigate into the d dictionary to get the results array and process all of the dictionaries it contains.
How cant I set a static value while mapping entities?
I have a JSON response like this:
"friends": [
{
"id": 123,
"name": "Friend",
},
]
"featured": [
{
"id": 456,
"name": "Some Featured user",
},
]
My mapping and descriptors look like this:
RKMapping *friendsMapping = [ProjectMappingProvider userMapping];
RKMapping *featuredMapping = [ProjectMappingProvider featuredUserMapping];
RKResponseDescriptor *friendsResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:friendsMapping
method:RKRequestMethodGET
pathPattern:#"/api/users"
keyPath:#"friends"
statusCodes:statusCodeSet];
RKResponseDescriptor *featuredResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:friendsMapping
method:RKRequestMethodGET
pathPattern:#"/api/users"
keyPath:#"featured"
statusCodes:statusCodeSet];
RKManagedObjectRequestOperation *operation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request
responseDescriptors:#[
friendsResponseDescriptor,
featuredResponseDescriptor]];
... some code emited for readabilty ...
Now mu friendsResponseDescriptor and featuredResponseDescriptors look almost identical, but I would like to set additional CoreData parameter accordingly. Objects mapped through friendsDescriptor should have section = 0 and objects mapped through featured descriptor should have section = 10.
So, can I do something like this?
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:#"User"
inManagedObjectStore:[[DataModel sharedDataModel] objectStore]];
[mapping addAttributeMappingsFromDictionary:#{
#"id": #"userId",
#"name": #"name" }];
mapping.identificationAttributes = #[ #"userId" ];
// How can I do somethning like this?
[mapping setValue:#0 forKey:#"section"];
And the featured mapping:
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:#"User"
inManagedObjectStore:[[DataModel sharedDataModel] objectStore]];
[mapping addAttributeMappingsFromDictionary:#{
#"id": #"userId",
#"name": #"name" }];
mapping.identificationAttributes = #[ #"userId" ];
// How can I do somethning like this?
[mapping setValue:#10 forKey:#"section"];
Note that I don't have any other indicator whetever user is a friend or featured in the user JSON itself. The only way I can distinguish the type of user (friend,featured) is in which list in JSON response the user is set.
I am later using the section property in the table view controller to have sections.
If you are using different entities, set default values on them. If you aren't using different entities, consider changing so that you are (they could be sub-entities of a common parent).
You can't insert data into the mapping. The mapping only describes what RestKit should process. To edit the values you would need to get involved in the mapping process yourself and implement some delegate methods to inject additional data.