AFNetworking JSON parsing - fails of unknown reason - objective-c

im trying to parse some JSON. for simplicity ill explain using the default example at github:
when running:
NSURL *url = [NSURL URLWithString:#"http://httpbin.org/ip"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation
JSONRequestOperationWithRequest:request success:^(
NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"IP Address: %#", [JSON valueForKeyPath:#"origin"]);
} failure:nil];
[operation start];
i get the correct output logged. however, when i copy the example's content (which is basically 1 element) to a txt or html file (so URLWithString gets #"http:// my server address /file.txt"), putting it on my testing server and trying to prase from there, i get no output. what is wrong with this? thanks for your time!
(note: if i go to http:// my server address /file.txt i can see the contents there clearly so that's not the problem)
edit: as suggested, the content is:
"{
"origin": "10.44.119.100"
}"

Your problem probably has something to do with the fact that you're serving content as a text file (.txt) rather than as JSON (Content-Type: application.json / .json extension). AFNetworking is strict about HTTP standards in order to guard against unexpected behavior. Either set the correct Content-Type header on your server, or (as a hack) do AFJSONRequestOperation +addAcceptableContentTypes: adding text/plain.
As a meta note: when asking a question on Stack Overflow, specifics matter. If you had posted the error you were seeing in the console, it would be much easier to determine what the problem was. Likewise, approximate code is not actual code; if you have a problem, be specific about exactly what's going on. Details matter.

You should encode the json data first and then write it into the text file and when you are reading the data from file... decode the data first...
EDIT:
replace JSON operation with simple http and check if you are able to get data from there...
and if you are then JSONOperation basically is seeking for json response which is not in text file... i guess
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]initWithRequest:request];
[operation setUploadProgressBlock:^(NSInteger bytesWritten,long long totalBytesWritten,long long totalBytesExpectedToWrite)
{
NSLog(#"Sent %lld of %lld bytes", totalBytesWritten, totalBytesExpectedToWrite);
}];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSString *str = [[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#" Success %#",str);
// id response = AFJSONDecode(responseObject, nil);
[self requestSucceed:response];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"error: %#", operation.responseString);
}];

Related

During start up, how can load remote image?

I success to load the image and JSON with NSURLSession.
But I want to load these files during Start up.(When launchImage is viewed, file is loaded and launchImage is disappeared then remote-image is shown up) How can I do this?
Although yes, a library is not always necessary, I do find AFNetworking to be very handy in this case. Import AFNetworking.h and UIImageView+AFNetworking.h and you can make requests like this for images from a remote server. After creating an imageView you can load the image into a UIImageView using a request like this.
[imageView setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://image.url"]]
placeholderImage:[UIImage imageNamed:#"loading"]
success:^(NSURLRequest *request , NSHTTPURLResponse *response , UIImage *image ){
NSLog(#"Loaded successfully: %d", [response statusCode]);
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){
NSLog(#"failed loading: %#", error);
}
];
Using the NSHTTPURLResponse you can also get the image and do whatever caching you would like or save it to a directory.
Edit: This question was phrased in a way difficult to understand. Although this may not directly answer the askers question, it is relevant to the question's title and could help someone who searches for it in the future.

URL reachablility - false positive

There are a ton of "false positive" posts, though none appear to match my situation. (Most are talking about internet connection errors..)
As with everyone almost else, I just want to determine whether a specific URL is valid.
Now, I'm dealing with subdomains, and it works great as long as I choose a valid domain. If I choose an invalid subdomain, I get a false positive. Using a browser to test reveals that my ISP gives me a really annoying suggestions page if I type in a URL that doesn't exist.
Any way to validate that the responding server IS actually at the URL I asked for?
My Code:
-(BOOL) canConnectToURL:(NSString * )sURL {
BOOL bSuccess = NO;
if (![txtSubDomain.text.lowercaseString isEqual: #"www"]){
NSLog(#"canConnectToURL(%#)",sURL);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: sURL]];
[request setHTTPMethod: #"HEAD"];
NSURLResponse *response;
NSError *error;
[NSURLConnection sendSynchronousRequest: request returningResponse: &response error: &error];
NSLog(#"URL: %#",sURL);
NSLog(#"data: %#",response);
bSuccess = ([(NSHTTPURLResponse *)response statusCode] == 200);
}
return bSuccess;
}
I believe you can attach a delegate to your NSURLRequest that handles the method connection:willSendRequest:redirectResponse: in the NSURLConnectionDataDelegate protocol, and catch it there.
https://developer.apple.com/library/mac/documentation/Foundation/Reference/NSURLConnectionDataDelegate_protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40011348-CH1-SW9

Objective-C staying in a JSONRequest loop until JSON returns a specific value

I am implementing an HTTP request using Objective-C using AFJSONRequestOperation for a mobile app and I do not know how to implement a loop until a condition is satisfied (i.e. the profiling_status key in JSON has the value 1). The request is run when a button is pressed. In the background the server does some calculations that take a while. Until the server finishes, the profiling_status value is 2. When it finishes the value is 1. So, I would like to stay in a loop until the value changes to 1 and then display the JSON.
Returning the JSON in the success block gives a pointer error.. and returning the JSON at the end of the method will return nil.
I have this code:
- (IBAction)getProfileInfo:(id)sender
{
profiling_status = 2;
NSDictionary *JSON;
while (profiling_status == 2){
JSON = [self getJSON];
profiling_status = [JSON objectForKey:#"profiling_status"];
}
NSLog(#"JSON: %#", JSON);
}
- (NSDictionary*)getJSON
{
__block NSDictionary* JSONResult = nil;
MyAPIClient *client = [MyAPIClient sharedClient];
NSString *path = [NSString stringWithFormat:#"/profile?json"];
NSURLRequest *request = [client requestWithMethod:#"GET" path:path parameters:nil];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
JSONResult = JSON;
//can’t do this ---- return JSONResult;
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"%#", [error userInfo]);
}];
[operation start];
return JSONResult; //will return nil
}
Any help?
Thank you.
You can't use a while loop for this (and shouldn't anyway as it will just kill the app by spawning connections). Instead you need to structure your methods with blocks so that the block running the request checks the result and either recursively calls the check method (preferably after a short delay) or calls a completion block.
Also think about keeping a count of the number of iterations or the time taken so you can abort the processing.

JSON Payload doesnt seem to be sending

My problem I'm pretty positive is simple, I must just be missing something.. just not sure what.
I can send GET and POST for granular elements (this=that kind of stuff), but a web service call I need to send data too, takes a raw JSON block, with no "key"
Heres the method I wrote:
-(NSData *)execute {
// Smart Chooser ?
if(PostData.count >0 || Payload != nil)
[self setMethod:UPLINK_METHOD_POST];
else
[self setMethod:UPLINK_METHOD_GET];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.connectionUrl
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:10];
if([UPLINK_METHOD_GET isEqualToString:self.connectionMethod])
[request setHTTPMethod:#"GET"];
else
[request setHTTPMethod:#"POST"];
NSString *gData = [self compileGetData];
NSString *pData = [self compilePostData];
// if we have get data, set it into the URL string
if(GetData.count > 0) {
[self setURLWithString:[[self.connectionUrl absoluteString] stringByAppendingString:[#"?" stringByAppendingString:gData]]];
[request setURL:self.connectionUrl];
}
// if we have post data, set it in the body
if(PostData.count > 0) {
const char *bytes = [[NSString stringWithString:pData] UTF8String];
[request setHTTPBody:[NSData dataWithBytes:bytes length:strlen(bytes)]];
}
// Override any post data if a payload is already defined.
if(Payload != nil) {
[request setHTTPBody:[Payload dataUsingEncoding:NSUTF8StringEncoding]];
}
NSLog(#"URL : %#", request.URL);
NSURLResponse *response;
NSError *err;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
if(err != nil)
NSLog(#"here was an error: %#", err);
return responseData;
}
-(NSDictionary *)executeAsJSON
{
NSData *responseData = [self execute];
NSError *e;
return [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&e];
}
Ok SO, the way this thing works, is that it automatically sets whether the request is POST or GET depending on the data provided in the GetData, PostData, and Payload vars.
The request is GET by default, but turns into POST if PostData or Payload have anything in them.
The compileGetData and compilePostData mostly just bring back formatted strings with arrays of information combined, nothing special there.
But thats not where the problem is.
See, "Payload" overrides anything "PostData" had in it. If you had provided PostData elements into the class, it would just be overridden by a provided Payload if that does exist.
I needed to provide this to demonstrate the "workarea" as it exists right now, its not linearly provided information.
This is the area of interest:
// Override any post data if a payload is already defined.
if(Payload != nil) {
//const char *plbytes = [[NSString stringWithString:Payload] UTF8String]; // this didn't work
[request setHTTPBody:[Payload dataUsingEncoding:NSUTF8StringEncoding]]; // inline, doesn't work either
}
When I say "doesnt work", what I mean is, im getting back an error JSON array from the webservice that basically means "hey, wheres the payload?". If the request is not POST it comes back as a general error, so thats all working, the URL is then obviously correct.
I've used RESTConsole for Chrome to test the webservice to make sure its working properly, and it does.
I've also checked through the debugger the exact payload im sending, i copy+pasted that into RESTConsole, and it works there.
I'm.. honestly at a loss here...
Try using a web proxy like Charles or Wireshark (I personally preferr Charles due to it's ease of use, it's a 30-day trial though) and monitor the request you make from RESTConsole and the one you make from your app and see if they look the same.
Check any headers, line returns and anything else that looks different.
That's the best I can think of to start with

AFNetworking post request not getting through to Sinatra app

I'm trying to post a request from my iPhone with a Sinatra API I made. Currently all my Sinatra app is doing is printing out the request that has been sent to it. This is the code for that:
post '/profile' do
puts "#{params}"
end
My objective-c is pretty simple as well. All it does is send a post request to my API:
NSURL *url = [NSURL URLWithString:kBaseURLString];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:JSON, #"json", nil];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST" path:#"/profile" parameters:dictionary];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"SUCCESS");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#", error);
}];
[operation start];
When JSON (in line 3 of the obj-c) is a very short string, such as #"test", Sinatra prints it out correctly like this:
{"json"=>"test"}
When I use the actual JSON profile data, which is a very long JSON blob, Sinatra prints it out as this:
{"json"=>"(null)"}
I can't figure out why the long blob is getting through. I am 100% sure that i'm passing the correct string, but Sinatra is not receiving it. My current theory is that Sinatra has a max character limit on requests, but I am new to Sinatra and Ruby, and I have no idea how I'd test that. What is going wrong?
UPDATE:
First off, thanks Kjuly for your suggestions. I figured out that I was wrong on the character limit on Sinatra thing. In obj-c I'm doing a log of the dictionary that has the JSON blob on line 3, and it has the json blob. However, when I log the body of the NSMutableURLRequest on line 4, the body is empty. When I use my tiny JSON blob, the body is filled.
Does NSMutableURLRequest have a character limit? Can anyone think of a reason why it would not accept my very large dictionary with the large JSON blob, but not with the small one.
Thanks!
UPDATE AGAIN:
The request body now fills correctly. I had to add this line into line 3:
[httpClient setParameterEncoding:AFJSONParameterEncoding];
Now I am getting this response back in the HTTPResponse from Sinatra:
Error Domain=com.alamofire.networking.error Code=-1016 "Expected content type {(
"text/json",
"application/json",
"text/javascript"
)}, got text/html"
Sinatra is now just printing
{}
Instead of {"json"=>"(null)"}
Still not sure what's going on.
Update 3
Okay, what I thought was the HTTPResponse from Sinatra - the text/json stuff - was because I was returning a text/html from Sinatra back in AFNetworking. I have now checked the body that Sinatra is receiving, and my giant JSON blob is in there. However, "params" is still empty.
Anyone have any idea why?
FIXED IT
Looks like when you post JSON to sinatra you have to read the body of the request directly. In Sinatra, you do this like so:
profile = JSON.parse(request.body.read.to_s)
Then profile is your parsed object.
I think you need to use AFJSONRequestOperation instead, here's a sample code:
// Fetch Data from server
NSURL *url = [NSURL URLWithString:#"https://gowalla.com/users/mattt.json"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation * operation =
[AFJSONRequestOperation JSONRequestOperationWithRequest:request
success:^(NSURLRequest * request, NSHTTPURLResponse * response, id JSON) {
NSLog(#"Name: %# %#", [JSON valueForKeyPath:#"first_name"], [JSON valueForKeyPath:#"last_name"]);
}
failure:nil];
[operation start];
Or you can visit the WIKI PAGE, see Step 4: Download and Parse JSON.