Code execution with sendAsynchronousRequest - objective-c

A variable is being set to (null) due to the sendAsynchronousRequest not completing before the request is complete. See code:
main.m:
GlobalSettings *globalsettings = [[GlobalSettings alloc] init];
NSString *url = [globalsettings facebookLink];
NSLog(#"URL: %#", url);
So, inside GlobalSettings:
-(NSString *)facebookLink
{
__block NSString *strReturn;
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://urlEditedOut/"]];
__block NSDictionary *json;
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
json = [NSJSONSerialization JSONObjectWithData:data
options:0
error:nil];
strReturn = json[#"FB"];
}];
return strReturn;
}
So this works fine, has been tested inside the completion block. However back in main.m the variable url is being set to (null) due to (i assume) the async request still connecting / processing request.
How do you combat this so that the variable is saved as the correct value?

The url is set to null because the method returns immediately due to the asynchronous request. The way to avoid this is a delegate. make main the delegate of the GobalSettings and call the delegate method from the completion block. This SO-Post isnt an exact duplicate, but its close enough to get you started.
How to return the ouput if method is using NSURLConnection Asynchronous calls with blocks
Avt's answer is what i would suggest, but returning a block works, too.

Related

App crashes on slow InternetConnection

I am getting data from server in applicationDidBecomeActive method.When net connection is too slow app keep crashing.I do not know how to handle this problem.any help will be appreciated.thanks in advance.
NSString *post =[[NSString alloc] initWithFormat:#"=%##=%#",myString,acMobileno];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http:///?data=%#&no=%#",myString,acMobileno]];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:postData];
NSError *error1 = [[NSError alloc] init];
NSHTTPURLResponse *response = nil;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error1];
NSString *string;
if ([response statusCode] >=200 && [response statusCode] <300)
{
string = [[NSString alloc] initWithData:urlData encoding:NSMacOSRomanStringEncoding];
}
It's probably crashing because the connection has started downloading, but it hasn't finished therefore allowing the complier to pass your if statement, which would inevitably give a nil urlData parameter.
To fix this, you should be checking to see if there is an error, and then the response headers for the download. Also, I recommend running this operation on a background thread so that it doesn't block the user experience - at the moment, the app will have a delayed launch depending on the size of your file, and the user's download speed.
NSError *error1 = nil;
NSHTTPURLResponse *response = nil;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error1];
NSString *string = nil;
if (error != nil && ([response statusCode] >=200 && [response statusCode] <300)){
string = [[NSString alloc] initWithData:urlData encoding:NSMacOSRomanStringEncoding];
}
else {
NSLog(#"received error: %#", error.localizedDescription);
}
For a background thread, run the above code in a dispatch_async statement, or use -sendAsynchronousRequest: instead of -sendSynchronousRequest.
Alternatively, as #Viral said, it is possible that the request is taking too long, and the app is hanging as a result of the synchronous request not finishing before the UI should have been loaded.
Most probably, it's due to synchronous call in Application's delegate method. It is taking too much time to load the UI (As internet connection is slow and you are calling the web service on main thread); and therefore OS thinks your App has hanged due to unresponsive UI and crash the App itself.
Just for debugging purpose, try the same code in your FirstViewController's viewDidAppear method. It should work fine there. And if it is so, you need to change your call to somewhere else (also, preferably in some background thread, OR Async).
EDIT: Though, If it works elsewhere, you need to change the call as Async OR on background thread for smoother UX.

iOS App Wait for HTTP Response when calling a method?

I am trying to call a class method that takes a string and posts it to a site to receive a JSON response(among some other variables I have stored in the DataClass). I am stuck trying to return the data in the form of a response and can not at this point even NSLog the returned data. The question is, now that I have called my class method, how can the class method wait to return a response from an HTTP POST to return data? Once I return my JSON, I can expand it to a dictionary and process from there. Help is appreciated :)
Class Method:
//
// APISample.m
//
// Created by Sam on 1/6/13.
// Copyright (c) 2013 Sam. All rights reserved.
//
#import "APISample.h"
#import "DataClass.h"
#implementation APISample
#synthesize first_name = _first_name;
#synthesize last_name = _last_name;
#synthesize profile_pic_url = _profile_pic_url;
#synthesize responseData;
-(id)init
{
self = [super init];
return self;
NSLog(#"Loaded APISample and fetching");
}
+(id)getDataAboutUser:(NSString *)user_request_id;
{
DataClass *userdata=[DataClass getInstance];
NSLog(#"Loaded APISample and fetching %#", user_request_id);
NSMutableURLRequest *user_fetch_details = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://10.0.23.161/users/user_fetch_details.php"]];
[user_fetch_details setHTTPMethod:#"POST"];
NSMutableString *postString = [NSMutableString stringWithString:#"id=123"];
[postString appendString:#"&userrequest_id="];
[postString appendString:[userdata.str_userid copy]];
[postString appendString:#"&user_id="];
[postString appendString:[userdata.str_userid copy]];
[postString appendString:#"&identifier="];
[postString appendString:[userdata.str_identifier copy]];
[user_fetch_details setValue:[NSString stringWithFormat:#"%d", [postString length]] forHTTPHeaderField:#"Content-length"];
[user_fetch_details setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
NSURLConnection *connection=[[NSURLConnection alloc] initWithRequest:user_fetch_details delegate:self];
NSMutableData *responseData=[NSMutableData data];
[responseData appendData:[NSURLConnection connection:didReceiveData];
if (connection) {
// Create the NSMutableData that will hold
// the received data
// receivedData is declared as a method instance elsewhere
NSMutableData *responseData=[NSMutableData data];
} else {
// inform the user that the download could not be made
}
NSLog(#"Received Data %#", [[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding]);
return [[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
NSString *receivedDataString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
if ([receivedDataString isEqualToString: #"error"]) {
UIAlertView *errorAlert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"An error has occured. The application will now exit. Unexpected Response!"
delegate:nil
cancelButtonTitle:#"Close"
otherButtonTitles:nil];
[errorAlert show];
exit(0);
}else{
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
NSString *firstnameResponse = [json objectForKey:#"first_name"];
NSString *lastnameResponse = [json objectForKey:#"last_name"];
NSString *profile_pic_urlResponse = [json objectForKey:#"profile_pic_url"];
NSLog(#"didReceiveData %# analysed " , firstnameResponse);
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"connectionDidFinishLoading");
NSLog(#"Succeeded! Received %d bytes of data",[self.responseData length]);
}
#end
I receive no data in the log after "Received Data" and do not see my error messages. Thanks you
The design pattern you are describing is called a CallBack. You need to be notified of an event occurring at some point in the future. In objective-c there are 4 main forms of callbacks.
Target Action Pairing (this is what is used with buttons, and things of the like. "When this button is pressed notify my target, and tell them to execute this action")
Delegation (you are using a form of delegation in the code above with NSURLConnection. When you see the word 'delegate' i want you to think 'helper object'. You are saying, "hey NSURLConnection, when important events happen, i would like you to tell this delegate (helper object) about these events)
Notifications (these are used a lot when dealing with model objects changing)
and finally... the one i would recommend for your purposes...
Blocks.
A block is a very cool variable. Most variables hold data. A block is a variable which holds code to be executed at some point in the future. So in your situation you could pass a completion block along with your method getDataAboutUser:(NSString *)user_request_id. So it would look like this.
getDataAboutUser:(NSString*)string withCompletion:(void(^)(NSData *finishedData))cBlock
Store that cBlock as an instanceVar. Then when your NSURLConnection finishes downloading all its data, you will execute the cBlock, passing in the finished data as an argument.
Blocks are a fairly complicated things if you have not used them before, so i would reccomend taking 20 minutes and reading this.
Since you need your method to wait for a response before returning, you can use NSURLConnection's convenience class method sendSynchronousRequest to carry out a synchronous request instead of creating and managing an NSURLConnection instance asynchronously.
So instead of your [[NSURLConnection alloc] init...] line you can do this:
NSURLResponse *response = nil;
NSError *error = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:user_fetch_details returningResponse:&response error:&error];
Following which you can immediately parse the JSON from responseData instead of doing that in the connection:didReceiveData delegate.
Edit: Just saw user698846's suggestion to modify your method signature to take a completion block. That's also a good and possibly cleaner way to approach your problem if you are at liberty to change your method signature (i.e. nobody is requiring your function to return synchronously). Either way, sendSynchronousRequest is possibly the easiest way out and there's no shame in it especially if there's nothing your app nor your user can do while waiting for the request to complete.
This is some code:
NSURLResponse *response = nil;
NSError *error = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:user_fetch_details returningResponse:&response error:&error];

Getting data out of the NSURLResponse completion block

It looks like I didn't get the concept of blocks completely yet...
In my code I have to get out the JSON data from the asychronous block to be returned to from the 'outer' method. I googled and found that if defining a variable with __block, the v̶i̶s̶i̶b̶i̶l̶i̶t̶y̶ _mutability_ of that variable is extended to the block.
But for some reason returned json object is nil.I wonder why?
- (NSMutableDictionary *)executeRequestUrlString:(NSString *)urlString
{
__block NSMutableDictionary *json = nil;
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPShouldHandleCookies:YES];
[request setHTTPMethod:#"GET"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-type"];
NSString *cookieString = [self.userDefaults objectForKey:SAVED_COOKIE];
[request addValue:cookieString forHTTPHeaderField:#"Cookie"];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue currentQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(#"dataAsString %#", [NSString stringWithUTF8String:[data bytes]]);
NSError *error1;
NSMutableDictionary * innerJson = [NSJSONSerialization
JSONObjectWithData:data
options:kNilOptions
error:&error1];
json = innerJson;
}];
return json;
}
First, to answer your question:
But for some reason returned json object is nil. I wonder why?
The variable that you are returning has not been set at the time when you return it. You cannot harvest the results immediately after the sendAsynchronousRequest:queue:completionHandler: method has returned: the call has to finish the roundtrip before calling back your block and setting json variable.
Now a quick note on what to do about it: your method is attempting to convert an asynchronous call into a synchronous one. Try to keep it asynchronous if you can. Rather than expecting a method that returns a NSMutableDictionary*, make a method that takes a block of its own, and pass the dictionary to that block when the sendAsynchronousRequest: method completes:
- (void)executeRequestUrlString:(NSString *)urlString withBlock:(void (^)(NSDictionary *jsonData))block {
// Prepare for the call
...
// Make the call
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue currentQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSLog(#"dataAsString %#", [NSString stringWithUTF8String:[data bytes]]);
NSError *error1;
NSMutableDictionary * innerJson = [NSJSONSerialization
JSONObjectWithData:data options:kNilOptions error:&error1
];
block(innerJson); // Call back the block passed into your method
}];
}
When you call sendAsynchronousRequest:queue:completionHandler:, you've requested an asynchronous request. So it queues the request and the block and returns immediately. At some point in the future the request is made, and some point after that the completion block is run. But by that time, return json has long since run.
If you want to be able to return the data synchronously, then you must make a synchronous request. That will hang this thread until it completes, so it must not be the main thread.
Check the string when converting data coming from server using below code:
NSLog(#"dataAsString %#", [NSString stringWithUTF8String:[data bytes]]);
if the string is in a proper JSON format, ONLY then your JSON object will be correct.
Hope this hepls!!

Getting data from the object, that uses NSURLConnection

I have a class, that gets some data using NSURLConnection. It's method getData creates a request to a server and when some data recieved, method connection:didRecieveData: updates some properties.
- (void)getData
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:dataURL];
NSURLConnection *connectionWithRequest = [NSURLConnection connectionWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Processing data
dataProperty = processedData;
}
The problem is, when I create an instance of this class and call method getData, I can't immediately get object's properties, because data is not received yet. I've read Apple reference about delegates and protocols, but I don't understand how to implement delegate method for this class, that would work like connection:didRecieveData: for NSURLConnection.
Can you explain me how to do this? I would be very glad, if you just post a link to an example. Thank you.
I don't understand how to implement delegate method for this class, that would work like connection:didRecieveData: for NSURLConnection.
The same way NSURLConnection does:
Give this object a property named delegate.
Set that property to another object.
In connectionDidReceiveData:, send a message to the delegate.
In the delegate, implement the method that the other object will be calling.
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"YOUR API URL"]];
NSString *email = #"username#gmail.com";
NSString *password = #"123456";
NSString *deviceToken = #"simulator";
NSString *deviceType = #"1";
NSString *post = [NSString stringWithFormat:#"email=%#&password=%#&deviceToken=%#&deviceType=%#",email,password,deviceToken,deviceType];
NSData *requestBodyData = [post dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPMethod = #"POST";
request.HTTPBody = requestBodyData;
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse response, NSData responseData, NSError *error)
{
NSLog(#"%#",responseData);
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:nil];
NSLog(#"%#",dic[#"data"]);
}];

Objective C: Function returning correct data for the first time of call and null for other times

Am a beginner in objective C, i am implementing a function that would query a web server and display the returning string in console. I am calling the function (getDatafromServer) repeatedly in a loop. The problem is that the first time am getting the value whereas the other times, it returns me a (null) in console... I've searched about memory management and check out on the forums but none have worked. Can you please guys tell me where am wrong in the codes below? Thanks in advance....
#implementation RequestThread
+(void)startthread:(id)param{
while (true) {
//NSLog(#"Test threads");
sleep(5);
NSLog(#"%#",[self getDatafromServer]);
}
}
+(NSString *) getDatafromServer{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *myRequestString = #"name=Hello%20&email=essssss#live.com";
NSData *myRequestData = [NSData dataWithBytes:[myRequestString UTF8String] length:[myRequestString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: [NSURL URLWithString:#"http://192.168.1.32/gs/includes/widget/getcalls.php?user=asdasd&passw=asdasdasd"]];
[request setHTTPMethod:#"POST"];
[request setHTTPBody: myRequestData];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"content-type"];
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *myString = [NSString stringWithUTF8String:[returnData bytes]];
[myRequestString release];
[request release];
[returnData release];
return myString;
[pool release];
}
#end
You have a problem with the autorelease pool. Firstly, as Nickolay has said, the release never happens because it is after the return. I'm amazed you aren't seeing compiler warnings. Make sure you have -Wall set in "other warning flags" and you have the "Run static analyzer" build option set.
Since you want to use the returned string outside of the function, the autorelease pool must also be outside the function, or the string may be deallocated before your log gets to it. Your code structure should look more like:
+(void)startthread:(id)param
{
while (true)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
//NSLog(#"Test threads");
sleep(5);
NSLog(#"%#",[self getDatafromServer]);
[pool drain]; // use instead of release in case you move to GC
}
}
The other problem you have is that you are not doing any error checking. How can you be sure that:
the request to the server is working?
the response from the server is encoded as UTF-8.
You need to check if returnData is nil after you get it and you need to examine the NSError if it is. So you need something like this:
NSError* error = nil;
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
if (returnData == nil)
{
the error object will be set and contain useful info.
}
You also need to check if myString is nil. If it is, it will be because the response was not encoded as UTF-8. With HTTP the default encoding is not UTF-8, it is ISO-8859-1. Also, the body of the response might not be character data at all. You need to examine the response to find out how to decode the data. So the code snippet I posted above should really look like:
NSError* error = nil;
NSURLResponse* response = nil;
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (returnData == nil)
{
// the error object will be set and contain useful info.
}
else
{
// You can get the content type and encoding from the response here.
}
Edit
Also, your code violates the Memory Management Rules. You did not obtain myRequestString or returnData through alloc, copy or new, neither have you retained them, so you must not release them.
That's bad idea to use autorelease pool in this function, and release it after 'return'.
Remove pool and everything should be ok.