Asynchronous vs synchronous methods on iphone - iphone

I saw there is a method for synchronous, like if I wanted to do something like:
-(IBAction)doNSURLConnSync {
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSError *error = nil;
NSURLResponse *response = nil;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
}
How does it perform differently than if I did asynchronous:
-(IBAction)doNSURLConnASync {
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
if (connection) {
responseData = [[NSMutableData alloc] init];
[webview loadHTMLString:#"" baseURL:nil];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}
else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Network error occured"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"%s", __FUNCTION__);
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[connection release]; // got passed in as a param. we are done with it now.
[webview loadData:responseData
MIMEType:nil
textEncodingName:nil
baseURL:nil];
[responseData release];
}
Also, with my doNSURLConnSync method, I am just trying to load a UIWebView. Is there a reason why it doesn't? The button just sits there and stays highlighted, while it tries to access the webpage, but does nothing in the end, compared to the asynchronous version.
Also, for networkactivityindicator in my asynchronous code, I wanted to set my UIWebView to blank, have the indicator on while my webview loads, and then turn off the network activity indicator once the page loads. However, if I delete the loadHTMLString method, the network activity indicator works as it's supposed to, but with the loadHTMLString, the UIWebView goes blank, but the network activity indicator does not. Any thoughts? Thanks.

First, for the syncrhonous:
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
This is the method signature, when you call the synchronous request, it wil return the data for you to display on the UIWebView and you have to call the UIWebView to display the data. However, the synchronous calling will block your UI until all the data come back. So, be careful with UX.
NSUrlConnection sendSynchronousRequest
For the asynchronous, it will not block your UI, user can still do everything they want with it, like go back to the previous screen. So, usually, it is recommended for big and long network
I don't know why it doesn't show your indicator. But why do you need this line : [webview loadHTMLString:#"" baseURL:nil]; . You only need to call it after you got your HTML response

A synchronous request ties up the main thread, which you should reserve for UI widget updates.
Doing an asynchronous request on a background thread frees up the main thread to update the UI.
Pull your UI update code (indicator view and web view) into separate methods, calling them on the main thread with -performSelectorOnMainThread:withObject:waitUntilDone:
[self performSelectorOnMainThread:#selector(updateWebview) withObject:nil waitUntilDone:YES];

Related

Checking for valid IP for connection with NSURLConnection

I currently have an ap that attempts to open a webview based on certain servers I am communicating with.
However, I allow the user the capability to type in their own server IPs in case both the iphone/ipad and server(or other device) are not on the same network. However, I am attempting to use NSURLConnection to detect if I can open a connection with the given IP however NSURLConnection never returns an error, even if the server address(or even a random web address) is completely bogus.
the .h
#interface DetailViewController : UIViewController <UIPopoverControllerDelegate, UISplitViewControllerDelegate, UITableViewDataSource, UITableViewDelegate,UIWebViewDelegate, NSURLConnectionDataDelegate> {
the relevant code in the .m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
(NSIndexPath *)indexPath
{
dev_ip = #"http://www.asdfasfdfa.com/";
//dev_ip = (random ip)
NSMutableURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:dev_ip]];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (conn) {
NSLog(#"Connection established");
}
else{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:[NSString stringWithFormat:#"No Device at designated IP"]
delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
}
}
this if/else always outputs 'Connection established.' Is this something NSURLConnection should not be used for? If so, what can I use to detect devices at given IP's for connectivity. I need to stop the user from attempting to connect to bad IP's so whats the best method in doing so?
NSURLConnection, when used with delegation, will call delegate methods when it connects, fails to connect and receive data. You should look into NSURLConnectionDelegate.
Here's a quick example:
// In your .h
#interface MyClass : NSObject <NSURLConnectionDelegate, NSURLConnectionDataDelegate>
#end
EDIT You actually need both delegates.
// In your .m
#implementation MyClass
- (void)myMethodToConnect {
NSString *dev_ip = #"http://74.125.137.101"; // Google.com
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:dev_ip]];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
switch ([(NSHTTPURLResponse *)response statusCode]) { // Edited this!
case 200: {
NSLog(#"Received connection response!");
break;
}
default: {
NSLog(#"Something bad happened!");
break;
}
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"Error connecting. Error: %#", error);
}
#end
Also, just throwing it out there too, you don't necessarily have to use an asynchronous call. You can send a synchronous call which doesn't require you to implement a delegate. Here's how:
NSString *dev_ip = #"http://www.asdfasdfasdf.com/";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:dev_ip]];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *connectionData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
You can check the response value and errors.
There's a better way to test whether a server is valid or not. The Reachability class provides good API for this purpose.

Download file to app directory in iphone

I am new to iPhone,
I am currently developing an iPhone app and would like to implement the ability to download file from the internet. I have created the UIWebView, but want to know the best way of capturing the files when they are linked to in the webview and then download them to a specified folder in the documents directory.
Here is my code snippet,
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.fileData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data1
{
[self.fileData appendData:data1];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[activityIndicator stopAnimating];
activityIndicator.hidden=TRUE;
}
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
url = [request URL];
//CAPTURE USER LINK-CLICK.
NSString *file = [NSString stringWithString:[url absoluteString]];;
NSURL *fileURL = [NSURL URLWithString:file];
NSURLRequest *req = [NSURLRequest requestWithURL:fileURL];
[NSURLConnection connectionWithRequest:req delegate:self];
data = [NSData dataWithContentsOfURL:url];
//Saving file at downloaded path.
DirPath = [DestPath stringByAppendingPathComponent:[url lastPathComponent]];
[data writeToFile:DirPath atomically:YES];
UIAlertView* Alert = [[UIAlertView alloc] initWithTitle:#"Download Complete !"
message:nil delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[Alert show];
[Alert release];
return YES;
}
Problem is where to write condition, if my downloading gets failed and also i am getting Warning in my log shows : “wait_fences: failed to receive reply: 10004003”
It seems like you're doing the same thing multiple times in this code. For example, you create a new NSURLRequest object even though one was already passed inside of the delegate method? You also run the synchronous dataWithContentsOfURL method after creating a new NSURLConnection? You also append some data into a property only to do nothing with that property?
What you probably want to do is create a new asynchronous NSURLConnection when the UIWebView should load. From there, allow the UIWebView to load that page. Inside of your delegate methods, instead of appending the data to some property, append the downloaded data to a file. When the connection finishes downloading, present your alert informing the user that the data was downloaded and saved.

NSUrlConnectionDelegate not calling methods to load data

I have looked at NSURLConnectionDelegate connection:didReceiveData not working already, but there didn't seem to be any good result from that, so I am curious why I am not able to get any data.
I put in breakpoints in didReceiveResponse and didReceiveData.
It does print out "connection succeeded", so I know that the connection is started.
I am using ARC for memory management.
- (void)load {
request = [NSMutableURLRequest requestWithURL:myURL
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:60];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (conn) {
[conn start];
NSLog(#"connection succeeded, %s", [myURL description]);
responseData = [NSMutableData data];
} else {
NSLog(#"connection failed");
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
UPDATE:
To see how I test this look at Asynchronous unit test not being called by SenTestCase.
I did implement the two methods mentioned by jonkroll, in his answer, I just didn't show them, but, they also aren't being called.
I had added [conn start] only because it wasn't working, and I was hoping that may solve it, but no such luck.
When you declare your connection like this:
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
You are creating a local pointer. When your method completes, since it was the last strong reference to the NSURLConnection, ARC releases it. You need to use a strong ivar (and/or) property to hold a strong reference to the NSURLConnection you create.
Edit
Here is basic sample of code that I tested in a sample project. Give it a run. Verbose logging helps.
#implementation <#Your class here#> {
// With ARC ivars are strong by default
NSMutableData *_downloadedData;
NSURLConnection *_connection;
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSHTTPURLResponse *realResponse = (NSHTTPURLResponse *)response;
if (realResponse.statusCode == 200){
// Really any 2** but for example
_downloadedData = [[NSMutableData alloc] init];
NSLog(#"Good response");
} else {
NSLog(#"Bad response = %i",realResponse.statusCode);
}
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
if (connection == _connection){
[_downloadedData appendData:data];
NSLog(#"Getting data...");
}
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
if (connection == _connection){
_connection = nil;
NSLog(#"We're done, inform the UI or the delegates");
}
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
_connection = nil;
NSLog(#"Oh no! Error:%#",error.localizedDescription);
}
- (void)load {
NSURL *url = [NSURL URLWithString:#"http://www.google.com/"];
NSURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:60];
// Assign strong pointer to new connection
_connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
NSLog(#"Connection was initialized? = %#",(!!_connection)?#"YES":#"NO");
}
#end
The NSURLConnection method initWithRequest starts an asynchronous request for data from a url. Because the request is done asynchronously you can't expect to work with the response in the same method in which the request is invoked. Instead you need to do so in the NSURLConnection's delegate callback methods. You have already implemented didReceiveResponse: and didReceiveData:, but there are a couple others that will be useful to you.
If you want to look at the contents of the response you should do so in connectionDidFinishLoading:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// response is complete, do something with the data
NSLog(#"%#", responseData);
}
The fact that your code prints out "connection succeeded" doesn't really mean that the request was successful, only that the NSURLConnection object was created successfully. To test whether there was a problem with the connection you can implement the delegate method connection:didFailWithError:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
Also there is no need to call [conn start]. The request will be started automatically when you call initWithRequest:
I suggest reading Apple's documentation on Using NSURLConnection for more details.

NSURLConnection delegate methods are not called

I am trying to create a simple NSURLConnection to communicate with a server using a GET request. Connection works well, but delegates methods of NSURLConnection are never called..
Here is what am doing:
NSString *post = [NSString stringWithFormat:#"key1=%#&key2=%#&key3=%f&key4=%#", val1, val4, val3, val4];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease] ;
[request setURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://www.domain.com/demo/name/file.php?%#", post]]];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
Have implemented the following delegate methods, but none of them is called..
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(#"did fail");
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
NSLog(#"did receive data");
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSLog(#"did receive response ");
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSLog(#"did finish loading");
[connection release];
}
Am I missing something?
Try running the operation on main thread:
NSURLConnection * connection = [[NSURLConnection alloc]
initWithRequest:request
delegate:self startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
[connection start];
Are you calling this on a background thread? If you are performing this on a background thread, the thread is probably exiting before the delegates can be called.
Try to check length for the received response it should not getting 0 byte of data.
Apart from checking if the request is called from the main thread, you can check if you give back execution time to the system (if you exit "main").
I had some test code that would stay in a loop until the delegate was called : it would never be called, because the system needs to do stuff in order for the delegate to be called, in the main thread.

"-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data" not called

Have a look to this code snippet:-
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[webData setLength: 0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"Recieving Data...");
[webData appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"ERROR with theConenction");
[connection release];
[webData release];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"DONE. Received Bytes: %d", [webData length]);
NSLog(theXML);
}
I am calling a SOAP web service.There are no errors or warnings displayed in my code.
When I hit the web service through safari it works fine. But the problem arises when I try
hit it through my codes.
Everything works fine but the connection:didRecieveData does not gets called.
Thus, I get no data in the webData variable. This webData is a NSMutableData object.
The problem seems to be silly but any one with any answers ....
Thank You All.
I suspect you are having a memory management issue. I could be mistaken on this, but I believe that even:
NSURLConnection* connection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
won't work, because connection will be released at the end of the containing method, when connection goes out of scope. Make sure NSURLConnection *connection and NSMutableData *data are declared as member variables where ever you are doing this, and that you alloc and init them appropriately. My code usually looks like:
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:30.0];
// cancel any old connection
if(connection) {
[connection cancel];
[connection release];
}
// create new connection and begin loading data
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if(connection) {
// if the connection was created correctly, release old data (if any), and alloc new
[data release];
data = [[NSMutableData data] retain];
}
Also, release the connection and data in dealloc. For good measure, release and set them to nil at the very end of didFailWithError and didFinishLoading:
[connection release];
connection = nil;
[data release];
data = nil;
Good luck; I've done this a million times, let me know if you cannot get it working.
You don't happen to be calling the NSConnection in a thread do you? If you are then what's happening is that the thread is terminating before NSConnection and its delegates have finished so it'll just bomb out without an error.
A workaround for this is in this thread
You're not getting any error messages in didFailWithError either? Kind of a silly suggestion, but are you sure you're setting the proper NSURLConnection delegate?
NSURLConnection* connection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
Sometimes it's something small like that.
Another idea is to drop in a toolkit like ASIHTTPRequest and see if it works going through them.
There also could be problems, if are trying to start NSURLConnection from another Thread.
Please call method [connection start] on main thread, if you have not customized Run Loop for it.