I am aware of discussions regarding nsurlconnection on ios, and that there is a minimum of 240 seconds for a timeout. My question is, if I am sending a synchronous call via NSURLConnection's + (NSData *)sendSynchronousRequest:(NSURLRequest )request returningResponse:(NSURLResponse *)response error:(NSError **)error, is there any chance I can cancel this before the 240 seconds is up? I am thinking perhaps setting a timer to cancel this synchronous request, but im not even sure if its even possible? Im thinking:
[self performSelector:#selector(cancelRequest:) withObject:myRequest afterDelay:myTimeOut];
I have a feeling this will result in disaster if somehow the request has been released, and I would have no way to determine that. Thoughts? Has anyone tried to do this? This is a synchronous call.
You cannot cancel it. Simply don't use it and use an asynchronous call instead. Those you can easily cancel.
This seemed to work for me:
NSURL *url = [NSURL URLWithString:#"http://someurl.com"];
NSURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:5];
NSHTTPURLResponse *response = nil;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL];
if (response == nil) {
// timed out or failed
} else {
// all good
}
Ofcourse setting the timeout interval to how long you want it to block the main thread before timing out - The above code successfully timed out after 5 seconds
Tested in iOS6 & iOS5.1
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10.0];
webData = (NSMutableData *)[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
if (webData==nil) {
[self displayAlert:#"Time Out" message:#"Request Timed Out"];
}
Timeout in exactly 10 seconds.
Related
I have a nested loop of sending the request.
-(void) download
{
for(NSString *id in array)
{
//init with request and start the connection
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy: NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request deletegate:self];
[conn start];
}
}
-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data
{
//enter here secondly
}
-(void) connectionDidFinishLoading:(NSURLConnection *) connection
{
//enter here last, after finish the for loop
//my intention is use the downloaded data to do something before sending a new request.
}
The problem is that I want to enter "-(void) connectionDidFinishLoading:(NSURLConnection *) connection" first before send the request again in the for loop.
But currently it will finish the for loop and sent all the request before enter to "-(void) connectionDidFinishLoading:(NSURLConnection *) connection".
You Should Try This NSURLConnection is deprecated in iOS9
for (NSString *URL in URLArray) {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// check error and/or handle response here
}];
[task resume];
}
and use dispatch_group_t group = dispatch_group_create();
add line to for loop dispatch_group_enter(group); will call
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// Request Finish
});
for your goal
In your case you need to try block function because as per your requirement you want response of the first connection for another request.
for(NSString* url in array)
{
// Generate a NSURLRequest object from the address of the API.
NSURL *url = [NSURL URLWithString:urlLink];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// Send the request asynchronous request using block!
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error) {
NSLog(#"Error in updateInfoFromServer: %# %#", error, [error localizedDescription]);
} else if (!response) {
NSLog(#"Could not reach server!");
} else if (!data) {
NSLog(#"Server did not return any data!");
} else {
[self doStuffWithData:data];
}
}];
}
URL loading is not a synchronous operation (or at least should never be done synchronously), because it can take up to 90 seconds just for a DNS lookup failure, and almost infinitely long if the server keeps dribbling out data. If you block the main thread for even a fraction of that amount of time, iOS will kill your app.
Instead of scheduling the requests in a loop and waiting for them to finish, you need to schedule the first request (and only the first request). Then, in your connectionDidFinishLoading: method (and maybe your connection:DidFailWithError: method), schedule the next request.
With that said, unless you still need to support iOS 6/10.8 and earlier, you should probably be using NSURLSession. (The same general advice applies; the delegate method names are changed to protect the guilty.)
I check internet connection following way.
in viewDidload
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
then
- (BOOL)connected {
return [AFNetworkReachabilityManager sharedManager].reachable;
}
But even if i don't have internet connection but 3g is on, it still returns true.
How can i detect if the real internet connection exists?
Reachability being true doesn't mean that the next network access you do will succeed -- you need to assume that network access can always fail.
It's good at letting you know the user has turned off network access (like Airplane mode), but if you are on a bad network, dropping lots of packets, then Reachability will still return true. It should also detect if you can't get any Wifi or 3G at all. But, if you have one bar -- it's going to return true, even if that means that network access won't really work.
I did it this way. I know it's no elegant, but...
+ (void)checkInternet:(connection)block
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSURL *url = [NSURL URLWithString:#"http://www.google.com/"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = #"HEAD";
request.cachePolicy = NSURLRequestReloadIgnoringLocalAndRemoteCacheData;
request.timeoutInterval = 10.0;
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:
^(NSURLResponse *response, NSData *data, NSError *connectionError)
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
block([(NSHTTPURLResponse *)response statusCode] == 200);
}];
}
I am a beginer in iOS programming. I have some problem with NSURLConnection: I have installed SWRevealViewController https://github.com/John-Lluch/SWRevealViewController and when my app is loading Data from server, I can't use interaction with screen. I can't open my SWR-menu while Data is loading.
Here is my SWR in viewDidLoad:
SWRevealViewController *revealViewController = self.revealViewController;
if ( revealViewController ) {
[self.openMenyItmet setTarget: self.revealViewController];
[self.openMenyItmet setAction: #selector( revealToggle: )];
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
}
After that, I called Get method in viewDidLoad:
[self GetQUIZ];
Method detail:
- (void)GetQUIZ {
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
NSString *url = [NSString stringWithFormat:#"http://stringlearning.com/api/v1/user-quiz?token=%#",[[NSUserDefaults standardUserDefaults] stringForKey:#"token"]];
[request setURL:[NSURL URLWithString: url]];
[request setHTTPMethod:#"GET"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setValue:[UIDevice currentDevice].name forHTTPHeaderField:#"device"];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
NSLog(#"Left menu, User details: %#", [[NSString alloc] initWithData:[request HTTPBody] encoding:NSUTF8StringEncoding]);
NSLog(#"%#", [request allHTTPHeaderFields]);
if(conn) {
NSLog(#"Connection Successful");
} else
NSLog(#"Connection could not be made");
And then I use data in connectionDidFinishLoading:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSError *deserr = nil;
NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:responseData options: 0 error: &deserr];
I read that i should use async methods, but I never use it before. Would you write some detail solution ?
Maybe, does have different path?
I would be very grateful for the help!
I'd suggest starting with NSURLSession, which is a modern API that will accomplish the same thing, asynchronously.
To use NSURLSession, you need a few piece of the puzzle:
A web address to reach, and optionally any payload or custom headers.
An instance of NSURL: where you're downloading from and an NSURLRequest to wrap it in.
An NSURLSessionConfiguration, which handles things like caching, credentials and timeouts.
The session itself.
You need an NSURLSessionTask instance. This is the closest object to your NSURLConnection. It has callbacks via delegate or a completion block, if you just need to know when it finishes.
Here's how this would look in code:
// 1. The web address & headers
NSString *webAddress = [NSString stringWithFormat:#"http://stringlearning.com/api/v1/user-quiz?token=%#",[[NSUserDefaults standardUserDefaults] stringForKey:#"token"]];
NSDictionary <NSString *, NSString *> *headers = #{
#"device" : [UIDevice currentDevice].name,
#"Content-Type" : #"application/x-www-form-urlencoded"
};
// 2. An NSURL wrapped in an NSURLRequest
NSURL* url = [NSURL URLWithString:webAddress];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 3. An NSURLSession Configuration
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
[sessionConfiguration setHTTPAdditionalHeaders:headers];
// 4. The URLSession itself.
NSURLSession *urlSession = [NSURLSession sessionWithConfiguration:sessionConfiguration];
// 5. A session task: NSURLSessionDataTask or NSURLSessionDownloadTask
NSURLSessionDataTask *dataTask = [urlSession dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
}];
// 5b. Set the delegate if you did not use the completion handler initializer
// urlSession.delegate = self;
// 6. Finally, call resume on your task.
[dataTask resume];
This will run asynchronously, allowing your UI to remain responsive as your app loads data.
When you send a request on the main thread, like you are doing now, your UI, which is always performed on the main thread, is blocked, waiting for the request to finish and process. So you should perform all your network on a background thread, asynchronously. I would recommend first to check the networking library AFNetworking , it could simplify most of your networking problems.
Welcome to SO. You should know that NSURLConnection was deprecated in iOS 9. You should be using NSURLSession instead. The approach is very similar. You can take the NSURLRequest you've created and pass it to the sharedSession object, which is set up for async requests. The simplest way to deal with it is to use the call dataTaskWithRequest:completionHandler:, which takes a completion block. In your completion block you provide code that handles both success and failure.
I have a simple method that takes a url and loads it from the server:
- (void)loadURL:(NSString*)url
{
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] init];
request.HTTPMethod = #"GET";
request.URL = [NSURL URLWithString:url];
NSHTTPURLResponse* response;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
}
The server returns a response with max-age of 1 day.
Problem is that when I run these 3 lines repeatedly, 2 of them randomly miss the cache and reload the response:
[self loadURL:#"http://192.168.0.105:8080/users/51bdbc73808897302f000001/avatar?size=200x200&access_token=abcdefghijklmnopqrstuvwxyzzp77eDLqub3EWfXGe4c09RoyipmXgcENwfE6EV9yzvgp5VTSZww"];
[self loadURL:#"http://192.168.0.105:8080/users/51ee9d4e263d08fe04000003/avatar?size=200x200&access_token=abcdefghijklmnopqrstuvwxyzzp77eDLqub3EWfXGe4c09RoyipmXgcENwfE6EV9yzvgp5VTSZww"];
[self loadURL:#"http://192.168.0.105:8080/users/51d17b81de38c60b20000006/avatar?size=200x200&access_token=abcdefghijklmnopqrstuvwxyzzp77eDLqub3EWfXGe4c09RoyipmXgcENwfE6EV9yzvgp5VTSZww"];
If I add some random unique data (&x, &y, &z) to each request's query string it fixes the problem:
[self loadURL:#"http://192.168.0.105:8080/users/51bdbc73808897302f000001/avatar?size=200x200&access_token=abcdefghijklmnopqrstuvwxyzzp77eDLqub3EWfXGe4c09RoyipmXgcENwfE6EV9yzvgp5VTSZww&x"];
[self loadURL:#"http://192.168.0.105:8080/users/51ee9d4e263d08fe04000003/avatar?size=200x200&access_token=abcdefghijklmnopqrstuvwxyzzp77eDLqub3EWfXGe4c09RoyipmXgcENwfE6EV9yzvgp5VTSZww&y"];
[self loadURL:#"http://192.168.0.105:8080/users/51d17b81de38c60b20000006/avatar?size=200x200&access_token=abcdefghijklmnopqrstuvwxyzzp77eDLqub3EWfXGe4c09RoyipmXgcENwfE6EV9yzvgp5VTSZww&z"];
Also, if I reduce the length of query strings to 80 chars it fixes the problem as well:
[self loadURL:#"http://192.168.0.105:8080/users/51bdbc73808897302f000001/avatar?size=200x200&access_token=abcdefghijklmnopqrstuvwxyzzp77eDLqub3EWfXGe4c09Royipm"];
[self loadURL:#"http://192.168.0.105:8080/users/51ee9d4e263d08fe04000003/avatar?size=200x200&access_token=abcdefghijklmnopqrstuvwxyzzp77eDLqub3EWfXGe4c09Royipm"];
[self loadURL:#"http://192.168.0.105:8080/users/51d17b81de38c60b20000006/avatar?size=200x200&access_token=abcdefghijklmnopqrstuvwxyzzp77eDLqub3EWfXGe4c09Royipm"];
What's going on?
Is this a bug in iOS? How can I fix it?
P.S: I've tested this in an empty application with no extra stuff both on iOS 5 and 6.
Set the caching behaviour in the NSURLRequest with requestWithURL:cachePolicy:timeoutInterval:. Try:
- (void)loadURL:(NSString*)url
{
NSURL *url = [NSURL URLWithString:url];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy: NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0];
NSHTTPURLResponse* response;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
}
Possibly also use NSURLRequestReloadIgnoringLocalAndRemoteCacheData for cachePolicy.
you might want to use ASIHTTPRequest instead
http://allseeing-i.com/ASIHTTPRequest/
i have this method
- (BOOL)connectedToInternet
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:
[NSURL URLWithString:#"http://www.google.com/"]];
[request setHTTPMethod:#"HEAD"];
NSHTTPURLResponse *response;
[NSURLConnection sendSynchronousRequest:request
returningResponse:&response error: NULL];
return ([response statusCode] == 200) ? YES : NO;
}
that method is taking a few seconds to do it, im using it in a simple if conditional to know if i have internet connection.
is there any way to do it in a background thread without having to change all code.
I'm calling it this way
if([self connectedToInternet])
So if i do it in a background thread i cant get the return value and then my method cant return the value.
If i have to change all it doesn't worth it.
I hope u can understand my question and thanks for any help.
In Apple's "Reachability" Code Sample note the reachabilityWithAddress: method please.
You can do something similar to this using blocks;
definition (.h)
+ (void)isConnectedToInternet:(void (^)(BOOL connected))block;
implementation (.m)
+ (void)isConnectedToInternet:(void (^)(BOOL))block
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:
[NSURL URLWithString:#"http://www.google.com/"]];
[request setHTTPMethod:#"HEAD"];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
if (block) {
block( ([httpResponse statusCode] == 200) ? YES : NO);
}
}];
}
then call it like
[MyClass isConnectedToInternet:^(BOOL connected) {
if (connected) {
// do stuff;
}
}];
I don't know what exactly what you want to do, but what you want to use is probably :
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
//your asynchronous code here
});
But by using an if condition, you need the result in order to continue, don't you? So why running the code in background?
I would suggest the method which you are implementing to know 'if Internet is connected or not' is not the most optimized one... few days back I also tried to implement the same thing.. and I came across couple of solutions, over Internet.. and I wrote about it on my blog.. Checking Internet connection in cocoa.
My preferred way to know if network is connected or not is by using Reachability class. You can get clue on using it from this code: NetworkCheckUtility.
Hope this helps :-)