AFJSONRequestOperation crashing with "data parameter is nil" NSJSONSerialization error - iphone

I previously asked a similar question on this but didn't get much help and have now looked into it further and still can't see why I have a problem.
NSURL *url = [NSURL URLWithString:#"http://datapoint.metoffice.gov.uk/public/data/val/wxfcs/all/json/sitelist?res=daily&key=<MY API KEY>"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"Success");
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Fail");
}];
[operation start];
This fails with the following
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'data parameter is nil'
I believe the problem is due to the JSON type returned as ISO-8859-1, I managed to get this working with NSJSONSerialization by encoding the retuned string to NSUTF8StringEncoding
Example...
NSString *string = [NSString stringWithContentsOfURL:kMetOfficeAllSites encoding:NSISOLatin1StringEncoding error:&error];
NSData *metOfficeData = [string dataUsingEncoding:NSUTF8StringEncoding];
id jsonObject = [NSJSONSerialization JSONObjectWithData:metOfficeData options:kNilOptions error:&error];
if (error) {
//Error handling
} else {
//use JSON
So I looked at responseJSON in AFJSONRequestOperation.m
- (id)responseJSON {
[self.lock lock];
if (!_responseJSON && [self.responseData length] > 0 && [self isFinished] && !self.JSONError) {
NSError *error = nil;
// Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
// See https://github.com/rails/rails/issues/1742
if ([self.responseData length] == 0 || [self.responseString isEqualToString:#" "]) {
self.responseJSON = nil;
} else {
// Workaround for a bug in NSJSONSerialization when Unicode character escape codes are used instead of the actual character
// See http://stackoverflow.com/a/12843465/157142
NSData *JSONData = [self.responseString dataUsingEncoding:self.responseStringEncoding];
self.responseJSON = [NSJSONSerialization JSONObjectWithData:JSONData options:self.JSONReadingOptions error:&error];
}
self.JSONError = error;
}
[self.lock unlock];
return _responseJSON;
}
The code is crashing in the else statement, however this seems to be doing what I was doing previously when using NSJSONSerialization directly and re-encoding the responseString.
I even hardcoded dataUsingEncoding to NSUTF8StringEncoding but it still crashes and I don't understand why?
Note: The above works fine with other JSON feeds, also from other feeds on
http://datapoint.metoffice.gov.uk/ but
http://datapoint.metoffice.gov.uk/public/data/val/wxfcs/all/json/sitelist?res=daily&key=
includes the place name Sóil Chaorainn which causes the problem

The problem seems to be that the text encoding of the response is wrong. There are characters that can't be encoded using ISO-8859-1. This is why the responseString method (see AFURLConnectionOperation.m) returns nil and the JSON serialization fails.
To solve the issue you could subclass AFJSONRequestOperation and override responseStringEncoding in this way to enforce UTF-8 encoding:
- (NSStringEncoding)responseStringEncoding {
[self.lock lock];
if (!_responseStringEncoding && self.response) {
self.responseStringEncoding = NSUTF8StringEncoding;
}
[self.lock unlock];
return _responseStringEncoding;
}

Related

Objective C retrieve twitter email from JSON

TwitterKit changed the way its email gets retrieved again and now I can't figure out how to retrieve the email from JSON using the new format.
Previously I would just do this:
TWTRShareEmailViewController* shareEmailViewController =
[[TWTRShareEmailViewController alloc]
initWithCompletion:^(NSString* email2, NSError* error) {
NSLog(#"Email %#, Error: %#", email2, error);
But now they've gotten rid of the TWTRShareEmailViewController (as of version 2.0) and I have to do this:
TWTRAPIClient *client = [TWTRAPIClient clientWithCurrentUser];
NSURLRequest *request = [client URLRequestWithMethod:#"GET"
URL:#"https://api.twitter.com/1.1/account/verify_credentials.json"
parameters:#{#"include_email": #"true", #"skip_status": #"true"}
error:nil];
[client sendTwitterRequest:request completion:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
**something should go here**
}];
...but I'm not quite sure how to get the email from the json now.
Any help would be appreciated.
Ok. In the end it was pretty easy. All I had to do was convert the JSON to a string. And from there I could do whatever I wanted to it. So I did it this way:
TWTRAPIClient *client = [TWTRAPIClient clientWithCurrentUser];
NSURLRequest *request = [client URLRequestWithMethod:#"GET"
URL:#"https://api.twitter.com/1.1/account/verify_credentials.json"
parameters:#{#"include_email": #"true", #"skip_status": #"true"}
error:nil];
[client sendTwitterRequest:request completion:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSString *json = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}];
From there you could turn it to a dictionary or array or whatever you want to do with it. Phew! I really need more sleep!

Ok in simulator but throwing exception in ios device

when i am running my app in the simulator everything is working perfectly .But when i running the same app in the Ipad exception is being thrown.
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'data parameter is nil.
In my app at one step i have to request a web-URl and need to parsed the returned JSON response. But I have checked the web-url and have been able to parse perfectly in simulator. But all the problem has been arisen in real ios device.But I think i have identified the code where it is getting wrong.
+ (NSDictionary*) getParsedJSON:(NSString*) urlString {
NSLog(#"################################################################################");
NSLog(#"getParsedJSON => urlString:");
NSLog(#"%#", urlString);
NSURL* url = [NSURL URLWithString:urlString];
NSURLRequest* request = [NSURLRequest requestWithURL:url];
NSURLResponse *response1 = nil;
NSError *error = nil;
NSData* response = [NSURLConnection sendSynchronousRequest:request returningResponse:&response1 error:&error];
//NSData* response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSLog(#"--------------------------------------------------------------------------------");
NSString* responseString = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSLog(#"getParsedJSON => responseString:\n%#", responseString);
NSLog(#"--------------------------------------------------------------------------------");
NSError* jsonParsingError = nil;
NSDictionary* parsedJSON = [NSJSONSerialization JSONObjectWithData:response options:0 error:&jsonParsingError]; // here is place where exception seems to be thrown.
if (jsonParsingError) {
NSLog(#"ERROR in parsing JSON: %#", jsonParsingError);
} else {
NSLog(#"getParsedJSON => parsedJSON: \n%#", [parsedJSON description]);
}
NSLog(#"################################################################################");
return parsedJSON;
}
I have identified the line where it seems to be wrong .I have also attached screen shot of the exception report..Hoping for your experienced reply.
AS we can see from the logs your response string is null while you are using it on your Device. This may be due to some internet access problem. Try to Use:
if([response isequaltostring:#"(null)"]||response == nil || response.length == 0)
{
NSError* jsonParsingError = nil;
NSDictionary* parsedJSON = [NSJSONSerialization JSONObjectWithData:response options:0 error:&jsonParsingError]; // here is place where exception seems to be thrown.
if (jsonParsingError) {
NSLog(#"ERROR in parsing JSON: %#", jsonParsingError);
}
else {
NSLog(#"getParsedJSON => parsedJSON: \n%#", [parsedJSON description]);
}
}
Also try to add the exceptional breakpoint and post where exactly the app crashed.
Let me know the result.
First, you need to set an exception breakpoint in Xcode - there are many posts here on how to do that. Second, after each of you statements where an object is created or returned, add an assert:
NSURL *foo = ...
assert(foo);
Doing this will help you find the first issue not the last one.
As per your logs, your response string is empty!
Do the below two things!
Add NSLog(#"Response Data: %#",response); and check if the response has value?
If 'response' has value, convert it to a string - Log the string value - And check if the any of the key has nil value?
'NSJSONSerialization JSONObjectWithData' method would crash if it finds any key with nil value.

Possible to return an object into a completion block?

I work with Core data and I want to call a function for returning an object. This object is completed in this function with AFNetworking into a completion block. I want to know if it's possible to return this object into this completion block... for use it later. Failed code here, because newMember in completion block is not allowed and not working.
This error appears :
Incompatible block pointer types sending 'Member *(^)(NSURLRequest...'
+(Member *)returnMemberModelWithId:(NSString *)myid withContext:(NSManagedObjectContext *)managedObjectContext{
__block NSMutableArray *dicoNe =[[NSMutableArray alloc] init];
__block Member *newMember = [NSEntityDescription
insertNewObjectForEntityForName:#"Member"
inManagedObjectContext:managedObjectContext];
NSString *s= #".json";
NSString *str = [NSString stringWithFormat: #"%#%#", myid, s];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://epnet.fr/"]];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
[httpClient setParameterEncoding:AFFormURLParameterEncoding];
NSString *path = [NSString stringWithFormat:#"members/%#", str];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET" path:path parameters:nil];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *innerRequest, NSHTTPURLResponse *response, id reponseJSON) {
dicoNe = reponseJSON;
NSNumber *v = [dicoNe valueForKey:#"id"];
newMember.idMember = v; //And others assignments ..
return newMember; // It's not working
} failure:^(NSURLRequest *innerRequest, NSHTTPURLResponse *response, NSError *error, id reponseJSON) {
NSLog(#"Fail %#", reponseJSON);
}];
[operation start];
return newMember;
}
So , my object newMember which I try to return is null at the end. So my question is, how can I return my object completed by AFN with this function? Many thanks
What you are effectively asking is "How do I make this asynchronous function synchronous?"
The answer is that you don't. Or, you shouldn't.
What you should do is make that method return (void) (or BOOL + an NSError**) and then make whatever call is necessary to process the result of the query from your completion block.
There are a number of issues with that code. The initial [[NSMutableArray alloc] init]; is pointless given that the first thing you do in the completion block is dicoNe = responseJSON;. There is no need to use stringWithFormat: with a static string in this expression: [NSString stringWithFormat:#"http://epnet.fr/"].
Calling valueForKey: on an array will return an array. Something is afoul with the way you are handling that information (is responseJSON really an array?!).

no returns in NSJSONSerialization

Could you please forgive me for eventual mistakes i can make asking this question me it's my first one here.
After reading several topics on this website, like this one first i'll try to use the describe methods but it still doesn't work # all :-(
My .json file looks like this
{ "speakers" :
[
{
"name":"Value",
"picture": "URL VALUE",
"business":"VALUE",
"desc":"VALUE",
"twitter": "URL VALUE"
}
{
...
}
]
}
So this is my reasoning :
I firstly have a dictionary which contains speaker attribute
This one contains an array, field by some dictionnaries within "name", "business",... attr.
So, this is my obj-C code :
NSString *URLStr = #"URLofMyJsonFile";
NSURLRequest *JSONRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithString:URLStr ]]];
NSData *JSONData = [NSURLConnection sendSynchronousRequest:JSONRequest returningResponse:nil error:nil];
NSError *parsingError = nil;
NSDictionary *speakerDictionnary = [NSJSONSerialization JSONObjectWithData:JSONData options:0 error:&parsingError];
NSArray *speakersArray = [speakerDictionnary objectForKey:#"news"];
for (NSDictionary *oneSpeaker in speakersArray) {
NSLog(#"The speakers's name is %#", [oneSpeaker objectForKey:#"name"]);
NSLog(#"The speakers's business is %#", [oneSpeaker objectForKey:#"business"]);
NSLog(#"The speakers's desc is %#", [oneSpeaker objectForKey:#"desc"]);
}
EDIT : I remplace the right URL of my Script with Dummy
Your JSON isn't valid, there needs to be a comma between the individual speaker dictionaries.
{ "speakers" :
[
{
"name":"Value",
"picture": "URL VALUE",
"business":"VALUE",
"desc":"VALUE",
"twitter": "URL VALUE"
} <=== MISSING COMMA HERE
{
...
}
]
}
As omz mentioned the json is wrong. you can try below code :
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.appios.fr/client/takeoff/app/script/jsonSpeaker.json"]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *reponse,NSData *data,NSError *error){
if (!error) {
NSError *jsonError;
id json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError];
NSArray *speakersList = [json objectForKey:#"speakers"];
[speakersList enumerateObjectsUsingBlock:^(NSDictionary *dict,NSUInteger idx,BOOL *Stop){
NSLog(#"Name : %#",[dict objectForKey:#"name"]);
}];
}
} ];

AFNetworking JSON request with a boolean

I'm trying to send a JSON request using AFNetworking and have a problem with making values be translated to the json form of {"value": true}. Instead, I'm getting: {"value": 1}
Here's basically how I'm creating the request:
NSMutableURLRequest *request =
[self.httpClient requestWithMethod:#"POST"
path:url
parameters:#{#"value": #YES}];
AFJSONRequestOperation *operation =
[AFJSONRequestOperation JSONRequestOperationWithRequest:request ...];
[operation start];
Am I missing something trivial here? :)
Short answer:
Make sure you are running a recent version of AFNetworking. That's all I can see as the problem based on the code you've provided.
Long answer:
I've tried reproducing the issue you're describing with the most recent versions of AFNetworking and I could not. I dug into AFNetworking to see how the encoding of JSON is done. AFHTTPClient.m:442 uses NSJSONSerialization to encode JSON requests. I came up with the following code to test the issue:
NSError* error = nil;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:#{#"value" : #YES} options:0 error:&error];
NSLog(#"Resulting JSON:\n\n%#\n", [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]);
outputs:
{"value":true}
So #YES should do it. As a note, be sure not to use #(YES) in your code as it will output as a 1 instead of true.
NSError* error = nil;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:#{#"value" : #(YES)} options:0 error:&error];
NSLog(#"JSON:%#", [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]);
outputs:
{"value":1}
With that I went through and tried to figure out how AFHTTPClient need to be configured to send out a bool as 1/0 instead of true/false and could not find any. Here's my networking code.
AFHTTPClient* httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://<SERVER HERE>"]];
[httpClient setParameterEncoding:AFJSONParameterEncoding];
NSMutableURLRequest *jsonRequest = [httpClient requestWithMethod:#"POST" path:#"/" parameters:#{#"value": #YES}];
AFHTTPRequestOperation *jsonOperation = [AFJSONRequestOperation JSONRequestOperationWithRequest:jsonRequest success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"Success");
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Failure");
}];
[jsonOperation start];
Since #YES is an NSNumber, NSJSONSerialization turns this to 0/1.
I don't think there's a way other than #{#"value": (yesOrNo ? #"true" : #"false")} or using a different serialization class.
For people who might be running into this issue, there's another reason why it might be happening.
Make sure you set the parameterEncoding property of your AFHTTPClient subclass to AFJSONParameterEncoding, otherwise you'll run into the issue of NSNumber's initialization value not being correctly detected, and will see 0s and 1s being output instead by the encoder.
See this for reference as well.
Hope this helps.
In the subclass of HTTPClient. Instead of:
self.responseSerializer = [AFJSONResponseSerializer serializer];
try with:
$self.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
I have the same error, I am sending the #YES but the services give me fail, so I create and string of a json and create an jsonObject like this:
NSString* paramsString = #"{";
NSString* appending = [NSString stringWithFormat:#"\"%#\":%#,", KEY_CHECKED, (checked ? #"true" : #"false")];
paramsString = [paramsString stringByAppendingString: appending];
paramsString = [paramsString stringByAppendingString:#"}"];
id object = [NSJSONSerialization JSONObjectWithData:[paramsString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
Use this object for send the post with AFNetworking
[self postParameters:object];
for me works!