AFNetworkReachabilityManager status is unknown - ios

I started using AFNetworking's reachability manager instead of Apple's Reachability class but it doesn't work. Its reachability status is AFNetworkReachabilityStatusUnknown despite the internet connection is turned on on the device. This is my code:
reachabilityManager = [AFNetworkReachabilityManager sharedManager];
[reachabilityManager startMonitoring];
[reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status){
NSLog(#"Current status %#", reachabilityManager.localizedNetworkReachabilityStatusString);
}];
I put NSLog inside the block but i don't mean to say that i see results there. Actually the block is never called (while my device has normal internet connection). I just check the status before performing my first network request and i see it is -1 i.e. unknown. What's wrong?
This is how i check reachability status:
- (BOOL)connected
{
#if TARGET_IPHONE_SIMULATOR
return YES;
#else
NSLog(#"Status %d", reachabilityManager.networkReachabilityStatus);
return reachabilityManager.reachable;
#endif
}

Related

Why Reachability Manager can not detect internet?

I am trying to learn AFNetworking. when I run following code I get there is no internet. But in another check it says it is connected to the internet and device is connected to internet through wifi. This the output:
2016-02-19 15:16:40.315 AFNetworkingSample[377:47927] There is no internet connection
2016-02-19 15:16:40.331 AFNetworkingSample[377:47927] Reachability: Reachable via WiFi
Any idea why return value of connected method is false?
- (void)viewDidLoad {
[super viewDidLoad];
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
if (self.connected)
{
NSLog(#"We have internet connection");
}
else
{
NSLog(#"There is no internet connection");
}
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
NSLog(#"Reachability: %#", AFStringFromNetworkReachabilityStatus(status));
}];
}
-(BOOL)connected {
return [AFNetworkReachabilityManager sharedManager].reachable;
}
UPDATE
When I change it to following it works fine and detect internet properly. Anyone can explain this behaviour?
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
NSLog(#"Reachability: %#", AFStringFromNetworkReachabilityStatus(status));
if (self.connected)
{
NSLog(#"We have internet connection");
}
else
{
NSLog(#"There is no internet connection");
}
}];
UPDATE2
When I wait for two second it detects internet properly:
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if (self.connected)
{
NSLog(#"We have internet connection");
}
else
{
NSLog(#"There is no internet connection");
}
});
UPDATE3:
Is this how to practice to avoid race condition?
- (void)viewDidLoad {
[super viewDidLoad];
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
NSLog(#"Reachability: %#", AFStringFromNetworkReachabilityStatus(status));
if([AFNetworkReachabilityManager sharedManager].reachable)
{
NSLog(#"Internet connection started again");
}
else
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"Alert"
message:#"Error Retrieving Data"
preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *firstAction = [UIAlertAction actionWithTitle:#"OK"
style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
NSLog(#"You pressed button OK");
}];
[alert addAction:firstAction];
[self presentViewController:alert animated:YES completion:nil];
}
}];
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
});
}
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
The above is, effectively, an asynchronous call. You've told AFNetwork that you're interested in reachability. As such, it is going to go off and do whatever it needs to do to determine and monitor reachability in the background.
Since it is not a one shot asynchronous call, having a completion block on that method doesn't really fit the pattern of a normal completion handler. It could have been a "status handler" that is called whenever a state change happens, but the API is also designed to be used for passive polling (which some of your other code patterns, like the timer one, inadvertently do -- and, as you noted, not waiting long enough gives you the wrong answer, as expected).
By calling setReachabilityStatusChangeBlock:, you are registering a bit of code to be executed when the reachability status changes. Use that to trigger your "hey! reachability changed!" code, and all is well.
The only remaining issue is whether there is a race condition. You should probably set the block before calling startMonitoring. And, to be safe, you'll likely want to check to see if the network is connected immediately after you call startMonitoring as, technically, if the network was connected, then there may not be a state change that triggers a call to your state changed handler block.
You don't want to block the thread for 2 seconds to try and avoid the race condition. Do something like this:
... set up change handler block ...
... start monitoring ...
... check current internet state & alert user if necessary ...
Move your alert out of the change handler block and into a method that both the change handler block and the "check current internet state & alert user if necessary" mention above can call.
use Reachability like this.....
- (void)viewDidLoad {
if (self.isConnected)
{
NSLog(#"We have internet connection");
}
else
{
NSLog(#"There is no internet connection");
}
}
-(BOOL)isConnected
{
Reachability *aReachability = [Reachability reachabilityWithHostName:KWebserviceURL];
NetworkStatus netStatus = [aReachability currentReachabilityStatus];
if(netStatus==0)
{
// NoAccess
return NO;
}
else if(netStatus==1)
{
// ReachableViaWiFi
return YES;
} else if(netStatus==2)
{
// ReachableViaWWAN
return YES;
}
else
{
// Reachable
return YES;
}
}
if you want to make global class ror check internet connection then make NSObject see in my another ans....Check here
Hope it Helps you...

How to test the availability of URL or NOT?

I'm not found simple solution for this question on SO.
I need simple check logic for my URL and return YES - if available or NO - if not.
What i found:
-(BOOL)connectedToNetwork {
NSURL* url = [[NSURL alloc] initWithString:#"http://google.com/"];
NSData* data = [NSData dataWithContentsOfURL:url];
if (data != nil)
return YES;
return NO;
}
founded this
But i need more correctly answer.
UPD
I found SimplePing by Apple.
UPD1
Found answer on SO Thx all!
UPD2
Better solution SimplePingHelper required SimplePing:
- (void)tapPing {
[SimplePingHelper ping:#"www.google.com"
target:self sel:#selector(pingResult:)];
}
- (void)pingResult:(NSNumber*)success {
if (success.boolValue) {
[self log:#"SUCCESS"];
} else {
[self log:#"FAILURE"];
}
}
The preferred approach is trying to connect, and if that fails due to the host is not reachable check and monitor the network state. See Reachability. If the network state indicates that a host can be possibly reached, try again.
There are also a couple of third party libs that implement a handy API over Apple's Reachability interface.
I use AFNetworking to check reachability.
Pod file: pod "AFNetworking"
You have to include AFNetworkReachabilityManager.h in the file you want to use it.
This is a sample, basic code to check reachability.
// -- Start monitoring network reachability (globally available) -- //
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
NSLog(#"Reachability changed: %#", AFStringFromNetworkReachabilityStatus(status));
switch (status) {
case AFNetworkReachabilityStatusReachableViaWWAN:
case AFNetworkReachabilityStatusReachableViaWiFi:
// -- Reachable -- //
NSLog(#"Reachable");
break;
case AFNetworkReachabilityStatusNotReachable:
default:
// -- Not reachable -- //
NSLog(#"Not Reachable");
break;
}
}];
A valid URL is made of these characters :
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]#!$&'()*+,;=
You simply need to make a String checking function to validate your URL's characters are falling in or out.
Its was simple:
-(BOOL)connectedToNetwork {
NSURL* url = [[NSURL alloc] initWithString:#"http://google.com/"];
NSData* data = [NSData dataWithContentsOfURL:url];
if (data != nil)
return YES;
return NO;
}
founded this

Why is this reachable at all?

I have this code to start monitoring and when I test with this made-up domain, I'm still getting a status of reachableViaWWAN. Why?
Using AFNetworking version 2.5.0 from cocoapods. I also tried downloading the latest from github and got the same thing.
+ (void)startMonitoring {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
reachabilityManager = [AFNetworkReachabilityManager managerForDomain:#"thisIsNonsense.testSDK.irg"];
});
[reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status){
switch (status) {
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(#"reachable viaWWAN");
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(#"reachable viaWiFi");
break;
case AFNetworkReachabilityStatusNotReachable:
NSLog(#"NOT reachable");
break;
default:
NSLog(#"unknown status");
break;
}
}];
[reachabilityManager startMonitoring];
}
If I'm understanding how AFNetworkReachabilityManager is checking, it looks like it's just using SCNetworkReachability which looks like it claims
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.
docs

Apple's weather app fetches as soon as an Internet connection is made after being disconnected. Is there a better way to do this besides a while loop?

Psuedocode of my current solution:
if (disconnected):
while (disconnected):
check for connection
if (connected):
fetch results
Is there a more idiomatic way to tell when the device goes from being disconnected to establishing an internet connection?
Take a look at Apple's Reachability sample code
You don't have to investigate detailed implementation of Reachability.m. You can just use it as a library.
Absolutely! This is what SCNetworkReachability is for!
Unfortunately, people tend to use it incorrectly. What you are describing would be a correct use case for it.
Attempt a connection normally.
If it fails with NSURLErrorNotConnectedToInternet:
Use SCNetworkReachability to monitor the device network configuration for changes
When the network configuration moves to a state that indicates packets can again leave the device, your application will be notified and you can make connections again.
What you should NOT do is try to use SCNetworkReachability to see if the device is connected before connecting. This is not recommended for many reasons. The SCNetworkReachability API can tell you when it is, or is not, possible for packets to leave the device. It can't tell you if the thing you're connecting to is down, DNS isn't working, etc.
The Apple sample projects MVCNetworking and Reachability demonstrate use of the SCNetworkReachability API.
In practice, this looks like....
In your connection error handling:
if ([[error domain] isEqualToString:NSURLErrorDomain]){
NSURL *failingURL = [[error userInfo] valueForKey:NSURLErrorFailingURLErrorKey];
switch ([error code]){
case NSURLErrorNotConnectedToInternet:
[self beginObservingReachabilityStatusForHost:[failingURL host]];
break;
default:
break;
}
}
The beginObservingReachabilityStatusForHost: and endObsvervingReachabilityStatusForHost: methods:
- (void) beginObservingReachabilityStatusForHost:(NSString *)host {
SCNetworkReachabilityRef reachabilityRef = NULL;
void (^callbackBlock)(SCNetworkReachabilityFlags) = ^(SCNetworkReachabilityFlags flags) {
BOOL reachable = (flags & kSCNetworkReachabilityFlagsReachable) != 0;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self host:host didBecomeReachable:reachable];
}];
};
SCNetworkReachabilityContext context = {
.version = 0,
.info = (void *)CFBridgingRetain(callbackBlock),
.release = CFRelease
};
if ([host length] > 0){
reachabilityRef = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [host UTF8String]);
if (SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context)){
if (!SCNetworkReachabilitySetDispatchQueue(reachabilityRef, [self scNetworkQueue]) ){
// Remove our callback if we can't use the queue
SCNetworkReachabilitySetCallback(reachabilityRef, NULL, NULL);
}
[self setCurrentReachability:reachabilityRef];
}
}
}
- (void) endObsvervingReachabilityStatusForHost:(NSString *)host {
// Un-set the dispatch queue
if (!SCNetworkReachabilitySetDispatchQueue([self currentReachability], NULL) ){
}
SCNetworkReachabilitySetCallback([self currentReachability], NULL, NULL);
}
The C callback that wraps our block:
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkConnectionFlags flags, void* info) {
void (^callbackBlock)(SCNetworkReachabilityFlags) = (__bridge id)info;
callbackBlock(flags);
}
And the method that the block calls to do something when reachability changes:
- (void) host:(NSString *)host didBecomeReachable:(BOOL)reachable {
// Update your UI to indicate reachability status here
if (reachable){
[self endObsvervingReachabilityStatusForHost:nil];
}
}
Obviously to use the above you need a serial dispatch queue and a way to hold on to the SCNetworkReachabilityRef for later cancellation when you're done.

Setting up reachability with AFNetworking 2.0

I am trying to setup Reachability using the new 2.0 AFNetworking.
In my AppDelegate I initialise the sharedManager.
// Instantiate Shared Manager
[AFNetworkReachabilityManager sharedManager];
Then in the relevant VC method I check to see if isReachable:
// Double check with logging
if ([[AFNetworkReachabilityManager sharedManager] isReachable]) {
NSLog(#"IS REACHABILE");
} else {
NSLog(#"NOT REACHABLE");
}
At present this is not working as expected in the simulator, but I imagine this would need to be tested on device and not simulator.
Question
What I would like to do is monitor the connectivity within the VC. So I run the following in the viewDidLoad:
// Start monitoring the internet connection
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
How would I then register for the changes? What is/would be called once the network connection changes I cannot see this from the documentation.
As you can read in the AFNetworking read me page
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
NSLog(#"Reachability: %#", AFStringFromNetworkReachabilityStatus(status));
}];
Here's also a link to the official documentation.
I have a singleton AFHTTPRequestOperationManager class. In the singleton has a method:
+(void)connectedCompletionBlock:(void(^)(BOOL connected))block {
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
BOOL con = NO;
NSLog(#"Reachability: %#", AFStringFromNetworkReachabilityStatus(status));
if (status == AFNetworkReachabilityStatusReachableViaWWAN || status == AFNetworkReachabilityStatusReachableViaWiFi) {
con = YES;
}
if (block) {
[[AFNetworkReachabilityManager sharedManager] stopMonitoring];
block(con);
}
}];
}
Before make a request you call this method that return a block indicating if internet is reachable:
[TLPRequestManager connectedCompletionBlock:^(BOOL connected) {
if (connected) {
// Make a request
}else{
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:#"Notice" message:#"Internet is not available." delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alertView show];
}
}];
I was just going through your question and all the answers. After that I decided to do all these things once. So, in my existing project I just included the AFNetworking through cocoa-pods and here is the solution which is woking for me completely.
Solution -- First of all AFNetworkReachabilityManager is a singleton class. You don't need to do AppDelegate initialisation for sharedManager.
//[AFNetworkReachabilityManager sharedManager];
#import <AFNetworkReachabilityManager.h>
- (void)viewDidLoad {
//Starting the network monitoring process
[[AFNetworkReachabilityManager sharedManager]startMonitoring];
//Checking the Internet connection...
[[AFNetworkReachabilityManager sharedManager]setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status){
if (status == AFNetworkReachabilityStatusReachableViaWWAN || status == AFNetworkReachabilityStatusReachableViaWiFi) {
UIAlertView *alertNetFound = [[UIAlertView alloc]initWithTitle:#"Network Found" message:#"Please Wait Until It is loading" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertNetFound show];
}else{
UIAlertView *alertNetNotFound = [[UIAlertView alloc]initWithTitle:#"No Internet" message:#"Please Check Your Internet Connection Honey" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertNetNotFound show];
}
}];
So, in this case every time the device connects to a network, it will do the startMonitoring process first and after that it will hit the status block every time and will display alert according to the status.
You can do anything according to your choice by replacing the alerts on the status block. I used this to load an webpage automatically from local storage but I removed that code for simplicity.
Its even working with my simulator and Mac mini..
Thanks
Hope this helped.
I use this in the app delegate ->
func reachablityCode() {
AFNetworkReachabilityManager.sharedManager()
AFNetworkReachabilityManager.sharedManager().startMonitoring()
AFNetworkReachabilityManager.sharedManager().setReachabilityStatusChangeBlock({(status) in
let defaults = NSUserDefaults.standardUserDefaults()
if status == .NotReachable {
defaults.setBool(false, forKey:REACHABLE_KEY)
}
else {
defaults.setBool(false, forKey: REACHABLE_KEY)
}
defaults.synchronize()
})
}
And then this in the base file ->
func isReachable() -> Bool {
return NSUserDefaults.standardUserDefaults().boolForKey(REACHABLE_KEY)
}

Resources