I tested different frameworks, e.g.
https://github.com/tonymillion/Reachability
https://github.com/VerticodeLabs/VCLReachability
https://github.com/kstenerud/KSReachability
and I would like to know if a host is reachable. On my iPhone, I set my iMac as proxy (Charles) and block or don't block the connections, but the reachability is always YES. Only if I set a non-existing host, it returns NO. But if the host exists but I block the connection to it, I always get isReachable. Isn't there a way to check if the host is really reachable?
If I try with KSReachability, I'm doing the following:
self.reachability = [KSReachability reachabilityToHost:#"www.stackoverflow.com"];
self.reachability.notificationName = kDefaultNetworkReachabilityChangedNotification;
self.reachability.onReachabilityChanged = ^(KSReachability *reachability) {
NSLog(#"isReachable: %i", reachability.reachable);
};
I always get isReachable: 1 there with the following configuration:
connected to Wifi
configured my iMac as HTTP-Proxy
blocking www.stackoverflow.com in my Charles Proxy
When I try to reach www.stackoverflow.com in Safari, the page can't be opened (as expected). I would expect the reachability to be false (isReachable: 0) in this case.
EDIT
So the most important question for me is - how to achieve the behavior I'm expecting? I.e. that the app continuously checks if the given host is really reachable?
The code statement:
self.reachability = [KSReachability reachabilityToHost:#"www.stackoverflow.com"];
actually calls below method:
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL,
[hostName UTF8String]);
SCNetworkReachability reference says:
The SCNetworkReachability programming interface allows an application
to determine the status of a system's current network configuration
and the reachability of a target host. A remote host is considered
reachable when a data packet, sent by an application into the network
stack, can leave the local device. Reachability does not guarantee
that the data packet will actually be received by the host.
The explanation clears that iOS system doesn't send any request to outside world to check the reachability. It just tells that data packet can leave the device or not. If system were to send the request it automatically means that it is connected to network.
You can verify this by mentioning a valid host like "www.stackoverflow.com" and check in charles (first unblock it) that no request is sent.You can also check with other valid host names like "www.abcdefgh.com" (verify this by running it in Safari and see in charles) it also gives you the reachability but charles shows no request.Also if you put http:// before any valid host, something like "http://www.stackoverflow.com" it will also fails reachability. So it is clear that it is not an outgoing http request. If system has to send a request outside then what's the point of providing the class? A developer could have created a network connection and try to connect to a host and see if it passes or fails.
However it is interesting that if an invalid host like "www.hjjkhkhk.com" is provided iOS system gives reachability as false. Now the question is how iOS system finds a valid or invalid host without sending any query to outside world? May be it is periodically caching a list of DNS ranges??? Highly improbable to me.
In your AppDelegate add this method:
#import "Reachability.h"
-(NSString *)checkNetworkConnectivity
{
NSString *networkValue;
Reachability *rc = [Reachability reachabilityWithHostName:#"www.stackoverflow.com"];
NetworkStatus internetStatus = [rc currentReachabilityStatus];
if(internetStatus==0)
{
networkValue = #"NoAccess";
}
else if(internetStatus==1)
{
networkValue = #"ReachableViaWiFi";
} else if(internetStatus==2)
{
networkValue = #"ReachableViaWWAN";
}
else
{
networkValue = #"Reachable";
}
return networkValue;
}
Checking if the host is reachable
NSString *netStr = [appDelegate checkNetworkConnectivity];
if([netStr isEqualToString:#"NoAccess"])
{
[appDelegate callNoNetworkAlert];
}
Firstly, unless we see some code, we cannot make sure what you're doing, you're doing the right way. However I will assume you are doing it correctly.
Secondly, you should test what FreeNickname has suggested in his comment. Maybe it's not actually unreachable, and the reachability is acting correctly, when you expect a different response.
Last, but very important, from the Reachability docs :
Note: Reachability cannot tell your application if you can connect to a particular host, only that an interface is available that might allow a connection, and whether that interface is the WWAN.
What it means is that, even though your server might not be returning any responses, Reachability does NOT check that. It only checks if your server is available , such that a packet can be sent. What it does with that packet is of no concern to Reachability. If Reachability is able to transmit the entire packet, it assumes your host is reachable. It will return unreachable iff your server is down, disconnected, or does not exist.
I would like to add that To listen the Network changes at Runtime, you need to listen to the Notifications that tell you that a Network state has been changed.
This is how you can do this :
Implement a listener in AppDelegate file.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reachabilityChanged:)
name:kReachabilityChangedNotification object:nil];
Now reachabilityChanged will be called when there is a change in Network status (perhaps connectivity of internet connection or it's disconnection). You can do appropriate handling in reachabilityChanged event
like:
- (void) reachabilityChanged:(NSNotification *)note
{
Reachability* reachability = [note object];
if (reachability == self.hostReachability)
{
isHostReachable = YES; //A flag to keep track of connectivity
}
if (reachability == self.internetReachability)
{
isInternetAvailable = YES;
}
if (reachability == self.wifiReachability)
{
isWifiAvailable = YES;
}
//If all are true that means we have host and Internet available
if (isHostReachable && isInternetAvailable && isWifiAvailable)
{
isInternetAvailable = true;
}
}
Hope this will help you.
AFNetworking
A delightful iOS and OS X networking framework
http://afnetworking.com
Network Reachability Manager
AFNetworkReachabilityManager monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces.
Shared Network Reachability
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status)
{
NSLog(#"Reachability: %#", AFStringFromNetworkReachabilityStatus(status));
}];
HTTP Manager Reachability
NSURL *baseURL = [NSURL URLWithString:#"http://example.com/"];
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL];
NSOperationQueue *operationQueue = manager.operationQueue;
[manager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status)
{
switch (status)
{
case AFNetworkReachabilityStatusReachableViaWWAN:
case AFNetworkReachabilityStatusReachableViaWiFi:
[operationQueue setSuspended:NO];
break;
case AFNetworkReachabilityStatusNotReachable:
default:
[operationQueue setSuspended:YES];
break;
}
}];
Related
I use Starscream. How can I check the socket is connecting or not?
isConnecting is a private property.
You should use socket.isConnected:
if socket.isConnected {
//socket is connected
}
In case if you want to check your socket is still alive (In case if you feel that it might be dead for example you are not receiving a packets stream you are suppose to get) then you can send ping, it will result in triggering websocketDidDisconnect method in case socket is gone. and Yes, You are right isConnected says that it was connected in the past so you can not rely on it.
func sendPing() {
guard let socket = socket, socket.isConnected
else { return }
socket.write(ping: "PING")
}
I just want to know how to detect internet connectivity in iOS when device is connected to wifi but no intenet connection in modem.
I have used apple's and AFNetworking's reachability but they only check for connectivity and returns connected flag even there is no working internet in modem.
I found the solution by hitting host via Reachability:-
Might be helpful for someone.
-(BOOL)isCheckConnection {
Reachability *rc = [Reachability reachabilityWithHostName:#"www.google.com"];
NetworkStatus internetStatus = [rc currentReachabilityStatus];
if(internetStatus==0)
{
//#"NoAccess";
return NO;
}
else if(internetStatus==1)
{
//#"ReachableViaWiFi";
return YES;
} else if(internetStatus==2)
{
//#"ReachableViaWWAN";
return YES;
}
else
{
//#"Reachable";
return YES;
}
}
In order to be sure that your device is really connected to internet, at least you have to try a ping to a up&running at anytime server (like stackoverflow.com o google.com).
Of course, as suggested by #jonrsharpe in question's comments, if this check is necessary in order to understand if app can reach the web server, then a ping or something like that to your web server is also necessary.
But a ping to a surely working web server (like google) will give you the answer it your device is connected to internet (so your modem can reach internet), that way if your web server is not responding you can accordingly show a warning alert in your app to inform user that currently your server is unreachable.
Going to code, you can try to check connection status with Reachability library like below
Reachability *reachability = [Reachability reachabilityWithHostName:#"stackoverflow.com"];
NetworkStatus networkStatus = [reachability currentReachabilityStatus];
and then check networkStatus variable: if 0 you don't have access to internet, otherwise YES, so
if(networkStatus==0) {
// no access to internet
} else {
// you have access to internet
}
I'm using the Reachability class provided here by Apple in my iOS project. I call its currentReachabilityStatus method always before trying to call my own REST web services:
- (NetworkStatus)currentReachabilityStatus
{
NSAssert(_reachabilityRef != NULL, #"currentNetworkStatus called with NULL SCNetworkReachabilityRef");
NetworkStatus returnValue = NotReachable;
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))
{
if (_alwaysReturnLocalWiFiStatus)
{
returnValue = [self localWiFiStatusForFlags:flags];
}
else
{
returnValue = [self networkStatusForFlags:flags];
}
}
return returnValue;
}
In turn, I call this method from a custom class I made for convenience:
+ (BOOL)checkNetStatus
{
Reachability *reach = [Reachability reachabilityForInternetConnection];
NetworkStatus status = [reach currentReachabilityStatus];
return [self boolFromStatus:status];
}
I'm performing some tests in an iPhone, enabling the flight mode in its Settings, and then when the app is back to foreground, that method is called several times (my app retries to call the web services if no reachability until they become reachable) and finally I get an EXC_BAD_ACCESS exception at line if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)), and the app crashes.
I don't understand exactly why, because currentReachabilityStatus is called several times before I get the exception and the crash, could it be because it is being called a lot of times and too fast? How could I solve this?
I need help, thanks in advance.
EDIT: whenever I'm going to call of my RESTful services, I do something like this:
- (void)callWebService
{
if ([MyReachabilityManager checkNetStatus]) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
MyServiceWrapper *requestService = [[MyServiceWrapper alloc] initWithServiceUrl];
[requestService queryService];
}
else {
[self keepRequestingMyService]; // this calls this method again until a timeout
}
}
Something similar happened to me, one of my application crashed while switching to Airplane mode and to Wifi frequently (Happened if the Wi-Fi is having low signal strength).
In my case the crash is caused by the NSAssert, the _reachabilityRef was null, so the assert condition was failing:
NSAssert(_reachabilityRef != NULL, #"currentNetworkStatus called with NULL SCNetworkReachabilityRef");
I just removed that and re-wrote that method by adding a extra null check for _reachabilityRef
I had the same: app SOMETIMES crashes when releasing reachabilityRef. Seems like it should be NULL but it is not?
After experimenting i found if I added something in Reachability.h interface (originally it had nothing):
#interface Reachability : NSObject{
BOOL dummy;
}
it stopped crashing, could you try that and report if it helps?
I use XCode 7.1.1
How to check for network synchronously? I want the network checking to block the calling thread and return the correct result
I tried tonymillion's Reachability and AFNetworkReachabilityManager but they all use callback block. It means the reachability status is unknown before the callback.
I want to check network at applicationDidFinishLaunchingWithOptions: but at this point, reachability is AFNetworkReachabilityStatusUnknown (AFNetworkReachabilityManager) or not reliable (tonymillion's Reachability)
I see that the only way is to perform NSURLConnection against some host (google.com for example) like this Check for internet connection - iOS SDK
Are there any better way?
To answer my own question: It is reliable
Read this Technical Q&A QA1693 Synchronous Networking On The Main Thread
reachability — The System Configuration framework reachability API
() operates synchronously
by default. Thus, seemingly innocuous routines like
SCNetworkReachabilityGetFlags can get you killed by the watchdog. If
you're using the reachability API, you should use it asynchronously.
This involves using the SCNetworkReachabilityScheduleWithRunLoop
routine to schedule your reachability queries on the run loop
So we can use it like this iOS: Check whether internet connection is available
- (BOOL) isConnectionAvailable
{
SCNetworkReachabilityFlags flags;
BOOL receivedFlags;
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(CFAllocatorGetDefault(), [#"dipinkrishna.com" UTF8String]);
receivedFlags = SCNetworkReachabilityGetFlags(reachability, &flags);
CFRelease(reachability);
if (!receivedFlags || (flags == 0) )
{
return FALSE;
} else {
return TRUE;
}
}
I'm developing an iOS app which needs to know whether the cellular network is metered to save the cost, do we have an api in iOS to get the information?
In android the api is http://developer.android.com/reference/android/net/ConnectivityManager.html#isActiveNetworkMetered().
Returns if the currently active data network is metered. A network is classified as metered when the user is sensitive to heavy data usage on that connection due to monetary costs, data limitations or battery/performance issues. You should check this before doing large data transfers, and warn the user or delay the operation until another network is available.
Thanks,
James
There is no check exactly to check like network is metered or not but you can check whether the network connection is via cellular or wifi.
You can use the Reachability class provide by apple here
Then implement the following to find whether you have connected on cellular
- (BOOL)networkCheckForCellular
{
Reachability *wifiReach = [Reachability reachabilityForInternetConnection];
NetworkStatus netStatus = [wifiReach currentReachabilityStatus];
switch (netStatus)
{
case NotReachable:
{
NSLog(#"%#",#"NETWORKCHECK: Not Connected");
return false;
break;
}
case ReachableViaWiFi:
{
NSLog(#"%#",#"NETWORKCHECK: Connected Via WiFi");
return false;
break;
}
case ReachableViaWWAN:
{
NSLog(#"%#",#"NETWORKCHECK: Connected Via WWAN");
return true;
break;
}
}
return false;
}