ResKit Object Mapping nested objects - ios

I am attempting to use RestKit to parse some JSON. The app is for tracking competitions, and has a concept of venues, games, and players. A game has a winner and loser (both of class Player) and a venue. The JSON returned by the web service looks like this:
[{"id":1,"winner":{"name":"NAME2","id":2,"picture_url":"CK.png"},"loser":{"name":"NAME3","id":3,"picture_url":"NJ.png"},"venue":{"name":"Venue 1","id":2}},
{"id":2,"winner":{"name":"NAME2","id":2,"picture_url":"CK.png"},"loser":{"name":"NAME1","id":1,"picture_url":"NC.png"},"venue":{"name":"Venue 1","id":2}},
{"id":3,"winner":{"name":"NAME2","id":2,"picture_url":"CK.png"},"loser":{"name":"NAME3","id":3,"picture_url":"NJ.png"},"venue":{"name":"Venue 1","id":2}},
{"id":4,"winner":{"name":"NAME2","id":2,"picture_url":"CK.png"},"loser":{"name":"NAME1","id":1,"picture_url":"NC.png"},"venue":{"name":"Venue 1","id":2}}]
My object header files look like this:
# PTGame.h
#interface PTGame : NSObject
#property (nonatomic, assign) NSNumber *uid;
#property (nonatomic, assign) PTPlayer *winner;
#property (nonatomic, assign) PTPlayer *loser;
#property (nonatomic, assign) PTVenue *venue;
#end
# PTVenue.h
#interface PTVenue : NSObject
#property (nonatomic, copy) NSString *name;
#property (nonatomic, assign) NSNumber *uid;
-(CLLocation *)location;
-(CLLocationDistance)distanceFromLocation:(CLLocation *)location;
#end
# PTPlayer.h
#interface PTPlayer : NSObject
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *pictureUrl;
#property (nonatomic, assign) NSNumber *uid;
#end
Finally, my mapping setup looks like:
RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client];
RKObjectMapping *venueMapping = [RKObjectMapping mappingForClass:[PTVenue class]];
[venueMapping addAttributeMappingsFromDictionary:#{#"name" : #"name",
#"id" : #"uid"}];
RKObjectMapping *playerMapping = [RKObjectMapping mappingForClass:[PTPlayer class]];
[playerMapping addAttributeMappingsFromDictionary:#{#"name" : #"name",
#"id" : #"uid",
#"picture_url": #"pictureUrl"}];
// Add Object Mapping for PTGame
RKObjectMapping *gameResponseMapping = [RKObjectMapping mappingForClass:[PTGame class]];
// Setup Game Object relationships
[gameResponseMapping addAttributeMappingsFromDictionary:#{#"id": #"uid"}];
[gameResponseMapping addRelationshipMappingWithSourceKeyPath:#"winner" mapping:playerMapping];
[gameResponseMapping addRelationshipMappingWithSourceKeyPath:#"loser" mapping:playerMapping];
[gameResponseMapping addRelationshipMappingWithSourceKeyPath:#"venue" mapping:venueMapping];
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
RKResponseDescriptor *gameListResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:gameResponseMapping
method:RKRequestMethodGET
pathPattern:#"/games.json"
keyPath:nil
statusCodes:statusCodes];
[objectManager addResponseDescriptor:gameListResponseDescriptor];
This all looks right to me. No errors are thrown when the result is returned, but later I get an exception when attempting to access a property of the game. A peek into the objects via XCode debugger shows this:
Something seems to be broken - why do those strings show as "winner" and "venue". Why is the uid null? The objects seem different every time I make the request, leading me to believe the memory is not be handled properly.
Any ideas?

Wain's comment led me down the path of looking outside of the mapping code. I looked in the RestKit Object Mapping docs and noticed all of the attributes were using the nonatomic and copy properties, rather than assign. Additionally, the object references (such as PTPlayer *winner were simply nonatomic. Changing my code to reflect this fixed the issue.
The resulting header files look like:
# PTPlayer.h
#interface PTPlayer : NSObject
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *pictureUrl;
#property (nonatomic, copy) NSNumber *uid;
#end
# PTVenue.h
#interface PTVenue : NSObject
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSNumber *uid;
#property (nonatomic, assign) CGFloat longitude;
#property (nonatomic, assign) CGFloat latitude;
-(CLLocation *)location;
-(CLLocationDistance)distanceFromLocation:(CLLocation *)location;
#end
# PTGame.h
#interface PTGame : NSObject
#property (nonatomic, copy) NSNumber *uid;
#property (nonatomic) PTPlayer *winner;
#property (nonatomic) PTPlayer *loser;
#property (nonatomic) PTVenue *venue;
#end

Related

Realm Error: Property requires a protocol defining the contained type

I have the following model and I'm using Realm:
#interface GUIRoutineModel : GUIModel # GUIModel is a subclass of RLMObject
#property (nonatomic, retain) NSString *dateCreated;
#property (nonatomic, retain) NSString *dateModified;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *type;
#property NSInteger userId;
#property int routineId; #also have same issue using NSInteger
#end
When I call:
// Persist to Realm DB
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
[realm addObject:routineModel];
}];
I get the following error:
'Property 'routineId' requires a protocol defining the contained type - example: NSNumber<RLMInt>.'
I have tried changing the routineId property to NSNumber<RLMint>, but that didn't work either. Can anyone tell me what I'm doing wrong?
UPDATE:
Here is another version of the model that I have tried:
#interface GUIRoutineModel : GUIModel
#property (nonatomic, retain) NSString *dateCreated;
#property (nonatomic, retain) NSString *dateModified;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *type;
#property NSInteger userId;
#property NSNumber<RLMInt> *routineId;
#end
The Property requires a protocol defining the contained type error is only generated by Realm for a property of type NSNumber without a protocol annotating the expected concrete type. That means it cannot be generated for either of the model classes you mention. It's likely you have a another routineId property elsewhere in your app that is triggering the error.

RestKit: mapping to NSDictionary with values of specific type

I've got the following JSON coming from my webservice:
"GasPrices":{
"Ai92":{
"Price":30.1000,
"LastDate":"\/Date(1385337600000)\/",
"Votes":0
},
"Ai95":{
"Price":33.2000,
"LastDate":"\/Date(1385337600000)\/",
"Votes":0
}
I want to map it to NSDictionary, whose keys would be NSStrings and values would be of my custom class, say, PriceInfo.
What I got now with default setup is NSDictionary whose values are also NSDictionaries.
How can I achieve the desired mapping?
UPD. Here's my full setup for now.
#interface FillingStation : NSObject
#property (nonatomic, strong) NSNumber *uid;
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSDictionary *fuelPrices;
#end
#interface PriceInfo : NSObject
#property (nonatomic, strong) NSNumber *price;
#property (nonatomic, strong) NSDate *lastDate;
#property (nonatomic, strong) NSNumber *votes;
#end
Configuring the mapping:
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[FillingStation class]];
[mapping addAttributeMappingsFromDictionary:#{
#"Id" : #"uid",
#"Title" : #"title",
#"GasPrices" : #"fuelPrices"
}];
This results in fuelPrices is NSDictionary with the structure:
NSString -> NSDictionary,
NSString -> NSDictionary,
...
And I want it to be:
NSString -> PriceInfo,
NSString -> PriceInfo,
...
And I don't want an intermediate dictionary in FillingStation which I can then manually map.
Okay, seems like I found the answer. Not exactly what I wanted, yet acceptable for me: https://github.com/RestKit/RestKit/wiki/Object-Mapping#handling-dynamic-nesting-attributes
I had to add another property to PriceInfo class and change fuelPrices from NSDictionary to NSSet (NSArray would also work, but I don't need ordering), so it became:
#interface FillingStation : NSObject
#property (nonatomic, strong) NSNumber *uid;
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSSet *fuelPrices;
#end
#interface PriceInfo : NSObject
#property (nonatomic, strong) NSString *fuelType;
#property (nonatomic, strong) NSNumber *price;
#property (nonatomic, strong) NSDate *updateDate;
#property (nonatomic, assign) NSUInteger votes;
#end
and my mapping now looks like this:
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[FillingStation class]];
[mapping addAttributeMappingsFromDictionary:#{
#"Id" : #"uid",
#"Title" : #"title"
}];
RKObjectMapping *priceInfoMapping = [RKObjectMapping mappingForClass:[PriceInfo class]];
[priceInfoMapping setForceCollectionMapping:YES];
[priceInfoMapping addAttributeMappingFromKeyOfRepresentationToAttribute:#"fuelType"];
[priceInfoMapping addAttributeMappingsFromDictionary:#{
#"(fuelType).Price": #"price",
#"(fuelType).LastDate": #"updateDate",
#"(fuelType).Votes": #"votes"
}];
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"GasPrices"
toKeyPath:#"fuelPrices"
withMapping:priceInfoMapping]];
Jastor May be what you're looking for. Nested objects directly mapped to known classes.

iOS creating complex JSON

I have been struggling with this issue for some time now and I cant figure out why the JSON serialization is not working. What seems to be a simple task it taking way too long.
I have my model classes listed below. User object has NSArray of Education and Employment objects.
I am using RestKit and all of the relationships have been made and I can see that the dictionary of the parameters is being made the data is correct. In order to debug the issue further I am making JSON using the following code.
NSDictionary *parameters = [RKObjectParameterization parametersWithObject:self.user requestDescriptor:delegate.postUserRequestDescriptor error:&error];
// Serialize the object to JSON
BOOL isTurnableToJSON = [NSJSONSerialization
isValidJSONObject: parameters];
if(isTurnableToJSON){
NSData *JSON = [RKMIMETypeSerialization dataFromObject:parameters MIMEType:RKMIMETypeJSON error:&error];
}
isTurnableToJSON is always "NO". When I force creation of JSON I get the following error.
** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (Education)'
The same error is also thrown for employment if I remove the relationship of Education with user.
Here is my user, education, and location models.
#interface User : NSObject
#property (strong, nonatomic) NSString *first_name;
#property (strong, nonatomic) NSString *middle_name;
#property (strong, nonatomic) NSString *last_name;
#property (strong, nonatomic) NSString *profile_picture;
#property (strong, nonatomic) NSString *display_name;
#property (nonatomic, strong) NSArray *education;
#property (nonatomic, strong) NSArray *employment;
#property (nonatomic, strong) NSMutableArray *skills;
#property (strong, nonatomic) NSString *about_you;
#end
#interface Education : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *graduation_date;
#property (nonatomic, strong) NSString *major;
#property (nonatomic, strong) NSString *degree;
#property (nonatomic, strong) Location *location;
#property (nonatomic) NSNumber *current;
#end
#interface Location : NSObject
#property (nonatomic, strong) NSString *city;
#property (nonatomic, strong) NSString *state;
#property (nonatomic, strong) NSString *country;
#end
At this point I am at lose of any ideas and any help would be appreciated.
Update: RestKit Relationship mapping.
RKObjectMapping *locationMap = [RKObjectMapping mappingForClass:(Location.class)];
[locationMap addAttributeMappingsFromDictionary:#{#"city":#"city",#"state":#"state",#"country":#"country"}];
RKObjectMapping *educationMap = [RKObjectMapping mappingForClass:(Education.class)];
[educationMap addAttributeMappingsFromDictionary:# { #"name":#"name",#"graduation_date":#"graduation_date",#"major":#"major", #"degree":#"degree",#"current":#"current"}];
[educationMap addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"location" toKeyPath:#"location" withMapping:locationMap]];
RKObjectMapping *employmentMap =[RKObjectMapping mappingForClass:(Employment.class)];
[employmentMap addAttributeMappingsFromDictionary:#{#"company":#"company",#"occupation":#"occupation", #"start_date":#"start_date",#"end_date":#"end_date",#"current":#"current"}];
RKObjectMapping *userPutRequestMapping = [RKObjectMapping requestMapping];
[userPutRequestMapping addAttributeMappingsFromDictionary:putUserMap];
[userPutRequestMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"education" toKeyPath:#"education" withMapping:[educationMap inverseMapping]]];
[userPutRequestMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"employment" toKeyPath:#"employment" withMapping:[employmentMap inverseMapping]]];
USER PUT REQUEST
[objectManager addRequestDescriptor:[RKRequestDescriptor requestDescriptorWithMapping:userPutRequestMapping
objectClass:[User class]
rootKeyPath:nil
method:RKRequestMethodPUT]];

Attribute of relationship in Core Data is returning nil when being called in iOS

I have an iOS application where I am using Core Data for storage. I have two entities ("MyEntity", and "OtherEntity") which are in a one to one relationship. Both entities are related to each other (i.e. each entity has an inverse relationship with the other). In addition to having a relationship with each other, one of the attributes of each entity is also the primarykey of the other entity.
The problem I am having is that I realize that I shouldn't have an attribute that is the foreign key of another entity, when the entities have a relationship with each other, however I am unable to retrieve the primary key indirectly by referencing the relationship attribute, but I am able to retrieve it by referencing the attribute that I explicitly set to the primary key of the other entity:
//NSInteger userId = [testUser.otherEntity.userId integerValue]; --> returns nil
NSInteger userId = [testUser.userId integerValue]; --> works fine
where "testUser" is an instance of type "MyEntity" which is a subclass of NSManagedObject, and "otherEntity" is an instance of the Entity, "OtherEntity" that has an inverse relationship with "MyEntity".
Here are the attributes of each Entity:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class OtherEntity;
#interface MyEntity : NSManagedObject
#property (nonatomic, strong) NSString * email;
#property (nonatomic, strong) NSNumber * primaryId; //primary key for MyEntity
#property (nonatomic, strong) NSString * metaData;
#property (nonatomic, strong) NSDate * birthDate;
#property (nonatomic, strong) NSString * name;
#property (nonatomic, strong) NSNumber * userId; //this is the primary key for OtherEntity
#property (nonatomic, strong) OtherEntity *otherEntity;
#end
and here are the attributes for OtherEntity:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class MyEntity;
#interface OtherEntity : NSManagedObject
#property (nonatomic, strong) NSString * address;
#property (nonatomic, strong) NSString * city;
#property (nonatomic, strong) NSString * country;
#property (nonatomic, strong) NSString * fax;
#property (nonatomic, strong) NSString * phone;
#property (nonatomic, strong) NSString * postalCode;
#property (nonatomic, strong) NSString * province;
#property (nonatomic, strong) NSNumber * myUserId//primary key of MyEntity
#property (nonatomic, strong) NSNumber * userId;//primary key of Other Entity
#property (nonatomic, strong) MyEntity *myEntity;
#end
What is it that I'm doing wrong?
(From the comments:) You did not set a relationship for testUser,
Therefore testUser.otherEntity is nil.
Then testUser.otherEntity.userId is also nil and [testUser.otherEntity.userId integerValue] return zero.

RestKit - Not key-value coding compliant, no root in JSON

I've been trying to map the following object from the JSON response and from everything I see in the console output, there isn't any reason why the mapping isn't successful - I appreciate if anyone could have a check and see:
#interface RKElectionsModel : NSObject
#property (nonatomic, assign) bool isActive;
#property (nonatomic, strong) NSNumber *electionID;
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSString *summary;
#property (nonatomic, strong) NSNumber *availableSeats;
#property (nonatomic, strong) NSNumber *candidatesCount;
#property (nonatomic, strong) NSNumber *withdrawnCount;
#property (nonatomic, strong) NSSet *candidates;
#end
/**
* Election Detail Mapping: Getting all election details, we have some extra information from
* the API call
*
*/
RKObjectMapping *electionDetailsMapping = [RKObjectMapping mappingForClass:[RKElectionsModel class]];
// Map JSON -> entities
[electionDetailsMapping addAttributeMappingsFromDictionary:#{
#"id": #"electionID",
#"title": #"title",
#"summary": #"summary",
#"active": #"isActive",
#"availableSeats": #"availableSeats",
#"candidatesCount": #"candidatesCount",
#"withdrawnCount": #"withdrawnCount"
}];
// Register our mappings with the provider
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:electionDetailsMapping pathPattern:#"/api/elections/:electionID/all" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
Console output on pastebin and any examples of the JSON response you can visit here
Appreciate any help,
Lewis
Seems like your trying to map a NSString to NSNumber.
In your NSObject try to add the following validation and transform the string you receive from the server to a NSNumber when the mapping starts.
- (BOOL)validateAvailableSeats:(id *)ioValue error:(NSError **)outError {
// Force the value to be a NSNumber
*ioValue = [NSNumber numberWithDouble:[(NSString*)value doubleValue] ]
return YES;
}
In the RestKit Wiki you'll find more transformations for your values.

Resources