NSTimer? - Check Connection iOS - ios

I am using the following method to check if my app has a connection. It's simple and works great for my needs.
+ (void)checkInternet:(connection)block
{
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)
{
block([(NSHTTPURLResponse *)response statusCode] == 200);
}];
}
However, what I'd like to do is if the status doesn't return 200, I'd like to check again, at least a couple of times. What's the best way to do this with 1 second intervals?
Below is how I'm calling the above method.
[self checkInternet:^(BOOL internet)
{
if (internet)
{
// "Internet" aka Google
}
else
{
// No "Internet" aka no Google
}
}];

I use Reachability for detecting general network connection issues (See end of answer). I use the following method for executing retries.
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
You could adapt your system something like the following to have a new class method which has an optional number of retries.
NB. Not tested the following. It is just to give you the general idea.
// Variable to track number of retries left. If you had a shared instance
// a property would be easier.
static NSUInteger maxConnectionTries = 0;
// New method which lets you pass a retry count.
+ (void)checkInternet:(connection)block withMaxTries:(NSUInteger)maxTries
{
maxConnectionTries=maxTries;
[self checkInternet:block];
}
// Your original code extended to retry by calling itself when code 200
// is seen on a delay of 1s. Defaults to old code when retry limit exceeded
// or non 200 code received.
+ (void)checkInternet:(connection)block
{
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)
{
if ([(NSHTTPURLResponse *)response statusCode] != 200 &&
maxConnectionRetries > 0){
maxConnectionRetries--;
[self performSelector:#selector(checkInternet:) withObject:block afterDelay:1.0];
}
else{
maxConnectionRetries = 0;
block([(NSHTTPURLResponse *)response statusCode] == 200);
}
}];
}
For general detection of internet connectivity, it is best to use Reachability. See here.
I start a reachability handler from my AppDelegate code and then publish local notifications when connectivity changes occur. This allows the application to always receive connection change notification and transient view controllers within viewWillAppear and viewWillDisappear to register and deregister for local notifications if they are interested in connection changes.

FYI here is what I came up with:
+ (void)checkInternet:(connection)block withMaxTries:(NSUInteger)maxTries
{
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)
{
if ([(NSHTTPURLResponse *)response statusCode] != 200 &&
maxTries > 0){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self checkInternet:block withMaxTries:maxTries - 1];
});
}
else{
block([(NSHTTPURLResponse *)response statusCode] == 200);
}
}];
}

Related

iOS: How to test Internet connection in the most easy way, without freezing the app (without Reachability)?

In my code I used to use three ways for checking Internet, but there is limits to them:
1/ Reachability method:
- (BOOL)isInternetOk
{
Reachability *curReach = [Reachability reachabilityWithHostName:#"apple.com"];
NetworkStatus netStatus = [curReach currentReachabilityStatus];
if (netStatus != NotReachable) //if internet connexion ok
{
return YES;
}
else
{
return NO;
}
}
Limit: It works in the most case, but my problem is that if I connect an antenna Wi-Fi without no internet on it, it says that the connection is okay, and it is not true. It is not a good solution, I need to check the status code that it seems not available on Reachability.
2/sendSynchronousRequest:
- (BOOL)isInternetOk2
{
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] init];
NSURL* URL = [NSURL URLWithString:#"https://www.google.com"];
NSError *error = nil;
[request setURL:URL];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[request setTimeoutInterval:15];
NSData* response2 = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
if (error)
{
return NO;
}
else
{
return YES;
}
}
Limit: It works too, but my problem is that if there is a time out, that can happen at every moment, it freezes the app during too much time. If I put it in a thread, it seems that when I do a request in a dispatch_async, the response is not taking in account.
3/sendAsynchronousRequest:
NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"https://www.google.com"]];
request.timeoutInterval = 10;
[NSURLConnection sendAsynchronousRequest:request queue:myQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
NSLog(#"response status code: %ld, error status : %#", (long)[httpResponse statusCode], error.description);
if ((long)[httpResponse statusCode] >= 200 && (long)[httpResponse statusCode]< 400)
{
// do stuff
NSLog(#"Connected!");
}
else
{
NSLog(#"Not connected!");
}
}];
Limit: I think it is the better way to do, but my problem is that I have to write that every where in my code, which will be a polution. I wonder if there is a less heavy way to do it.
What do you think about it? Is there another way easier to check if internet is working without freezing the app?
Thanks in advance.
Nayem is right - you should wrap the third option (async network check) in a class method like this:
+ (void)checkInternetConnectivityWithSuccessCompletion:(void (^)(void))completion {
NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"https://www.google.com"]];
request.timeoutInterval = 10;
[NSURLConnection sendAsynchronousRequest:request queue:myQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
NSLog(#"response status code: %ld, error status : %#", (long)[httpResponse statusCode], error.description);
if ((long)[httpResponse statusCode] >= 200 && (long)[httpResponse statusCode]< 400)
{
// do stuff
NSLog(#"Connected!");
completion();
}
else
{
NSLog(#"Not connected!");
}
}];
}
And then call the method like this:
[YourClass checkInternetConnectivityWithSuccessCompletion:^{
// your internet is working - add code here
}];
Another option with
#import <SystemConfiguration/SCNetworkReachability.h>
.....
+(bool)isNetworkAvailable {
SCNetworkReachabilityFlags flags;
SCNetworkReachabilityRef address;
address = SCNetworkReachabilityCreateWithName(NULL, "www.apple.com");
Boolean success = SCNetworkReachabilityGetFlags(address, &flags);
CFRelease(address);
bool canReach = success
&& !(flags & kSCNetworkReachabilityFlagsConnectionRequired)
&& (flags & kSCNetworkReachabilityFlagsReachable);
return canReach;
}

NSURLConnection send request after finish all process

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.)

internet connection reachability using afnetworking

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);
}];
}

IOS: Check existence of remote file

Is there any resource efficient way (something that does not tie up the main thread) in IOS to check the existence of a remote file?
I have user images stored on a server. While there is a consistent url scheme, some images are .jpg, others are .gif, etc. so to get the correct image name, I need to check does user/file.gif exist, user/file.jpg exist etc. in order to download the file to the IOS app.
I found this code in another answer in IOS
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:#"HEAD"];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
But I am not sure how to use it. Ideally, I would like to get a boolean yes or no as to whether the .gif file exists, the .jpg file exists etc for the users profile pic so I can fill in the correct name to download the user pic.
The alternative would be to write a service on the back end to return the file but wondering if it can all be done in IOS.
Thanks for any suggestions.
**Use this function below to check whether file exists at specified url**
+(void)checkWhetherFileExistsIn:(NSURL *)fileUrl Completion:(void (^)(BOOL success, NSString *fileSize ))completion
{
//MAKING A HEAD REQUEST
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:fileUrl];
request.HTTPMethod = #"HEAD";
request.timeoutInterval = 3;
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue currentQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError)
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
if (connectionError == nil) {
if ((long)[httpResponse statusCode] == 200)
{
//FILE EXISTS
NSDictionary *dic = httpResponse.allHeaderFields;
NSLog(#"Response 1 %#",[dic valueForKey:#"Content-Length"]);
completion(TRUE,[dic valueForKey:#"Content-Length"]);
}
else
{
//FILE DOESNT EXIST
NSLog(#"Response 2");
completion(FALSE,#"");
}
}
else
{
NSLog(#"Response 3");
completion(FALSE,#"");
}
}];
}

NSURLConnection async not working

I'm trying to make an asynchronous NSURL Request, but I'm getting all "FALSE."
-(BOOL)checkConnectionForHost:(NSString*)host{
BOOL __block isOnline = NO;
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:[NSURL URLWithString:host] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:1];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if([(NSHTTPURLResponse*)response statusCode]==200){
isOnline = TRUE;
}
}];
NSLog(#"%i",isOnline);
return isOnline;
}
Also, this code is being called "6" times when I'm actually just using it with a:
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
and there are only 3 cells, or 3 items in my data source. First time dealing with async and callbacks in Objective-C, so a detailed answer would be much appreciated! Thanks!
Asynchronous calls will be executed in parallel, and its result will receive in the completion block. In your case, the return statement will be executed before the completion of the Asynchronous request. That will be always FALSE.
You should use Synchronous request for this, and handle not to Block the UI.
-(BOOL)checkConnectionForHost:(NSString*)host{
BOOL isOnline = NO;
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:[NSURL URLWithString:host] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:1];
NSHTTPURLResponse *response;
NSError *error;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"Response status Code : %d",response.statusCode);
isOnline = response.statusCode == 200;
return isOnline;
}
You can use that method inside dispatch queues,
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
dispatch_async(queue, ^{
BOOL status = [self checkConnectionForHost:#"http://google.com"];
NSLog(#"Host status : %#",status ? #"Online" : #"Offline");
});
You should realize that this problem is inherently asynchronous. You can't solve it with a synchronous approach. That is, your accepted solution is just an elaborated and suboptimal wrapper which ends up being eventually asynchronous anyway.
The better approach is to use an asynchronous method with a completion handler, e.g.:
typedef void (^completion_t)(BOOL isReachable);
-(void)checkConnectionForHost:(NSString*)host completion:(completion_t)completionHandler;
You can implement is as follows (even though the request isn't optimal for checking reachability):
-(void)checkConnectionForHost:(NSString*)host
completion:(completion_t)completionHandler
{
NSURLRequest* request = [[NSURLRequest alloc]initWithURL:[NSURL URLWithString:host]];
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (completionHandler) {
completionHandler(connectionError == nil && [(NSHTTPURLResponse*)response statusCode]==200);
}
}];
}
Please note:
Don't set a timeout as short as in your original code.
The completion handler will be called on a private thread.
Usage:
[self checkConnectionForHost:self.host completion:^(BOOL isReachable){
dispatch_async(dispatch_get_main_queue(), ^{
self.reachableLabel.text = isReachable ? #"" : #"Service unavailable";
});
}];
Your isOnline is probably being set to YES, but it's happening asynchronously. It is almost certainly executing after you log out the value of isOnline. So you should move your NSLog() call up into the block you pass as the handler to the asynchronous URL request.

Resources