RestKit ios - put - json instead of form encoded - objective-c

i am writing an ios app that uses restkit to communicate with a web server through Rest with JSON
i am able to use [[RKObjectManager sharedManager] loadObjectsAtResourcePath:path delegate:self] to get object from my web service as JSON, map it to obj-c object, it works fine
now i am trying to use: [[RKObjectManager sharedManager] putObject:obj delegate:self]; and this call sends an object to the web service as form encoded and not JSON
so my question is: how to configure the sharedManager (or the routeur?) to send with content type JSON instead of form encoded.
any code example much appreciated.
Thx!

The easiest way is to simply set the property when you initialize the object manager, like so:
RKObjectManager* objectManager = [RKObjectManager objectManagerWithBaseURL:#"http://url.com"];
objectManager.serializationMIMEType = RKMIMETypeJSON;

Evan is correct, but I've had to also make sure I am sending a JSON string, because I had a nested NSDictionay.
If you have a dictionary you want to send as a JSON string, here's how you can do it:
// create a JSON string from your NSDictionary
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict
options:NSJSONWritingPrettyPrinted // Pass 0 if you don't care about the readability of the generated string
error:&error];
NSString *jsonString = [[NSString alloc] init];
if (!jsonData) {
NSLog(#"Got an error: %#", error);
} else {
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
// make the post using the objectManager if you want to map the response to a model
RKObjectManager* objectManager = [RKObjectManager sharedManager];
[objectManager loadObjectsAtResourcePath:#"/api/" delegate:self block:^(RKObjectLoader* loader) {
loader.serializationMIMEType = RKMIMETypeJSON; // We want to send this request as JSON
loader.objectMapping = [objectManager.mappingProvider objectMappingForClass:[Plan class]];
loader.resourcePath = #"/api/";
loader.method = RKRequestMethodPOST;
loader.params = [RKRequestSerialization serializationWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding] MIMEType:RKMIMETypeJSON];
}];

Okay just found how to do it:
subclass RKRouter.h or just change in RKDynamicRouter.m
return [object propertiesForSerialization];
to
[RKJSONSerialization JSONSerializationWithObject:[object propertiesForSerialization]];
and RestKit generate JSON for putObject call

Create an Object Manager and set the property for matching the header in JSON format
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:#"http://mobile.com"]];
[objectManager addResponseDescriptorsFromArray:#[responseDescriptor]];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;

You can change serializationMIMEType for individual requests by subclassing RKObjectManager and change implementation of requestWithObject:method:path:parameters: in subclassed manager.
Send request:
SubclassedObjectManager *manager = ...
[manager putObject:nil
path:pathString
parameters:parameters
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
}
];
Modify MIMEType of request for PUT method:
- (NSMutableURLRequest *)requestWithObject:(id)object method:(RKRequestMethod)method path:(NSString *)path parameters:(NSDictionary *)parameters
{
NSMutableURLRequest *request = [super requestWithObject:object method:method path:path parameters:parameters];
if (method&RKRequestMethodPUT) {
NSError *error = nil;
NSData *serializedJSON = [RKMIMETypeSerialization dataFromObject:parameters MIMEType:RKMIMETypeJSON error:&error];
[request setValue:RKMIMETypeJSON forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:serializedJSON];
}
return request;
}

Related

Parsing of NSDictionary of NSArray

I am using Restkit API to parse a JSON Reply, which I have received from my webserver. The reply from the webserver is in this form.scre
JSON reply is this
{"Menu Items":[
{
"ID":393,
"Title":"Lentil Soup",
"Description":"This is extremely tasty",
"Image":"Lentil_1.png"
},
{
"ID":392,
"Title":"Chicken Tortilla Soup",
"Description":"Quick. Simple. Delicious.":"$20",
"Image":"Tortilla_3.png"
}
]
}
I am using the below code to get the response, but fails to get any response.
NSURL *baseURL = [NSURL URLWithString:#"http://URL.com"];
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:baseURL]; // init by url
[client setDefaultHeader:#"Accept" value:RKMIMETypeJSON];
[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class] forMIMEType:#"text/html"];
//set up restkit
RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client];
//setup object mapping
//addAttributeMappingsFromArray is a shortcut method to use when the JSON and your data model share the same keys, which is “name” in your case.
RKObjectMapping* boyMapping = [RKObjectMapping mappingForClass:[ListItem class] ];
[boyMapping addAttributeMappingsFromArray:#[#"ID"]];
// register mappings with the provider using a response descriptor
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:boyMapping
method:RKRequestMethodAny
pathPattern:#"/demo/restaurant/app/menu_lists.php"
keyPath:#"Menu Items"
statusCodes:[NSIndexSet indexSetWithIndex:200]];
[objectManager addResponseDescriptor:responseDescriptor];
NSDictionary *parameterDict = [NSDictionary dictionaryWithObjectsAndKeys:_slugString, #"item_slug", nil];
[[RKObjectManager sharedManager] getObjectsAtPath:#"/demo/restaurant/app/menu_lists.php"
parameters:parameterDict
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
_menuItems = mappingResult.array;
NSLog(#"menuItems is %#", _menuItems);
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"What do you mean by 'there is no coffee?': %#", error);
}];
every time it go to the failure Block.
(assuming the response received has a status code of 200)
You create an object manager:
RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client];
then you configure it. But, then you try to use it with:
[[RKObjectManager sharedManager] getObjectsAtPath:...
and this is using a different instance that has been created somewhere else in the code and doesn't have any of the configuration you set on objectManager.
You need to reorganise your code to ensure you create and appropriately use your object manager(s).

How to POST a simple object's data to Server using RestKit 0.20

I am new in RestKit. I did not find a proper documentation or tutorial to send simple object data to Restful API.
Here is my problem in detail.
I have a class with name User having two properties for now: email and password.
I want to send them to server using RestKit 0.20.
I found some tutorials but all of them are outdated for RestKit v 0.10. I found this question but this is outdated as well. There is no sharedInstance selector of class RKObjectManager in RestKit 0.20 but sharedManager.
Any help would be great.
Finally I found the solution. Thanks #Mateusz for helping me out.
Here is the solution.
// Construct a request mapping for User
RKObjectMapping *requestMapping = [RKObjectMapping requestMapping];
[requestMapping addAttributeMappingsFromDictionary:#{ #"email": #"email", #"password": #"password" }];
// construct a response mapping for User
RKObjectMapping *responseMapping = [RKObjectMapping mappingForClass:[User class]];
[responseMapping addAttributeMappingsFromDictionary:#{#"email": #"email", #"password": #"password", #"guid": #"guid"}];
RKRequestDescriptor *req = [RKRequestDescriptor requestDescriptorWithMapping:requestMapping objectClass:[User class] rootKeyPath:#"user" method:RKRequestMethodPOST];
RKResponseDescriptor *res = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping method:RKRequestMethodAny pathPattern:nil keyPath:#"user" statusCodes:[NSIndexSet indexSetWithIndex:200]];
// Register our descriptors with a manager
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:#"http://localhost/api/user/"]];
[manager addRequestDescriptor:req];
[manager addResponseDescriptor:res];
// preparing sending User object
User *user = [User new];
user.email = #"example#example.com";
user.password = #"password";
NSLog(#"user email : %#", user.email);
[manager postObject:user path:#"user" parameters:#{#"api_key": MY_API_KEY} success:^(RKObjectRequestOperation *operation, RKMappingResult *result) {
NSArray *arr = [result array];
User *temp= [arr objectAtIndex:0];
NSLog(#"SUCCESS ---------------------------- User's email: %#", temp.email);
NSLog(#"User's guid: %#", temp.guid);
// NSLog(#"--------- - --- -- - all resutl: %#", result);
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"failed post %#", error);
NSLog(#"%#",operation.description);
NSLog(#"%#",operation.HTTPRequestOperation.description);
}];

AFNetworking getting data for XML parse error

This is my AFHTTPClient singleton:
+ (API *)sharedInstance
{
static API *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[API alloc] initWithBaseURL:[NSURL URLWithString:kAPIHost]];
[sharedInstance setParameterEncoding:AFJSONParameterEncoding];
[sharedInstance registerHTTPOperationClass:[AFXMLRequestOperation class]];
[sharedInstance setDefaultHeader:#"Accept" value:#"application/rss+xml"];
});
return sharedInstance;
}
And method in same class (AFHTTPClient):
- (void)requestXMLDataCompletion:(JSONResponseBlock)completionBlock
{
NSMutableURLRequest *apiRequest = [self requestWithMethod:#"GET" path:kAPIPath parameters:nil];
AFXMLRequestOperation *operation = [[AFXMLRequestOperation alloc] initWithRequest:apiRequest];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){
// success
completionBlock(responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// failure
completionBlock([NSDictionary dictionaryWithObject:[error localizedDescription] forKey:#"error"]);
}];
[operation start];
}
When I call this function to get XML from RSS I get this error:
error = "Expected content type {(\n \"application/xml\",\n \"text/xml\"\n)}, got application/rss+xml";
Question:
Is whole concept of implemented singleton good and do I need any changes ?
Is there any suggestion if whole concept is wrong ?
Why am I getting this error?
Thanks.
Concept of Singleton
A singleton is more commonly known as a design pattern.
Usually a singleton is a class and behaves exactly like any other class,
the only exception being that any instances of a singleton reference the
same object data. This means that any instance of a singleton class are
actually all the same instance.
You can check out Singleton Pattern for more information and sample code to enforce how the singleton will be used.
Is there any suggestion if whole concept is wrong ?
I would suggest you to use Singleton for AFNetworking since you will have
only one instance of it.
Your Error
The error you are getting is because AFNetworking request wants Header Content-Type as "application/xml" or "text/xml"
Try changing this code:
[self registerHTTPOperationClass:[AFXMLRequestOperation class]];
to
[self registerHTTPOperationClass:[AFHTTPRequestOperation class]];
I had a similar problem:
Error Domain=AFNetworkingErrorDomain Code=-1016 "Expected content type {(
"text/xml",
"application/xml"
)}, got application/rss+xml"
The answer above is not full and clear, although it helped me a lot after I read their chat. registerHTTPOperationClass doesn't help. I decided to provide some code. Solution is to NOT use this:
[AFXMLRequestOperation XMLParserRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, NSXMLParser *XMLParser)
But download RSS XML using AFHTTPRequestOperation and create NSXMLParser manually:
NSString *articlesUrlString = #"http://pro.rabota.ru/feed/moscow.content.rss";
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:articlesUrlString]];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET" path:#"" parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSData *xmlData = (NSData *)responseObject;
NSXMLParser *XMLParser = [[NSXMLParser alloc] initWithData:xmlData];
XMLParser.delegate = self;
[XMLParser parse];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", error);
}];

JSON Response in postPath AFHTTPClient

I just switched to RestKit 0.2 and I am currently using the new "HttpClient" which is basically a AFHTTPClient. I have this line of code:
RKObjectManager* objectManager = [RKObjectManager sharedManager];
NSDictionary* params = [[NSDictionary alloc] initWithObjectsAndKeys: login, #"username", password, #"password", nil];
[[objectManager HTTPClient]postPath:#"users/login/?format=json" parameters:params
success:^(AFHTTPRequestOperation *operation, id responseObject)
{
//reponseObject vs operation.response
NSLog(#"%#", responseObject);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"ERROR");
}];
This POST calls return a JSON response in the form: {"api_key":"....","username":"...."}. As simple as that.
Before switching to 0.2, I was able to get the api_key key in the response by doing:
[[RKClient sharedClient] post:#"/users/login/?format=json" usingBlock:^(RKRequest *request)
{
request.onDidLoadResponse = ^(RKResponse *response)
{
id parsedResponse = [response parsedBody:NULL];
NSString *apiKey = [parsedResponse valueForKey:#"api_key"];
}
}.....];
http://restkit.org/api/master/Classes/RKResponse.html
But now, I can't do that and if I do a NSLog on the responseObject, I get:
<7b227265 61736f6e 223a2022 41504920 4b657920 666f756e 64222c20 22617069 5f6b6579 223a2022 61356661 65323437 66336264 35316164 39396338 63393734 36386438 34636162 36306537 65386331 222c2022 73756363 65737322 3a207472 75657d>
And the weird thing is that if I do:
NSLog(#"%#", operation.responseString);
I do have the JSON (in NSString) showing up.
So two questions:
1) Why is printing the responseObject showing me HEX code, and not the actually JSON response?
2) Why if I do operation.responseString it is showing the actual Response Object? Is there a way to get the actual data in ResponseObject after being parsed from the JSON?
AFNetworking should instantiate a AFJSONRequestOperation. Probably it creates a basic AFHTTPRequestOperation instead (check [operation class]) resulting in a NSData object as response.
Make sure you register the operation class in the init method of your AFHTTPClient subclass (initWithBaseURL):
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
// Accept HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
[self setDefaultHeader:#"Accept" value:#"application/json"];
You could also try to use AFJSONRequestOperation directly like this:
NSURLRequest *request = [[objectManager HTTPClient] requestWithMethod:#"POST" path:#"users/login/?format=json" parameters:params];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"JSON: %#", JSON);
} failure:nil];
[[objectManager HTTPClient] enqueueHTTPRequestOperation:operation];
What you are seeing, if I'm not mistaken, is the raw bytes from the NSData that is given to you when your success block is called.
The hex you posted reads:
{"reason": "API Key found", "api_key": "a5fae247f3bd51ad99c8c97468d84cab60e7e8c1", "success": true}
The reason the second NSLog shows you what you want is that the %# format string calls the description (correct me if I'm wrong here, SO) of the object you pass it and the NSData probably knows it is a string underneath.
So, on to how to get the JSON. It is really rather simple. Once you have your response object, you can do something like this:
NSDictionary* jsonFromData = (NSDictionary*)[NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:&error];
What this will do for you is use return an NSDictionary which encodes the root object in the JSON and then each value in the dictionary will be of the type NSString, NSNumber, NSArray, NSDictionary, or NSNull. See NSJSONSserialization for documentation.
The NSJSONReadingMutableContainers makes the dictionaries and arrays mutable. It's just a leftover from my code.
Hopefully you're on iOS 5 or later, or you'll need to find another solution for the parsing.
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSData *responseData = operation.HTTPRequestOperation.responseData;
id parsedResponse = [RKMIMETypeSerialization objectFromData:responseData MIMEType:RKMIMETypeJSON error:nil];
NSString *apiKey = [parsedResponse valueForKey:#"api_key"]
}

How to access secured url from ios

I'm trying to access a secure url from ios. Basically url will prompt the user with Username and Password. How can I send username and Password from ios?
My Code
Here is the Methods that I'm using to access JSON Parser
- (NSString *)stringWithUrl:(NSURL *)url{
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReturnCacheDataElseLoad
timeoutInterval:30];
// Fetch the JSON response
NSData *urlData;
NSURLResponse *response;
NSError *error;
// Make synchronous request
urlData = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
// Construct a String around the Data from the response
return [[[NSString alloc] initWithData:urlData encoding:NSUTF8StringEncoding] autorelease];}
- (id) objectWithUrl:(NSURL *)url{
SBJsonParser *jsonParser = [[[SBJsonParser alloc]init] autorelease];
NSString *jsonString = [self stringWithUrl:url];
// Parse the JSON into an Object
return [jsonParser objectWithString:jsonString error:nil]; }
Here is the piece of code that I'm retrieving json keys to my dictionary.
- (NSDictionary *) downloadFeed {
id response = [self objectWithUrl:[NSURL URLWithString:#"http://mysite.com/Services/Secure.svc/GetList?id=2127"]];
NSDictionary *feed = (NSDictionary *)response;
return feed; }
Can someone let me know where can I pass the Username and Password to this url?
Either switch to ASIHTTPRequest, which handles Basic authentication simply, or use a NSMutableRequest and set the Authorization header correctly, with a base64 encoded user:password pair.