I m using sendSynchronousRequest to get the data from the server. I know that synchronous will wait until the data received for that request.
But the problem comes when user by mistake enters some non-existing url and than tries to get response. In this case, if user goes in to background and than comes into foreground it shows only black screen. It only shows status bar. Also its not showing any background application. I have to press Home button to come out of my application.
On simulator, After 1+ minute it shows me the message that "Request time out" (No crash).
On Device, within 1 min application get crashes.
Any suggestion. Any Help. This is really a serious issue in my app.
Thanks.
Just like Julien said, the watchdog is killing your app. To answer some questions:
why does this happen only on the simulator?
Because when you're debugging the watchdog leaves your app alone, it can take time.
why does this happen only when the user enters a wrong url?
Because of the system timeout, the system will keep trying for 60 secs if it can't find a server.
so the problem is synchronous vs asynchronous?
No, the problem is the thread, you can do the same operation in a background thread, just don't do it on the main thread and the watchdog will leave you alone.
why is the screen black when the app comes up?
Remember, you are making blocking stuff on the main thread, the thread that draws...
Hope that was all. Let me know if I missed something.
Why not setting a timeout for your connection?
NSString *urlString = TEST_CONNECTION;
NSError *error = nil;
NSHTTPURLResponse *response = nil;
NSURLRequest *request = [NSURLRequest
requestWithURL:[NSURL URLWithString:urlString]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:5.0];
NSData *conn = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
This should release the synchronous waiting after a number of seconds, which should solve your problem without going with an asynchronous call (which sometimes isn't the proper solution)
I know this works properly because this is how I check if I am connected to a certain VPN (where reachability flags totally fail).
you should take a look to this article: https://developer.apple.com/library/ios/#qa/qa1693/_index.html
iOs contains a watchdog, if your application is blocked to much time on an operation on the main thread, this one will be killed. (for more details about Watchdog: http://en.wikipedia.org/wiki/Watchdog_timer)
So if you want to download something, don't download it on the main thread.
RELATE
UIImage *image = [self.imgCache objectForKey:urlString];
if(!image){
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString] cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:60.0];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
NSLog(#"%#",response);
UIImage *img = [UIImage imageWithData:data];
//
if(img)
{
dispatch_sync(dispatch_get_main_queue(), ^{
[self.imgCache setObject:img forKey:urlString];
completionBlock(img);
});
}
});
}
else{
completionBlock(image);
}
use ASIHttpRequest class instead of NSURLConnection , its nothing but wrapper around NSURLConnection and has very simple callbacks , you can also set time to complete a request. Please go through this link for more info http://allseeing-i.com/ASIHTTPRequest/
I think you first have to test user data whether it is correct or not and than only if it is correct, sends the request otherwise prompt user that "please enter correct data"...
or
when your parsing of data in response failed. You can also make protocol delegate method i.e FinishWithError so that you come up with your last UI.
Try this one:
#import "ASIHTTPRequest.h"
//In a method
[self performSelectorInBackground:#selector(DownLoadImageInBackground:) withObject:imgUrlArr];
-(void) DownLoadImageInBackground:(NSArray *)imgUrlArr1
{
NSURL * url = [Image URL];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
}
-(void)requestFailed:(ASIHTTPRequest *)request
{
NSLog(#"URL Fail : %#",request.url);
NSError *error = [request error];
// you can give here alert too..
}
-(void)requestFinished:(ASIHTTPRequest *)request
{
NSData *responseData = [request responseData];
UIImage *imgInBackground = [[UIImage alloc]
initWithData:responseData];
[imageView setImage: imgInBackground];
}
This might help you: I am also loading a number of images at one time, so images that have no proper data show a black screen. To avoid this, try to resize your imageview.
You could check the reachability of the URL before starting the request.
Apple has Reachability Methods to do so. But its easier to use a wrapper. E.g. ASIReachability.
I think the application crashing because you does not get any data when user enters wrong URL and you are using this 'returned' nil NSData to do stuffs.
I think this will fix your problem
NSData *data=[NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if(data!=nil){
///
} else {
NSLog(#"NO DATA");
}
Related
I am using following code to download file from url's asynchronously,
NSMutableData *responseData = [[NSMutableData alloc] init];
NSURL *url = [NSURL URLWithString:#"http://www.tuiscos.com/images/trading.png"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// do something with data
[responseData appendData:data];
myImage8.image = [UIImage imageWithData:data];
NSInteger len = response.expectedContentLength;
NSInteger receiverdBytes = 0;
receiverdBytes = data.length+ receiverdBytes;
float prog = (float)[responseData length]/(float)len;
[progress8 setProgress:prog];
}];
as the download progresses, I want to update the progress bar, but using this code, I am not getting a gradual progress, instead it is waiting to complete the download and jumping to the maximum value. How can I make a gradual progress in the value?
Can somebody provide a sample code? For asynchronous method with delegate methods.
Thanks :)
If you don't want to code everything on your own, I would suggest using ASIHTTPRequesst on this task:
http://allseeing-i.com/ASIHTTPRequest/How-to-use
It is very simple to implement and you can do simultaneous, asynchrony downloads. It also provides delegates for all needs, also for progress updates.
I used it in my projects for almost a year now and never regretted it.
CompletionHandler is executed at completion, of course. You have to a delegate for the connection. Use -initWithRequest:delegate: method. You will have to code the NSURLConnectionDelegate methods and the one you need to set progressView value is -connection:didReceiveData:
Here is the doc: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.pdf
I need a good solution for my little issue.
What I have at the moment:
My App works with asyncron https request very well. At the moment the app uploads an JSON object, so far so good. After that I save the object in an sqlite database.
Now I have to change to determine what the received server response code is.
I will get an response code like 000 if the uploaded data was valid, and a code like 151 if not. For that I have to wait for the server response to save the response code in the database as well.
I experimented with NSThread, too. But that didn't work out as well as I expected.
Somebody any suggestions? I mean, it should be one of the most common things ;) I just don't see it.
thx, dominik
I usually use an NSOperationQueue to manage my communication. This lets you use a synchronous request instead of asynchronous, and handle the whole shebang in one method.
Example:
-(void)doIt {
NSInvocationOperation *op = [[[NSInvocationOperation alloc] initWithTarget:self selector:#selector(doIt_) object:nil] autorelease];
[opQueue addOperation:op];
}
-(void)doIt_ {
NSData *data;
NSMutableURLRequest *request;
NSURLResponse *response = nil;
NSError *error = nil;
// Build request to spec
data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:error];
// do something with data
[self performSelectorOnMainThread:#selector(yadda) withObject:yaddayadda waitUntilDone:NO];
}
If you do use the asynchronous loading methods, you have to implement a delegate to catch the response data as it comes down, and do something with it when finished.
I recommend that you take a look at ASIHTTPRequest which is a wrapper for the CFNetwork API. Particularly look into the mechanism it offers for asynchronous HTTP requests, where you can define blocks to be executed once the request has completed or failed and can you can inspect the response data easily. Your code would be something like this:
__block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setRequestMethod:#"POST"];
// add your json object to the request
[request setCompletionBlock:^{
int responseCode = [request responseStatusCode];
NSData *responseData = [request responseData];
// do whatever you want with this info
}];
[request setFailedBlock:^{
NSError *error = [request error];
// handle the error
}];
[request startAsynchronous];
I need to connect to a protected site and try to use ASIHTTPRequest
Here is my code:
url = [NSURL URLWithString:#"http://myurl/page.aspx"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setUsername:username];
[request setPassword:password];
[request setDomain:domain];
[request startSynchronous];
NSError *error = [request error];
if (!error) {
[webView loadHTMLString:[request responseString] baseURL:[request url]];
}
When I use NSLog to see [request responseString], I get the correct HTML, but the result is a blank white webview.
From the outgoing request warnings that little snitch displays, I see the initial request and one going to an external resource.
My guess so far is that the inital request correctly uses the authentication from ASIHTTPRequest and fetches the page, but the uiwebview will try to load the included .js files and since uiwebview is not authenticating, it will not render the page at all ...
Anybody knows how to fix my problem?
Have you tried ASIWebPageRequest? My guess is you have resources in that page that are not downloaded, like http://myurl/image.jpg
ASIHttpRequest runs asynchronously. You need to put your webview loading code into the ASIHTTPRequest callbacks. (requestFinished).
Add a method to your class as follows:
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSError *error = [request error];
if (!error) {
[webView loadHTMLString:[request responseString] baseURL:[request url]];
}
}
There is also a requestFailed method that you can use to trap additional errors, you should implement this as well. One or the other of these methods will be called once ASIHttpRequest completes.
Note you will probably also need to set the delegate on the request before making the asynch call. (so same place you set the auth stuff).
request.delegate = self;
i try to use applicationWillTerminate in my app and i want that in this function i will send data to server,with this:
reqURL = [reqURL stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:reqURL]];
NSURLResponse *resp = nil;
NSError *err = nil;
NSData *response = [NSURLConnection sendSynchronousRequest: theRequest returningResponse: &resp error: &err];
but the problem is that this function didn't call when i close my app.
In iOS 4.x, apps are often moved into the background (in order to be quickly resumed) as opposed to being purged from memory. applicationWillTerminate: is only called when the latter occurs. You probably want applicationDidEnterBackground: which will be called when your app is quit but continues to stick around in the background. You will only have about 5 seconds to do any clean-up so you may also want to look into the beginBackgroundTaskWithExpirationHandler: / endBackgroundTask: methods in case your request is of critical importance and possibly requires more time.
Most likely the application will be going into the background state, not exiting completely. Try putting your code into:
- (void)applicationWillResignActive:(UIApplication *)application;
I use ASIHTTPRequest to do http requests in my iPhone app. ASIHTTPRequet comes with that feature that starts the activity indicator when issuing a request and stops it when finished. The problem is, once I started a request the indicator never stops and keeps spinning as long as my app runs.
Here is my code, a little utility method that fetches some content from the web synchroniously (since it gets started in a different thread):
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL: [NSURL URLWithString: url]];
[request startSynchronous];
NSError *error = [request error];
NSString *response = nil;
if (error) {
NSLog(#"error %#", error);
return nil;
}
int statusCode = [request responseStatusCode];
response = [NSString stringWithString: [request responseString]];
NSLog(#"status code: %d response: %#", statusCode, response);
if (statusCode != 200) {
return nil;
}
return response;
The above code works just fine, I get the contents of the given URL as a NSString only the indicator keeps spinning. My question is: Why does the indicator never stop and how to fix it? Do I have to release some resources here?
This is a bug that was fixed very recently in the development version of ASIHTTPRequest:
http://github.com/pokeb/asi-http-request/commit/35ea592084145b3332861344f36b52dbcaafa351
(It only affects synchronous requests started on a secondary thread)
Can you try the same thing with an asynchronous request and see if that changes it? I use ASIHTTPRequest and I've never noticed this behavior, but I also never use synchronous requests.