JSON Response in postPath AFHTTPClient - objective-c

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"]
}

Related

Handling response with raw data using RestKit

I am using RestKit 0.23.3.
For a particular path pattern (say download_data/:dataId):
I can send a request via RestKit without any problem,
I expect a response with arbitrary Content Type (image/png, text/plain, text/xml,... or even application/json),
If 2xx status code is returned by the server, I want to receive the body of HTTP response as NSData instance (regardless of Content Type) in the success handler block.
Is this possible (and how) using RestKit?
Thanks.
With new restkit 0.20.3 you can execute your rest web-service like:
[[AppDelegate appDelegate].rkomForProbeList getObjectsAtPath:[NSString stringWithFormat:kResource_Probe,[[NSUserDefaults standardUserDefaults] valueForKey:kNS_DF_AccessRef]] parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"%#",operation.HTTPRequestOperation.responseString);
// if raw data
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:[operation.HTTPRequestOperation.responseString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:NULL];
// if mapped
arrayForProbe = [[NSMutableArray alloc] initWithArray:mappingResult.array];
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"%#",operation.HTTPRequestOperation.responseString);
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:[,operation.HTTPRequestOperation.responseString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:NULL];
NSLog(#"%#",operation.HTTPRequestOperation.response.statusCode);
NSLog(#"%#",error.description);
[self hideViewContentAsProbesNotAvailable];
}];
By mapping you will get your object directly from mappingarray. For parsing raw data you have get json object from response string.
rkomForProbeList is your RKObjectManager's instance.
kResource_Probe is resource path. Like if base URL is http://www.hi.com/ and your rest api required http://www.hi.com/login then "login" will be your resource path.
I'm not familiar with RESTKit, but anyway maybe it will help you.
I suggest you to use NSURLSession for low level requests like this, as RESTKit adds a lot of abstractions, which is unnecessary for this task.
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:#"http://yourSite.com"]
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode >= 200 && statusCode < 300) {
// that's OK
} else {
// something wrong
}
}
}] resume];

How to read Java generated JSON data from Objective C?

I have generated JSON data from Java Restful WebServices and I need to put into the Objective C code. How can I use the JSON data and integrate into Objective C? The IDE has generated the local URL, how can I use the generated JSON data in other machine. Thank you
Have a look at NSURLConnection to retrieve the JSON from your web service. Then you can make use of NSJSONSerialization to parse it.
Use any of the many JSON parsers available. This question compares a few of them: Comparison of JSON Parser for Objective-C (JSON Framework, YAJL, TouchJSON, etc)
You can request the NSData from the URL and then use NSJSONSerialization to interpret it. For example:
NSURL *url = [NSURL URLWithString:#"http://www.put.your.url.here/test.json"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error) {
NSLog(#"%s: sendAsynchronousRequest error: %#", __FUNCTION__, error);
return;
}
NSError *jsonError = nil;
NSArray *results = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
if (jsonError) {
NSLog(#"%s: JSONObjectWithData error: %#", __FUNCTION__, jsonError);
return;
}
// now you can use the array/dictionary you got from JSONObjectWithData; I'll just log it
NSLog(#"results = %#", results);
}];
Clearly, that assumed that the JSON represented an array. If it was a dictionary, you'd replace the NSArray reference with a NSDictionary reference. But hopefully this illustrates the idea.

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);
}];

objective-c post data & image

I am new to objective-c and I have been searching for a way to send a post request to my server (based on Rest URL) but also include an image with it... I have found many methods to post data... and methods to post just an image, but nothing that combines the two...
I am searching for a wrapper, class or library because it seems to be a tedious task to write all this from scratch. I found the "ASIHTTPRequest" but this is no longer supported, although Ic an turn off ARC, I would prefer to find something still supported...
I also found AFNetworking, which seems to still be supported but I could be wrong, I just cannot find a solution to combine VERY simply data and a profile image...
Any help is appreciated?
Should I just use the ASIHTTPRequest library... ?? Or does anyone have any sample code for the AFNetworking library?
Here is the code I am using for AFnetworking library...
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
_emailAddressField.text, #"email",
_usernameField.text, #"username",
_passwordField.text, #"password",
nil];
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:%"http://url.com/api/whatever/"];
[client postPath:#"/" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSString *text = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#"Response: %#", text);
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"%#", [error localizedDescription]);
}];
If you're using AFNetworking, you can use multipartFormRequestWithMethod to upload an image:
// Create the http client
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseUrl:url];
// Set parameters
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys: #"param1", #"key1", nil];
// Create the request with the image data and file name, mime type, etc.
NSMutableURLRequest *request = [httpClient multipartFormRequestWithMethod:method path:#"url/to/" parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:data name:nameData fileName:fileName mimeType:mimeType];
}];
And then you can add the upload progress block to get feedback of the upload process:
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
//Manage upload percentage
}];
Also, you can add setCompletionBlockWithSuccess to catch success and failure in your operation. More info can be found here. At last but not least important, add the request to the operation queue:
[httpClient enqueueHTTPRequestOperation:operation];

RestKit ios - put - json instead of form encoded

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;
}