AFNetworking GET request - unable convert responseObject to NSDictionary - ios

I have tried several option setting responseSerializer to JSON but i am unable to convert responseObject to NSDictionary. The problem is response i am getting is NSData
NSString *baseURLString = #"Your URL?";
NSString *str = #"adult=false&gender=male";
NSString *url = [NSString stringWithFormat:#"%#%#",baseURLString,str];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager GET:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
// do whatever you'd like here; for example, if you want to convert
// it to a string and log it, you might do something like:
NSLog(#"responseObject %#",responseObject);
NSString *jsonString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#"%#", jsonString);
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseObject
options:NSJSONReadingMutableContainers
error:&error];
NSLog(#"json %#",json);
if (error) {
NSLog(#"%#",[error description]);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
This NSLog(#"%#",[error description]); gives error as follows:
Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed.
(Cocoa error 3840.)" (Garbage at end.)
UserInfo=0xa7c5440 {NSDebugDescription=Garbage at end.}

Your JSON is coming back with garbage at the end.
You should fix your webservice to not embed timestamp and language at the end of your JSON. If you can not change your webservice, then use following to remove trailing garbage from your json before parsing it with NSJSONSerialization again.
NSRange range = [jsonString rangeOfString:#"}" options:NSBackwardsSearch];
jsonString = [jsonString substringToIndex:range.location + 1];
Then parse this cleaned up jsonString to NSDictionary:
NSData *newJSONData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:newJSONData
options:NSJSONReadingMutableContainers
error:&error];
NSLog(#"json %#",json);
It should work just fine.

Your data is not a valid JSON you can check in this JSON Formatter provide the url or copy paste the JSON data and hit process you will see that its not a valid JSON.
The reason it not valid is it contains This page was created in 0.0048439502716064 seconds this message at the end of its response. If have control in the server side than try to stop sending this line in the end or try to remove this line and the convert it to NSDictionary. Hope this helps .. :)

Related

Error while hitting a API

I am hitting a API for push notification and it is giving me error.
I am passing parameter as a string in API like this:
NSString * jsonString= [NSString stringWithFormat:#"{\"device_id\":%#,\"device_type\":I,\"regId\":%#}",[AppDelega‌​te getMacAddress],deviceToken];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager POST:kNotificationURL
parameters:jsonString
progress:nil
success:^(NSURLSessionTask *task, id responseObject) {
NSLog(#"notification JSON: %#", responseObject);
NSDictionary *json = [Utility cleanJsonToObject:responseObject];
NSError * err;
NSData * jsonData = [NSJSONSerialization dataWithJSONObject:json
options:0
error:&err];
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData
options:kNilOptions
error:&err];
NSLog(#"the data is %#",dict);
}
failure:^(NSURLSessionTask *operation, NSError *error) {
NSLog(#" notification Error: %#", error);
}];
The error I am getting:
Response :Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}
please let me know, what have i do wrong
A quick and dirty fix would be to try this, although building your own JSON string isn't a good idea. Look at using JSONSerialization
[NSString stringWithFormat:#"{\"device_id\":\"%#\",\"device_type\":\"I\",\"regId\":\"%#\"}",[AppDelega‌​te getMacAddress],deviceToken];

jsonObjectWithData causing memory crash?

I am getting response data from a server using AFHTTPRequestOperation in AfNetworking 2.0
NSURLRequest *request = [[ServiceHelper instance] getRequestData:postDict :[ServicesConfiguration GET_DOCUMENTS_URL]];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
AFHTTPRequestOperation *requestOperation = [manager HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSDictionary *returnData = [[ServiceHelper instance] getReturnDictionary:responseObject];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
The getReturnDictionary method called on the responseObject is a simple JSON Serializer..
- (NSDictionary *) getReturnDictionary : (NSData *) data {
if ( data == nil ) {
return [NSDictionary dictionary];
}
NSError * error = nil;
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (error != nil) {
NSLog(#"Error parsing JSON: %#",error);
return [NSDictionary dictionary];
}
else
return jsonDict;
}
This works fine for smaller amounts of data. But when the response object is like 100mb, the app hangs on
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
And then about 15 seconds later, the app crashes due to a memory error.
I think its pretty self explanatory that its the massive size of the data, but shouldn't it be able to handle it?
If I get the data directly from an [ NSURLConnection sendSynchronousRequest:]; - it works without hanging or crashing. As this is what I was doing originally - but switching to AFNetworking to display a progress bar more easily.
Any thoughts or tips are appreciated.
Update:
So You have 2 options to solve this:
Blockquote
Use NSJSONReadingMutableContainers as options
Blockquote
If previous does not work you are facing a known issue like below:
iOS Download & Parsing Large JSON responses is causing CFData (store) leaks
So now you have 2 options:
Use native JSON Serialization
First downloading the JSON files to disk without using AFNetworking and than parse.

How to parse JSON using AFNetworking in iOS?

I am using AFNetworking framework in my app, I am able to make HTTP request and able to get response from server but i am not able to parse the JSON.
The following is my code:
I have created a singleton class called WebServices and have created a method, which makes HTTP request.
+(void)getCompaniesc:(NSString *)companyID onSucess:(PSACompletionBlock2)onSucess
{
NSDictionary *params = #{#"companyId": companyID};
[[[WebServices sharedInstance] operationQueue] cancelAllOperations];
[[WebServices sharedInstance] postPath:#"GetCompany?" parameters:params success:^(AFHTTPRequestOperation *operation, id response)
{
NSString *st = [NSString stringWithFormat:#"%#",response];
NSLog(#"st=%#",st);
if (onSucess)
{
onSucess(YES, response);
}
}failure:^(AFHTTPRequestOperation *operation, NSError *error){
NSLog(#"%s: AFHTTPRequestOperation error: %#", __FUNCTION__, error);
}];
}
and from my ViewController I am calling the above function using the following code:
[WebServicesClass getCompaniesc:companyID onSucess:^(BOOL success, id jsonresponse) {
}];
I'm getting the response from the server which is type id.
And the following is my response from Server:
[{"ExistsInDB":"False","CanSave":"True","EntityName":"ACCOUNT","TypeDescription":"Company","TypePluralDescription":"Companies","RequiredProperties":"Chiever.Data.SAQueryFieldSet","MetaData":"","ReadOnly":"False","ACCTNAME":"","AREA_ID": "","ACCT_TYPE_ID":"","ADDR1":"","ADDR2":"","ADDR3":"","TOWN”:””,”COUNTY":"","POSTCODE":"","COUNTRY":"","TEL":"","FAX":"","EMAILORWEB":"","BUYGRP_ID": "","STATUS":"","SIC_CODE_ID”:””,”CURRENCY_ID":"","CALL_FREQ": "0","DORMANT":"False","CREATOR_ID": "","CREATED_ON":"01/01/0001 00:00:00","LAST_EDITOR_ID":"","LAST_EDITED":"01/01/0001 00:00:00","LAST_ACTION_BY":"","LAST_ACTION":"01/01/0001 00:00:00","NEXT_ACTION_BY":"","NEXT_ACTION":"01/01/0001 00:00:00","LOCALE_ID":"","BusinessUnits": "System.Collections.Generic.List`1[chiever.Platform.LookupIdValuePair]","ACCT_ID":"0000000320"}]
Can any one help me out with this?
Just do this check:
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData: jsonresponse options:NSJSONReadingMutableContainers error:&error];
if your return is an array, use this:
NSArray *arr = [NSJSONSerialization JSONObjectWithData: jsonresponse options:NSJSONReadingMutableContainers error:&error];
A.) If you're using AFHTTPRequestOperation make sure you're setting the responseSerializer:
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest];
operation.responseSerializer = [AFJSONParserResponseSerializer serializer];
Also, make sure that you're setting request "Content-Type" to be "text/json". Now, if you're getting JSON response from the server, then you probably should get a dictionary responseObject.
B.) If you're still getting an NSData, you can convert the NSData to NSString to see/debug what you're getting from the server:
NSString *stringResponse = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; // Use appropriate encoding
NSLog(#"String Response: %#", stringResponse);
C.) If you're sure that you're getting json response from the server, you can always convert the raw NSData response to NSDictionary using json serialization:
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"JSON: %#", json);
Hope this helps.
If your server returns the "Content-Type: application/json" header, you will get a NSDictionary with the json response from AFNetworking. Or in your case, a NSArray with NSDictionaries included.
In PHP, the headers can be modified by adding
header('Content-Type: application/json');
to the top of your script.

How do I make my AFNetworking "responseObject" that I receive a NSDictionary I can parse?

I have this call with AFNetworking 1.0 that returns a responseObject with the data from the API that I want:
[[AFDiffbotClient sharedClient] postPath:#"http://diffbot.com/api/batch" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
However, I have no idea how to process responseObject.
If I check [responseObject class] I get NSData.
If I NSLog(#"%#", responseObject) I get a bunch of numbers (memory addresses I assume):
<5b0a7b22 68656164 65727322 3a5b7b22 6e616d65 223a6e75 6c6c2c22 76616c75 65223a22 48545450 2f312e31 20323030 204f4b22 7d2c7b22 6e616d65 223a2244 61746522 2c227661 ...
If I do:
NSString *responseString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#"%#", responseString);
I get the output that I want! But, it's an NSString.
If I do:
NSError *error;
NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:&error];
NSLog(#"%#", responseDictionary);
I get an NSDictionary, but it's missing the vast majority of the response (i.e.: I don't get what's included with the NSString method).
How should I be processing this object?
This is how I do it..
- (void) requestDataFinish:(NSData *)data withError:(NSError *)networkError
{
NSDictionary *responseData;
NSError *error = nil;
if (data != nil) {
responseData = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:&error];
}
...

With AFNetworking how can I read the error message contained in request.responseString

NSLog(#"%#", request.responseString);
This gives me output of {"errors":{"email":["is already taken"]}}.
I would like to save email and the message string "is already taken" into a string to display in an alert. How can I access those two items into two strings?
The response string is the raw output from the server. In this case it is JSON encoded. You can either use one of the AFNetworking JSON-specific classes (i.e. AFJSONRequestOperation) to get the response back as a JSON object, or parse it yourself using NSJSONSerialization. I would suggest using AFJSONRequestOperation.
NSData *data = [request.responseString dataUsingEncoding:NSUTF8StringEncoding];
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSString *str = [[json objectForkey:#"errors"] objectForKey:#"email"][0];
I used the following, seems a tiny bit more robust:
[requestOperation setCompletionBlockWithSuccess:success failure:^(AFHTTPRequestOperation *operation, NSError *error) {
id response = error.userInfo;
if (response && [response isKindOfClass:[NSDictionary class]]) {
NSDictionary *responseDictionary = (NSDictionary *)response;
// AFNetworking hides the actual error response under this key
if ([responseDictionary valueForKey:NSLocalizedRecoverySuggestionErrorKey]) {
id suggestedRecovery = [responseDictionary valueForKey:NSLocalizedRecoverySuggestionErrorKey];
if ([suggestedRecovery isKindOfClass:[NSString class]]) {
// Try to json decode string
id json = [NSJSONSerialization JSONObjectWithData:[suggestedRecovery dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
if (json && [json isKindOfClass:[NSDictionary class]]) {
responseDictionary = json;
}
}
}
// .. extract error message out of responseDictionary
}
}];
NSData *responseData = [[error userInfo] objectForKey:#"data"];
if ([responseData length] > 0)
{
NSString *str = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(#"%#", str);
}

Resources