I'm trying to find a way to properly map a collection of objects to a JSON dictionary with a specific format.
I have an object with the following interface (partial):
#interface Reward : NSObject
...
#property (nonatomic, copy) NSString* title;
#property (nonatomic, copy) NSString* comment;
#property (nonatomic, strong) NSMutableOrderedSet* receivers; //set of User
...
#end
And the User object (partial) is:
#interface User : NSManagedObject
...
#property (nonatomic, strong) NSNumber* userId
...
#end
The goal is to POST a Reward object, along with the receivers property.
I could come up with an RKObjectMapping that works for title and comment attributes, but the receivers collection requires the following format:
"receivers":{"0":"<user_id_of_first_user>", "1":"<user_id_of_second_user>", ...}
My main problem is how to insert the index as a key.
I could do it manually and tweak the NSURLRequest HTTPBody, but I was hoping to find a more clean/RestKit way.
Thanks in advance.
if you want to do that's you need, as you said, use NSMutableURLRequest and add in your reward method request which gives NSString with your json this simple code can help you and other developers like me:
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[RKObjectManager baseUrl]];
Reward *reward = [[Reward alloc] init];
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[Reward class]];
RKResponseDescriptor *desc = [RKResponseDescriptor responseDescriptorWithMapping:mapping method:RKRequestMethodPOST pathPattern:nil keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[manager addResponseDescriptor:desc];
RKObjectManager *objectManager = [RKObjectManager sharedManager];
NSMutableURLRequest *request =
[objectManager requestWithObject:reward method:RKRequestMethodPOST path:#"yourpath" parameters:nil];
NSString *str = [NSString stringWithFormat:#"\"receivers\":%#",reward.request];
[request setHTTPBody:[str dataUsingEncoding:NSUTF8StringEncoding]];
RKObjectRequestOperation *operation = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:#[desc]];
[operation setCompletionBlockWithSuccess:success failure:failure];
operation.targetObject = reward;
[operation start];
I hope it`s help someone
Related
{"coord":{"lon":72.62,"lat":23.03},"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],"base":"stations","main":{"temp":303.082,"pressure":1014.85,"humidity":66,"temp_min":303.082,"temp_max":303.082,"sea_level":1018.46,"grnd_level":1014.85},"wind":{"speed":1.07,"deg":340.501},"rain":{"3h":0.435},"clouds":{"all":76},"dt":1475333911,"sys":{"message":0.0033,"country":"IN","sunrise":1475283682,"sunset":1475326567},"id":1279233,"name":"Ahmadabad","cod":200}
Above is my API response.
Now I want to mapping of "weather" and "name" and want the same object as a response.
I can create to class
#interface WeatherStatus : NSObject
#property (nonatomic, copy) NSString *name;
#property (nonatomic, strong) WeatherInfo *info;
#end
and
#interface WeatherInfo : NSObject
#property (nonatomic, copy) NSString *description;
#property (nonatomic, copy) NSString *icon;
Below is mapping code.
RKObjectMapping *weatherInfo = [RKObjectMapping mappingForClass:[WeatherInfo class]];
[weatherInfo addAttributeMappingsFromDictionary:#{#"description": #"description", #"icon": #"icon"}];
RKObjectMapping *weatherStatus = [RKObjectMapping mappingForClass:[WeatherStatus class]];
[weatherStatus addAttributeMappingsFromArray:#[#"name"]];
[weatherStatus addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"weather" toKeyPath:#"weather" withMapping:weatherInfo]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:weatherStatus method:RKRequestMethodGET pathPattern:nil keyPath:#"weather" statusCodes:nil];
[objectManager addResponseDescriptor:responseDescriptor];
NSDictionary *queryParams;
queryParams = [NSDictionary dictionaryWithObjectsAndKeys:kAPP_KEY, #"appid", #"Ahmedabad", #"q", nil];
[[RKObjectManager sharedManager] getObjectsAtPath:#"/data/2.5/weather" parameters:queryParams success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
WeatherStatus *obj = [mappingResult.array objectAtIndex:0];
NSLog(#"info %#",obj.info);
NSLog(#"name %#",obj.name);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"What do you mean by 'there is no coffee?': %#", error);
}];
I am getting
info (null)
name (null)
Can anyone let me know where is the mistake?
I have already seen RestKit complex and simple JSON RKObjectMapping almost working, but
Don't use description as a property name, it'll only cause you problems. Use overview or something similar instead.
In the JSON, the weather is an array, so you should make your weather (info) property a NSArray, and be sure the name in the mapping and the property match.
I change the property of info object in "WeatherStatus" class to NSArray.
#property (nonatomic, copy) NSString *name;
#property (nonatomic, strong) NSArray *weather;
Here is the modification of mapping code.
RKObjectMapping *venueMapping = [RKObjectMapping mappingForClass:[WeatherStatus class]];
[venueMapping addAttributeMappingsFromDictionary:#{#"name": #"name"}];
RKObjectMapping *locationMapping = [RKObjectMapping mappingForClass:[WeatherInfo class]];
[locationMapping addAttributeMappingsFromDictionary:#{#"description": #"description",#"icon":#"icon"}];
[venueMapping addRelationshipMappingWithSourceKeyPath:#"weather" mapping:locationMapping];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:venueMapping method:RKRequestMethodGET pathPattern:nil keyPath:nil statusCodes:nil];
[objectManager addResponseDescriptor:responseDescriptor];
Add relationship mapping with WeatherStatus class.
Credit goes to https://stackoverflow.com/users/1988185/wain
I have a JSON response from REST service I am using RESTKit and its not getting mapped , below is the source for the same
RKObjectMapping *userMapping = [RKObjectMapping requestMapping];
[userMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:#"strCode" toKeyPath:#"UserCode"]];
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:userMapping
objectClass:[User class]
rootKeyPath:nil method:RKRequestMethodPOST];
RKObjectMapping *responseMapping = [RKObjectMapping mappingForClass:[User class]];
[responseMapping addAttributeMappingsFromDictionary:#{ #"Id":#"Id",#"UserCode":#"strCode",#"FirstName": #"strFname", #"LastName": #"strLname",#"Email":#"strEmail",#"PhoneNumber":#"strPhoneNumber",#"CompanyName":#"strCompany",#"Address":#"strAddress",#"Abn":#"strAbn"}];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping
method:RKRequestMethodAny
pathPattern:nil
keyPath:nil
statusCodes:[NSIndexSet indexSetWithIndex:200]];
RKObjectManager *manager = [RKObjectManager sharedManager];
[manager addRequestDescriptor:requestDescriptor];
[manager addResponseDescriptor:responseDescriptor];
RKObjectMapping *errResponseMapping = [RKObjectMapping mappingForClass:[ServiceError class]];
[errResponseMapping addAttributeMappingsFromDictionary:#{ #"ErrorMessage": #"strErrorMessage", #"FriendlyMessage": #"strFriendlyMessage"}];
RKResponseDescriptor *errResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:errResponseMapping
method:RKRequestMethodAny
pathPattern:nil
keyPath:nil
statusCodes:[NSIndexSet indexSetWithIndex:200]];
[manager addResponseDescriptor:errResponseDescriptor];
manager.requestSerializationMIMEType = RKMIMETypeJSON;
User *user = [user new];
user.strCode = txtCode.text;
// POST the parameterized representation of the `page` object to `/posts` and map the response
[manager postObject:user path:[ServiceUrls userDetails] parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *result) {
NSlog(#"%d",result.count);
}
} failure:nil];
The user class looks like this
#interface User : NSObject
{
}
#property (nonatomic,retain) NSNumber *Id;
#property (nonatomic,retain) NSString *strCode;
#property (nonatomic,retain) NSString *strFname;
#property (nonatomic,retain) NSString *strLname;
#property (nonatomic,retain) NSString *strEmail;
#property (nonatomic,retain) NSString *strPhoneNum;
#property (nonatomic,retain) NSString *strCompany;
#property (nonatomic,retain) NSString *strAddress;
#property (nonatomic,retain) NSString *strAbn;
#end
JSON response that i get but isn't mapping is as follows
{"Id":7,
"UserCode":"CC1234",
"FirstName":"Test name_",
"LastName":"Banga",
"Email":"p#b.com",
"PhoneNumber":"0421196587",
"CompanyName":"String",
"Address":"String",
"Abn":"String"}
Not sure whats wrong with the code I have added, any clue ?
Maybe try this.
Mapping:
-(void) getUserMapping {
RKEntityMapping *userEntityMapping = [self generateEntityMappingFromClass:[User class]];
userEntityMapping.identificationAttributes = #[#"Id"];
return userEntityMapping;
}
Generate Mapping:
+ (RKEntityMapping *)generateEntityMappingFromClass:(Class)objectClass {
NSAssert(objectClass != NULL, #"Cannot generate a mapping from a null object class.");
NSDictionary *propertiesAndClasses = [[RKPropertyInspector sharedInspector] propertyInspectionForClass:objectClass];
NSAssert([propertiesAndClasses count] > 0, #"Cannot generate a mapping from a class containing zero properties.");
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass(objectClass) inManagedObjectStore: [RKManagedObjectStore defaultStore]];
NSMutableDictionary *mappingDictionary = [[NSMutableDictionary alloc] init];
for (NSString *property in [propertiesAndClasses allKeys]) {
NSLog(#"property: %#", property);
[mappingDictionary setObject:property forKey:property];
}
[mapping addAttributeMappingsFromDictionary:mappingDictionary];
return mapping;
}
Response descriptor:
RKResponseDescriptor *responseDescriptorBody = [RKResponseDescriptor responseDescriptorWithMapping:[self getUserMapping] method:RKRequestMethodGET pathPattern:#"" keyPath:#"" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
I think your problem is JSON without keyPath, try to change it from nil to #""
I can not seem to figure out how to map the following JSON, I am trying to map the hostedLargeUrl from the response. I am not sure what to do for this issue, and would like to apologize if my info is not detailed enough. Not too sure what type of details you would need.
Thank you in advance.
images: [
{
imageUrlsBySize: {
90: "http://lh4.ggpht.com/ZXiwjS55Zk7oBu6GWaVr0HAqIPKumXwBfGtzsCWEFdrJSOXiCcC-I3TpUwrXBnP_DPNuBm-ib-4-3aXbs4mfXA=s90-c",
360: "http://lh4.ggpht.com/ZXiwjS55Zk7oBu6GWaVr0HAqIPKumXwBfGtzsCWEFdrJSOXiCcC-I3TpUwrXBnP_DPNuBm-ib-4-3aXbs4mfXA=s360-c"
},
hostedLargeUrl: "http://i.yummly.com/Pasta-with-garlicky-broccoli-rabe-305651-270310.l.jpg",
hostedSmallUrl: "http://i.yummly.com/Pasta-with-garlicky-broccoli-rabe-305651-270310.s.jpg"
}
Here is my code:
+ (RKMapping *)recipeDetailMapping
{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[RecipeDetails class]];
[mapping addAttributeMappingsFromDictionary:#{
#"attribution.text" : #"attributionText",
#"images.hostedLargeUrl" : #"images"
}];
[mapping addAttributeMappingsFromArray:#[#"ingredientLines",
#"name",
#"totalTime",
]];
return mapping;
}
RecipeDetails
#property (nonatomic, copy) NSArray *ingredientLines;
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *totalTime;
#property (nonatomic, copy) NSString *attributionText;
#property (nonatomic, copy) NSString *images;
Last bit of code
- (void)loadRecipeDetails
{
NSIndexSet *statusCodeSet = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
RKMapping *mapping = [MappingProvder recipeDetailMapping];
NSString *resourcePath = [NSString stringWithFormat:#"/v1/api/recipe/%#", self.recipeInfo.recipeId];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping
method:RKRequestMethodGET
pathPattern:resourcePath
keyPath:nil
statusCodes:statusCodeSet];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"https://api.yummly.com/v1/api/recipe/%#?_app_id=%#&_app_key=%#&requirePictures=true", self.recipeInfo.recipeId
,Yummly_APP_ID , Yummly_API_kEY ]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
RKObjectRequestOperation *operation = [[RKObjectRequestOperation alloc] initWithRequest:request
responseDescriptors:#[responseDescriptor]];
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
self.recipeData = mappingResult.array;
[self updateUI];
[SVProgressHUD dismiss];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
NSLog(#"Response: %#", operation.HTTPRequestOperation.responseString);
[SVProgressHUD showErrorWithStatus:#"Request Failed"];
}];
[operation start];
}
The source data is in an array and you need to deal with that in some way.
Option 1.
Change the property to NSArray *images;
Option 2.
Add a custom method setImageArray: and implement it to extract the first image and store it. Then change the mapping to use a destination key of imageArray.
Other options really require a different object graph...
I'm new using RestKit, but I can't understand at all how it works...
please, can somewhere explain me it??
My Json file is:
{
"colors":
{
"red":"#f00",
"green":"#0f0",
"blue":"#00f",
"cyan":"#0ff",
"magenta":"#f0f",
"yellow":"#ff0",
"black":"#000"
}
}
and the path where i'm hosting this file is: http://186.36.181.116/tesis/file.json
The code that I'm trying in my ViewDidLoad method is:
- (void)viewDidLoad
{
[super viewDidLoad];
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[colores class]];
[mapping addAttributeMappingsFromArray:#[#"colors"]];
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful); // Anything in 2xx
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping method:RKRequestMethodAny pathPattern:#"/tesis/:coloresID" keyPath:#"colors" statusCodes:statusCodes];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://186.36.181.116/tesis/file.json"]];
RKObjectRequestOperation *operation = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:#[responseDescriptor]];
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *result) {
colores *colores = [result firstObject];
NSLog(#"Mapped the article: %#", colores);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failed with error: %#", [error localizedDescription]);
}];
[operation start];
}
My Class "colores" is as follow:
#import <Foundation/Foundation.h>
#interface colores : NSObject{}
#property (weak, nonatomic) IBOutlet NSString *colores;
#end
Thank you so much in advance!!
you can find a detailed tutorial here and full source code github.
In order to properly map responses to JSON, we must do the following things:
*Create an instance of RKEntityMapping for each entity in our managed object model
*Add mappings between JSON response keys and object properties
*Add mappings between embedded JSON objects and relationships
*Create response descriptors with the mappings
*Optional: Create request descriptors with the mappings if you plan to PUT or POST
I'm having a very hard time finding documentation or examples of creating a new managed object, setting it's values, and saving to the server using Restkit.
I have a NSManagedObject Post:
#interface Post : NSManagedObject
#property (nonatomic, retain) NSNumber * postID;
#property (nonatomic, retain) NSString * title;
#property (nonatomic, retain) NSString * text;
#end
This is my AppDelegate Setup:
// ---- BEGIN RestKit setup -----
RKLogConfigureByName("RestKit/Network", RKLogLevelTrace);
NSError *error = nil;
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"My_App" ofType:#"momd"]];
// NOTE: Due to an iOS 5 bug, the managed object model returned is immutable.
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
// Enable Activity Indicator Spinner
[AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
// Initialize the Core Data stack
[managedObjectStore createPersistentStoreCoordinator];
NSPersistentStore __unused *persistentStore = [managedObjectStore addInMemoryPersistentStore:&error];
NSAssert(persistentStore, #"Failed to add persistent store: %#", error);
[managedObjectStore createManagedObjectContexts];
// Configure a managed object cache to ensure we do not create duplicate objects
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
// Set the default store shared instance
[RKManagedObjectStore setDefaultStore:managedObjectStore];
// Configure the object manager
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:#"http://localhost:3000/api/v1"]];
objectManager.managedObjectStore = managedObjectStore;
NSString *auth_token = [[LUKeychainAccess standardKeychainAccess] stringForKey:#"auth_token"]; // Getting the Auth_Token from keychain
[objectManager.HTTPClient setAuthorizationHeaderWithToken:auth_token];
[RKObjectManager setSharedManager:objectManager];
// Setup Post entity mappping
RKEntityMapping *postMapping = [RKEntityMapping mappingForEntityForName:#"Post" inManagedObjectStore:managedObjectStore];
[postMapping addAttributeMappingsFromDictionary:#{
#"title": #"title",
#"text": #"text",
#"id": #"postID"}];
RKResponseDescriptor *postResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:postMapping pathPattern:nil keyPath:#"post" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:postResponseDescriptor];
Now, In my NewPostViewController when I click my "Save" button I have in my navbar, what do I need to do to save this post to my server?
Here's what I've tried, but it's not working correctly. I enter the success block and my server got the POST, but the fields are nil:
- (void)savePost {
RKManagedObjectStore *objectStore = [[RKObjectManager sharedManager] managedObjectStore];
Post *post = [NSEntityDescription insertNewObjectForEntityForName:#"Post" inManagedObjectContext:objectStore.mainQueueManagedObjectContext];
[post setTitle:#"The Title"];
[post setText:#"The Text"];
[[RKObjectManager sharedManager] postObject:post path:#"posts" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"Success saving post");
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failure saving post: %#", error.localizedDescription);
}];
}
It looks like you haven't added any RKRequestDescriptor's to your object manager. Without them, the poor object manager can't use key/value magic to serialize your object into an NSDictionary.
The thing that you HAVE added, the RKResponseDescriptor, describes how responses are managed. That's why you're seeing the success block called: RestKit has no idea what you're trying to send, but it recognizes the Post objects in the server response.
Try adding this below your responseDescriptor code:
RKRequestDescriptor * requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[postMapping inverseMapping] objectClass:[Post class] rootKeyPath:#"post"];
[objectManager addRequestDescriptor:requestDescriptor];
(double check the keypath; I don't know what your API expects, so I went with what you had in the response descriptor)