I have a doubt regarding downloading data from a web service. One way is to download it in a single line mentioned below.
NSString *returnString = [[NSString alloc] initWithData:[NSURLConnection sendSynchronousRequest:urlrequest returningResponse:nil error:nil] encoding:NSUTF8StringEncoding];
And the other way to get it is via connectionDidFinishLoading
[..]
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:urlrequest delegate:self];
NSHTTPURLResponse *response;
[NSURLConnection sendSynchronousRequest: urlrequest returningResponse: &response error: nil];
if( theConnection )
{
webData = [[NSMutableData data] retain];
}
else
{
NSLog(#"theConnection is NULL");
}
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[webData setLength: 0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)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]);
NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length: [webData length] encoding:NSUTF8StringEncoding];
}
Is there any difference between these two? NSURLConnection delegate method is not called when I use a separate class to parse the response.
You're using sendSynchronousRequest:returningResponse:error: which does not call any delegates methods because it doesn't need to: when you call it, the main thread stops until the request is finished and you get the response.
If you want to make an asynchronous request, use connectionWithRequest:delegate:. I recommend to always do asynchronous responses since the synchronous request blocks the main thread and your UI can't respond during that time. Animations will become interrupted. Scrolling becomes jerky. If you do want to use synchronous requests you should do it in a background thread.
The -sendSynchronousRequest:returningResponse:error: method blocks the main-thread (whenever it runs on the main-thread of course, since it's possible to run this method from any other thread, but I believe this is not recommended).
The methods using the delegates are asynchronous, the methods will fire and the results will (at some point in the future) be returned in the delegate methods. This gives the user a more smooth experience, since the main-thread will not be blocked.
Edit: personally I hardly ever use the -sendSynchronousRequest:returningResponse:error: method for the aforementioned reasons. Most of the time I use this method when I need to build something quickly, for example a proof-of-concept. I guess one could use the method for small downloads, yet if a timeout occurs (because for some reason the server is down) the whole UI will be blocked for (I believe) 2 minutes, which would be very annoying for the enduser.
An excellent demonstration to clarify your doubt is available in apple sample apps.You can refer Apple's sample app for a better understanding of asynchronous request and parsing data in separate class.
Related
I have an app with four tabs. In each tab I connect to remote server using nsurlconnection, fetches the response and display accordingly. While testing the app, I get crashes randomly. If I try to reproduce the crash again I do not get crash. I do not understand what is root cause of the crash. I enable NSZombie,symbolicated crash logs,checked the memory leak but no luck.
I started the project in Xcode 3 and now I imported same project to Xcode 4.2, so are there any issues with compatibility of Xcode?
And I use the same name for nsurlconnection in all tabs like
In Tab 1 I defined nsurlconnection as conn and Tab 2 defined nsurlconnection as conn.
Does this definition causes any issue?
Please help me solve this random crashes
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: url];
conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if(label != nil){
progressView = [[ProgressView showHUDAddedTo:self.tabBarController.view animated:YES] retain];
progressView.labelText = label;
}
[request release];
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"didReceiveresponse");
if ([response isKindOfClass: [NSHTTPURLResponse class]]) {
if([(NSHTTPURLResponse *)response statusCode] == 200){
}
else{
//show Connection Error Alert
}
}
responseData = [[NSMutableData alloc]init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"didReceiveData");
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[progressView hide:YES];
NSLog(#"didFail");
//show failed alert
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"didfinish loading");
if([responseData length] > 0)
{
//handles response data
}
}
My guess without seeing code, would be that in a tab your making an NSURLConnection and doing something with the result when it completes. If you change tab before the result is returned then it is causing it to crash.
You need to cancel the NSURLConnection when viewDidDisappear, or make sure that whatever code is run on completion of it doesn't contain anything that would cause a crash if the tab isn't visible (like setting a labels text).
The way I handle this, is have a separate class that performs the URL requests that sends a notification when it's complete. That way in your viewDidAppear method you set your viewController to listen for the notifications, and in the viewDidDisapper method you stop listening for the notifications. So if your view isn't visible when the URL request finishes, the notification is fired but nothing happens.
Could you provide the output of the console? It seems not to be an error from Xcode.
These type of error usually appear when you try to access a deallocated object.
i am sure you have tried Instruments with memory leak. give a try with instruments with zombie tool, you can find it easily in instruments library.
run your code with this tool and if this crash is because of any zombie object then you will easily able to detect the location.
it has helped me few times.
Before my application is going to be closed I have to logout user from web service. And I can't find the very last function that is invoked before application die?
-(void)LogoutUser
{
int userId = [[GlobalData sharedMySingleton] getUserId];
NSString *soapMsg =
[NSString stringWithFormat:
#"<?xml version=\"1.0\" encoding=\"utf-8\"?>...", userId
];
NSURL *url = [NSURL URLWithString: #"http://....asmx"];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
NSString *msgLength = [NSString stringWithFormat:#"%d", [soapMsg length]];
[req addValue:#"text/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[req addValue:#"http://..." forHTTPHeaderField:#"SOAPAction"];
[req addValue:msgLength forHTTPHeaderField:#"Content-Length"];
[req setHTTPMethod:#"POST"];
[req setHTTPBody: [soapMsg dataUsingEncoding:NSUTF8StringEncoding]];
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
if (conn)
{
webData = [[NSMutableData data] retain];
}
}
-(void) connection:(NSURLConnection *) connection didReceiveResponse:(NSURLResponse *) response
{
[webData setLength: 0];
}
-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data
{
[webData appendData:data];
}
-(void) connection:(NSURLConnection *) connection didFailWithError:(NSError *) error
{
[webData release];
[connection release];
}
-(void) connectionDidFinishLoading:(NSURLConnection *) connection
{
NSString *theXML = [[NSString alloc]
initWithBytes: [webData mutableBytes]
length:[webData length]
encoding:NSUTF8StringEncoding];
[theXML release];
[connection release];
[webData release];
}
There are two places you'll need to trigger your logout code from, both of which are detailed in the UIApplicationDelegate Protocol Reference documentation.
For pre-iOS 4 devices (and to cover other circumstances) you should use:
- (void)applicationWillTerminate:(UIApplication *)application
As Apple puts it:
For applications that do not support
background execution or are linked
against iOS 3.x or earlier, this
method is always called when the user
quits the application. For
applications that support background
execution, this method is generally
not called when the user quits the
application because the application
simply moves to the background in that
case. However, this method may be
called in situations where the
application is running in the
background (not suspended) and the
system needs to terminate it for some
reason.
However, you'll need to use...
- (void)applicationDidEnterBackground:(UIApplication *)application
...on iOS 4+ devices, as (once again from the Apple docs):
In iOS 4.0 and later, this method is
called instead of the
applicationWillTerminate: method when
the user quits an application that
supports background execution
That said, irrespective of all the above, you'll most likely want to logout of the web service when your app is backgrounded and log back in when it's "woken up" as well. See the above mentioned applicationDidEnterBackground: method and the applicationWillEnterForeground: method documentation for more details.
- (void)applicationDidEnterBackground:(UIApplication *)application {
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, called instead of applicationWillTerminate: when the user quits.
*/
}
this may be not a last function but. you can do logout here.
For typical apps under iOS 4.x, applicationWillResignActive and perhaps applicationDidEnterBackground will be called both before your app is terminated (at some unknown time in the future), and at other times as well when the app isn't being terminated. However it might be a good idea to log out here, as your app may never get any further CPU run time.
If you have pending network activity, such as trying to logout, you might want to use the multitasking call beginBackgroundTaskWithExpirationHandler: to request a bit of additional time in the background to finish the log out process, such as handshaking with any network callbacks required.
I am trying to get data from a server using the following code.
+ (NSString *) getData:(NSString *)methodName parameters:(NSDictionary *) parameters error:(NSError **)error
{
NSString *body = [UdoziProxy getRequestBody:methodName parameters:parameters];
NSMutableURLRequest *request = [UdoziProxy createRequest:body];
// Send the request .
NSHTTPURLResponse *urlResponse = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:error];
if (responseData == nil || error !=nil) {
return nil;
}
return [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] autorelease];
}
It works fine when the server is running but when I stop the server deliberately, the responseData object has still value and is not nil. How can I handle the situation where either the connection is lost or the server is down ?
You need to set the cache policy on your NSURLRequest object. If the synchronous API still doesn't honour that, you need to switch to using the asynchronous API and throw away the cache request object when you receive it. If that doesn't work, then maybe your network has a transparent proxy which is doing some caching for you.
you have to implement NSURLConnection delegates in
you can get failure status in
the following delegate
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
responseData =nil
}
there you have to make the recieved data to nil value
also its safe to
get data in
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{ }
which indicates that the connection is finished
I've been reading several threads and questions about this issue but I didn't find the solution.
I have some asynchronous calls performed with
[NSURLConnection connectionWithRequest:anURLRequest delegate:self];
The problem is that I want the interface to be operative but it is blocked until the connection is finished.
Is this solved launching another thread? Where is exactly the problem?
EDIT
Ok, after retrieve data I parse it with NSXMLParser, that do it synchronously and blocks main thread. Is this correct? Then, maybe I need to parse in another thread. Anyone has a guide?
From the docs:
Messages to the delegate will be sent on the thread that calls this method. For the connection to work correctly the calling thread’s run loop must be operating in the default run loop mode.
Are you sure that this code is being called on a run loop in default mode and not from a thread created by yourself with a different type of run loop mode?
The UI should not be locking up when you use connectionWithRequest. Try creating a label in your UI and have your connection update it with the current amount of data, like so:
- (void)downloadContentFromUrl:(NSURL *)url {
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (connection) {
receivedData = [[NSMutableData data] retain];
self.downloadProgressLabel.text = #"Downloading...";
} else {
// oh noes!
}
}
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[receivedData setLength:0];
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[receivedData appendData:data];
int kb = [receivedData length] / 1024;
self.downloadProgressLabel.text = [NSString stringWithFormat:#"Downloaded\n%d kB", kb];
}
connectionWithRequest does indeed run in it's own thread - no need for you to worry about this. In fact it must be started from the main thread. Check out the NSUrlConnection doc for more info.
+ (id)connectionWithRequest:(NSURLRequest *)request delegate:(id)delegate];
This method should create an asynchronous-request (that means that it runs in the background and it doesn't block the UI). You should check if there's another class/method in your file that blocks the UI (for example NSData's '+ (NSData *)dataWithContentsOfURL:(NSURL *)URL').
I don't know if it could help anyone, but I've the same problem (asynchronous URL request blocking the UI) but it was due to:
NSLog(#"dataReceived: %#", data);
in the connectionDidReceiveData method.
In my case I was trying to update the UIProgressView.progress property. I calculated the new value like that
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[self.progress setProgress:self.downloadedData.length / self.fileSize ];
[self.downloadedData appendData:data];
}
Which doesn't work, I replaced this snippet of code with
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
self.downloadedSize += data.length;
[self.progress setProgress:self.downloadedSize / self.fileSize ];
[self.downloadedData appendData:data];
}
And now the progress view updates with no problem.
i'm a beginner to iphone.i want to create a login page for my application.i cant figure out how to connect to a php page and retrieve corresponding data from mysql database to the iphone.could any one guide me how to go about it.
what does the iphone have to do with a connection between php and mysql ?
PHP will run with on a web server probably apache installed on some computer and it will connect to a MySQL db .. and u will access that php page from your iphone with a browser. Not sure what part will the iphone have in all this other than providing the browser
You might want to have a look at NSURLRequest which you can use with a NSURLConnection to send e.g. GET-Parameters to a URL. You can then implment the NSURLConnectionDelegate to respond to incoming data:
1) setup connection
receivedData =[NSMutableData data];
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:url]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:20.0];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
2) Setup delegate methods in self:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse*)response {
NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *) response;
if([httpResponse statusCode]==200)
[receivedData setLength:0];
else
NSLog(#"Http-Reponse %u",[httpResponse statusCode]);
// HANDLE ERROR!
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// append the new data to the receivedData
// receivedData is declared as a method instance elsewhere
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// HANDLE THE CONNECTION ERROR
// release the connection, and the data object
[connection release];
// receivedData is declared as a method instance elsewhere
[receivedData release];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// receivedData contains the data
// convert to string:
NSLog(#"finished loading: %#",[[[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding] autorelease]);
[connection release];
[receivedData release];
}
You'll want to expose the authentication functionality as a web service, then use the URL Loading code posted by Felix L. to initiate an actual connection to the web service.
You'll probably want to send a response from the server as XML, if so, you'll parse that response with an NSXMLParser, otherwise you can just send the response in whatever format you'd like and parse it appropriately.