RestKit 0.22 : how to map only firstObject of an array? - ios

I am receiving more data than needed: I only want to map first object of an array of objects.
Using RestKit 0.22, how to map the following Json:
{ "name": "foobar",
"tags": ["tag1", "tag2", "tag3"] }
With the following model:
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *firstTag;
To map name, I have this code:
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:#"MyObjectModel" inManagedObjectStore:[RKManagedObjectStore defaultStore]];
[mapping addAttributeMappingsFromDictionary:#{#"name": #"name"}];
To map firstObject of tags to firstTag, I do not know how to proceed. Note: MyObjectModel is an NSManagedObject, so firstTag is #dynamic.

The tried and tested approach is to create a method on the managed object which takes the full array. That method extracts the first item and saves it to the actual NSString property. Then your mapping is as standard as all of the data types match.

Related

Restkit: Mapping an array to object

My JSON looks like this:
[ [value1, value2, value3], [value1, value2, value3]]
I want to iterate over the external array, and map each internal array to an object such as:
#interface MyObject : NSObject
#property (nonatomic, copy) NSString* key1;
#property (nonatomic, copy) NSString* key2;
#property (nonatomic, copy) NSString* key3;
#end
For the sample JSON, I should get two mapped objects, e.g. MyObject1, MyObject2, where MyObject1 is mapped to the first internal array, and MyObject2 is mapped to the second internal array. Each having their properties mapped to corresponding values in the array i.e. key1 == value1, key2==value2 and key3==value3.
Any ideas hot to do such a mapping?
The way I ended up handling it was to add an array property to my object, and map the whole array to that property (a transformable property incase of a NSManagedObject subclass)
RKEntityMapping *responseMapping = [RKEntityMapping mappingForEntityForName:#"MyObject" inManagedObjectStore:managedObjectStore];
[responseMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:#"values"]];
After mapping is complete, I assign rest of the values in the completion block:
^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
[mappingResult.array enumerateObjectsUsingBlock:^(MyObject *obj, NSUInteger idx, BOOL *stop) {
obj.key1 = obj.values[0];
obj.key2 = obj.values[1];
obj.key3 = obj.values[2];
}];

RestKit EntityMapping with nested array

I have a problem with entity mapping part of my JSON response.
I've tried to make a seperate entity for Ingredients in my Core Data model and set the mappingForEntityName to #"Ingredients", but to no avail. In my Dish Core Data entity model I have declared the #"ingredientValue", #"ingredientName", and so on, but when looking in my .sqlite file, it doesn't populate the fields for ingredients.
RestKit doesn't throw any errors back at me. So I'm suspecting it has something to do with the ingredients being stored in an array?
As you can see I've declared 2 RKEntityMapping instances (I don't know if this is the correct way of doing it?). But my ingredients are not returned.
The JSON looks like this:
{ dishes: [
{
id: 34,
name: "Pavlova med citroncreme og jordbær",
header: "En laekker marengs kage til enhver lejlighed",
howto: "lots of text....",
image: "/img_path/img.jpg",
created_at: "2014-04-05T00:25:22.378Z",
ingredients: [
{
ingredient_id: 27,
ingredient_amount: 3,
ingredient_unit: "stk",
ingredient_name: "æggehvider"
},
{
ingredient_id: 28,
ingredient_amount: 2,
ingredient_unit: "dl",
ingredient_name: "sukker"
}
]
}
]}
Dish.h:
#interface Dish : NSManagedObject
#property (nonatomic, copy) NSString *dishName;
#property (nonatomic, copy) NSString *dishHeader;
#property (nonatomic, copy) NSString *dishImage;
#property (nonatomic, copy) NSString *dishIngredients;
#property (nonatomic) NSInteger dishID;
#property (nonatomic, copy) NSData *dishMainText;
#property (nonatomic) NSDate *createdAt;
//Ingredients
#property (nonatomic, copy) NSString *ingredientName;
#property (nonatomic, copy) NSString *ingredientUnit;
#property (nonatomic) NSInteger ingredientValue;
#property (nonatomic) NSInteger ingredientID;
#end
Dish.m:
RKEntityMapping *dishMapping = [RKEntityMapping mappingForEntityForName:#"Dish" inManagedObjectStore:managedObjectStore];
[dishMapping addAttributeMappingsFromDictionary:#{
#"id": #"dishID",
#"name": #"dishName",
#"header": #"dishHeader",
#"howto": #"dishMainText",
#"image": #"dishImage",
#"created_at": #"createdAt"}];
dishMapping.identificationAttributes = #[ #"dishID" ];
RKEntityMapping *ingredientMapping = [RKEntityMapping mappingForEntityForName:#"Dish" inManagedObjectStore:managedObjectStore];
[ingredientMapping addAttributeMappingsFromDictionary:#{
#"ingredient_amount": #"ingredientValue",
#"ingredient_unit": #"ingredientUnit",
#"ingredient_name": #"ingredientName"}];
[dishMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"dishes" toKeyPath:#"dishes" withMapping:ingredientMapping]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:dishMapping
method:RKRequestMethodGET
pathPattern:#"/dishes.json"
keyPath:#"dishes"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
EDIT Added part of the log output for RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace)
with mapping <RKEntityMapping:0x8f69750 objectClass=NSManagedObject propertyMappings=(
"<RKAttributeMapping: 0x8f6c150 name => dishName>",
"<RKAttributeMapping: 0x8f6c190 id => dishID>",
"<RKAttributeMapping: 0x8f6c480 header => dishHeader>",
"<RKAttributeMapping: 0x8f6c4a0 howto => dishMainText>",
"<RKAttributeMapping: 0x8f6c520 image => dishImage>",
"<RKAttributeMapping: 0x8f6c5c0 created_at => createdAt>",
"<RKRelationshipMapping: 0x8f6d8e0 dishes => dishes>"
I don't see dishes defined in your Dish entity. It should be a to-many relationship to Ingredient.
ingredientMapping should use the Ingredient entity.
Where you call dishMapping addPropertyMapping:..., you should be using ...FromKeyPath:#"ingredients" ... to match the key name in the JSON coming in.

How to add a connection to RKEntityMapping when it is contained in the same JSON

I'm trying to map a JSON response to instances of NSManagedObject, but i don't understand how to set the relationship/connection correctly.
My classes are:
#interface CloudObject : NSManagedObject
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) CloudFolder *parent;
#end
#interface CloudFolder : CloudObject
#property (nonatomic, retain) NSSet *children;
#end
#interface CloudFile : CloudObject
#property (nonatomic, retain) NSString * mimeType;
#end
JSON is something like this:
{
"name": "Folder1",
"is_dir": true,
"contents": [
{ "name": "Folder2", "is_dir": true }
{ "name": "File1", "is_dir": false, "mime_type": "audio/mpeg" }
]
}
I use RKDynamicMapping to map the response (root object):
RKEntityMapping *fileMapping = [RKEntityMapping mappingForEntityForName:#"CloudFile" inManagedObjectStore:managedObjectStore];
[fileMapping addAttributeMappingsFromDictionary:#{#"name": #"name", #"mime_type": #"mimeType"}];
RKEntityMapping *folderMapping = [RKEntityMapping mappingForEntityForName:#"CloudFolder" inManagedObjectStore:managedObjectStore];
[folderMapping addAttributeMappingsFromDictionary:#{#"name": #"name"}];
RKDynamicMapping *objectMapping = [[RKDynamicMapping alloc] init];
[objectMapping addMatcher:[RKObjectMappingMatcher matcherWithKeyPath:#"is_dir" expectedValue:[NSNumber numberWithBool:NO] objectMapping:fileMapping]];
[objectMapping addMatcher:[RKObjectMappingMatcher matcherWithKeyPath:#"is_dir" expectedValue:[NSNumber numberWithBool:YES] objectMapping:folderMapping]];
But how can I now setup the relationship/connection to #"contents" (which should be RKDynamicMapping too) and children/parent? (Folders contained in 'contents' do NOT have an 'contents'-attribute)
Take a look at the RestKit documentation for dynamic mappings : Dynamic Object Mapping.
According to the documentation, it looks like you should add this line, after creating your dynamic mapping objectMapping:
[folderMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"contents" toKeyPath:#"children" withMapping:objectMapping]];
You specify the "contents" property as being mapped with your dynamic mapping. Also, you don't need to worry about the inner folders not having a "contents" field. If "contents" doesn't exist, RestKit should just ignore it gracefully, and your NSSet *children property will be automatically set to nil for that object.

Restkit mapping complex nested array

I am trying to use restkit to read/parse an xml feed. which contains a nested array of artists. I can map other relationships but am unclear what I am to do with the artist relationship below.
My dataset looks like:
<JamBase_Data>
<Results_Title>Jersey City, NJ</Results_Title>
<event>
<event_id>1896611</event_id>
<artists>
<artist>
<artist_id>96929</artist_id>
<artist_name>The Happy Problem</artist_name>
</artist>
<artist>
<artist_id>29817</artist_id>
<artist_name>Craig Greenberg</artist_name>
</artist>
</artists>
<event_date>1/9/2013</event_date>
<venue>
<venue_id>48553</venue_id>
<venue_name>The Cake Shop</venue_name>
<venue_city>New York</venue_city>
<venue_state>NY</venue_state>
<venue_zip>10002</venue_zip>
</venue>
<event_url>
http://www.jambase.com/Shows/Event.aspx?eventID=1896611
</event_url>
</event>
</JamBase_Data>
Mappings:
RKObjectMapping *eventMapping = [RKObjectMapping mappingForClass:[Event class]];
RKObjectMapping *artistMapping = [RKObjectMapping mappingForClass:[Artist class]];
RKObjectMapping *venueMapping = [RKObjectMapping mappingForClass:[Venue class]];
[artistMapping mapKeyPathsToAttributes: #"artist_name",#"artist_name", nil];
[venueMapping mapKeyPathsToAttributes: #"venue_city",#"venue_city",#"venue_state",#"venue_state", #"venue_name", #"venue_name", nil];
[objectManager.mappingProvider setMapping:eventMapping forKeyPath:#"JamBase_Data.event"];
[eventMapping mapKeyPath:#"artists" toRelationship:#"artists" withMapping:artistMapping];
[eventMapping mapKeyPath:#"venue" toRelationship:#"venue" withMapping:venueMapping];
Venue.h
#interface Event : NSObject
#property (nonatomic, strong) NSSet *artists;
#property (strong, nonatomic) Venue *venue;
#end
Artist.h
#interface Artist : NSObject
#property (nonatomic, strong) NSNumber *artist_id;
#property (nonatomic, strong) NSNumber *artist_name;
I can get venue to map correctly, but cannot figure out how to get Artists to map. Is there something i need to do to tell the object manager to make an coming off of the event?
Any help would be greatly appreciated as this is my first foray into iOS development.
The Artists object you see in the XML translates to an NSArray. Now that contains objects of type Artists. Have you thought about it that way?

Relationships not Connected with Core Data and RestKit

I've been working with RestKit (0.9.3) to populate some Core Data in my app. The entity objects are created correctly, but the relationships between them are not created. From what I can tell, I have things setup correctly according to:
https://github.com/RestKit/RestKit/wiki/Object-mapping
RKRelationshipMappingExample in the Examples folder
So, here's a sample of the JSON that I'm getting back from the server:
{
"artist" : {
"artistId" : 123,
"artistName" : "The Artist",
"albums" : [
{
"albumPrice" : 12.99,
"albumId" : 456,
"trackCount" : 12,
"albumName" : "Album Title",
},
{
"albumPrice" : 10.99,
"albumId" : 789,
"trackCount" : 10,
"albumName" : "Another Album Title",
}
]
}
}
And I am using RestKit to populate the following two managed objects:
#interface Artist : NSManagedObject
#property (nonatomic, retain) NSNumber * artistId;
#property (nonatomic, retain) NSString * artistName;
#property (nonatomic, retain) NSSet *albums;
#end
#interface Artist (CoreDataGeneratedAccessors)
- (void)addAlbumsObject:(Album *)value;
- (void)removeAlbumsObject:(Album *)value;
- (void)addAlbums:(NSSet *)values;
- (void)removeAlbums:(NSSet *)values;
#end
#interface Album : NSManagedObject
#property (nonatomic, retain) NSNumber * albumId;
#property (nonatomic, retain) NSString * albumName;
#property (nonatomic, retain) NSNumber * albumPrice;
#property (nonatomic, retain) NSNumber * trackCount;
#property (nonatomic, retain) NSManagedObject *artist;
#end
I've setup the following configurations with RestKit, and the intent is for 3 Core Data items to be created: one artist and two albums. The artist entity has a "albums" relationship which should be a set of these two albums.
// Initialize our RestKit singleton
RKClient *client = [RKClient clientWithBaseURL:API_URL];
// Initialize for Object mapping
RKObjectManager* objectManager = [RKObjectManager objectManagerWithBaseURL:API_URL];
objectManager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:#"AppName.sqlite"];
// Configure ALBUM mapping
RKManagedObjectMapping* albumMapping = [RKManagedObjectMapping mappingForClass:[Album class]];
albumMapping.primaryKeyAttribute = #"albumId";
[albumMapping mapKeyPath:#"albumId" toAttribute:#"albumId"];
[albumMapping mapKeyPath:#"albumName" toAttribute:#"albumName"];
[albumMapping mapKeyPath:#"albumPrice" toAttribute:#"albumPrice"];
[albumMapping mapKeyPath:#"trackCount" toAttribute:#"trackCount"];
// Configure ARTIST mapping
RKManagedObjectMapping* artistMapping = [RKManagedObjectMapping mappingForClass:[Artist class]];
artistMapping.primaryKeyAttribute = #"artistId";
[artistMapping mapKeyPath:#"artistId" toAttribute:#"artistId"];
[artistMapping mapKeyPath:#"artistName" toAttribute:#"artistName"];
[artistMapping mapKeyPath:#"albums" toRelationship:#"albums" withMapping:albumMapping];
// Register mappings with the provider
[[RKObjectManager sharedManager].mappingProvider setMapping:albumMapping forKeyPath:#"albums"];
[[RKObjectManager sharedManager].mappingProvider setMapping:artistMapping forKeyPath:#"artist"];
And then later in my program, I load the data:
RKObjectManager *objectManager = [RKObjectManager sharedManager];
[objectManager loadObjectsAtResourcePath:#"/artist/123/albums" delegate:self];
After this is executed, my Core Data store is populated with the three objects (one artist and two albums), but the relationship between them is not created.
The result is that I can fetch this list of albums directly, or I can fetch the artist directly. But if I try to use: artist.albums the result is empty.
Can anyone offer suggestions on what I could be doing wrong, or how to further troubleshoot this?
Thanks so much.
EDIT: Update 10/26
I just setup a small sample project with these same configurations and everything was loaded correctly (i.e. The artist entity was created with a relationship to it's albums). Sadly, this means that the issue is something in my larger project, rather than in what I outlined here earlier. With that said, I'll continue trudging though and updating, so if anyone has advice on how to troubleshoot this or where in particular to look, I would greatly appreciate it.
EDIT: Resolved
Embarrassingly, the problem was simply that I was using the wrong base url. I'll post full details (in case it can help anyone else) tomorrow.
Well... we have a resolution. I'm embarrassed to say that the problem was simply that I was using the wrong base url in my main project. I made some updates to the JSON returned by the local copy of the API, but the base url was still pointing to the copy of the API online.
The change I made was to format the JSON this way rather than having "artist" and "albums" be separate entities in the JSON.

Resources