Search code examples
iosobjective-cnetwork-programmingreachability

Reachability unreachableBlock not getting called when there is no internet connection


This is my code,

Reachability *internetReachable = [Reachability reachabilityWithHostname:@"www.google.com"];


internetReachable.reachableBlock = ^(Reachability*reach)`
{

    NSLog(@"REACHABLE!");



};

internetReachable.unreachableBlock = ^(Reachability*reach){

    NSLog(@"UNREACHABLE!");


};
[internetReachable startNotifier];

Unreachable block is not getting called, when wifi is turned off. Any help?


Solution

  • I will give you the solution detaily.

    First I added Reachability Classes.

    Reachability.h

    #import <Foundation/Foundation.h>
    #import <SystemConfiguration/SystemConfiguration.h>
    #import <netinet/in.h>
    
    typedef enum {
        NotReachable = 0,
        ReachableViaWiFi,
        ReachableViaWWAN
    } NetworkStatus;
    #define kReachabilityChangedNotification @"kNetworkReachabilityChangedNotification"
    
    @interface Reachability: NSObject
    {
        BOOL localWiFiRef;
        SCNetworkReachabilityRef reachabilityRef;
    }
    
    //reachabilityWithHostName- Use to check the reachability of a particular host name. 
    + (Reachability*) reachabilityWithHostName: (NSString*) hostName;
    
    //reachabilityWithAddress- Use to check the reachability of a particular IP address. 
    + (Reachability*) reachabilityWithAddress: (const struct sockaddr_in*) hostAddress;
    
    //reachabilityForInternetConnection- checks whether the default route is available.  
    //  Should be used by applications that do not connect to a particular host
    + (Reachability*) reachabilityForInternetConnection;
    
    //reachabilityForLocalWiFi- checks whether a local wifi connection is available.
    + (Reachability*) reachabilityForLocalWiFi;
    
    //Start listening for reachability notifications on the current run loop
    - (BOOL) startNotifier;
    - (void) stopNotifier;
    
    - (NetworkStatus) currentReachabilityStatus;
    //WWAN may be available, but not active until a connection has been established.
    //WiFi may require a connection for VPN on Demand.
    - (BOOL) connectionRequired;
    @end
    

    Reachability.m

    #import <sys/socket.h>
    #import <netinet/in.h>
    #import <netinet6/in6.h>
    #import <arpa/inet.h>
    #import <ifaddrs.h>
    #import <netdb.h>
    
    #import <CoreFoundation/CoreFoundation.h>
    
    #import "Reachability.h"
    
    #define kShouldPrintReachabilityFlags 1
    
    static void PrintReachabilityFlags(SCNetworkReachabilityFlags    flags, const char* comment)
    {
    #if kShouldPrintReachabilityFlags
    
        NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n",
              (flags & kSCNetworkReachabilityFlagsIsWWAN)                 ? 'W' : '-',
              (flags & kSCNetworkReachabilityFlagsReachable)            ? 'R' : '-',
    
              (flags & kSCNetworkReachabilityFlagsTransientConnection)  ? 't' : '-',
              (flags & kSCNetworkReachabilityFlagsConnectionRequired)   ? 'c' : '-',
              (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)  ? 'C' : '-',
              (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
              (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)   ? 'D' : '-',
              (flags & kSCNetworkReachabilityFlagsIsLocalAddress)       ? 'l' : '-',
              (flags & kSCNetworkReachabilityFlagsIsDirect)             ? 'd' : '-',
              comment
              );
    #endif
    }
    
    
    @implementation Reachability
    static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
    {
    #pragma unused (target, flags)
        NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback");
    //  NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback");
        NSCAssert([(__bridge NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback");
        //We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively
        // in case someon uses the Reachablity object in a different thread.
    //    NSAutoreleasePool* myPool = [[NSAutoreleasePool alloc] init];
    
        Reachability* noteObject = (__bridge Reachability*) info;
        // Post a notification to notify the client that the network reachability changed.
        [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject];
    
    //    [myPool release];
    }
    
    - (BOOL) startNotifier
    {
        BOOL retVal = NO;
        SCNetworkReachabilityContext    context = {0, (__bridge void * _Nullable)(self), NULL, NULL, NULL};
        if(SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context))
        {
            if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode))
            {
                retVal = YES;
            }
        }
        return retVal;
    }
    
    - (void) stopNotifier
    {
        if(reachabilityRef!= NULL)
        {
            SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
        }
    }
    
    - (void) dealloc
    {
        [self stopNotifier];
        if(reachabilityRef!= NULL)
        {
            CFRelease(reachabilityRef);
        }
    //    [super dealloc];
    }
    
    + (Reachability*) reachabilityWithHostName: (NSString*) hostName;
    {
        Reachability* retVal = NULL;
        SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
        if(reachability!= NULL)
        {
            retVal= [[self alloc] init];
            if(retVal!= NULL)
            {
                retVal->reachabilityRef = reachability;
                retVal->localWiFiRef = NO;
            }
        }
        return retVal;
    }
    
    + (Reachability*) reachabilityWithAddress: (const struct sockaddr_in*) hostAddress;
    {
        SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);
        Reachability* retVal = NULL;
        if(reachability!= NULL)
        {
            retVal= [[self alloc] init];
            if(retVal!= NULL)
            {
                retVal->reachabilityRef = reachability;
                retVal->localWiFiRef = NO;
            }
        }
        return retVal;
    }
    
    + (Reachability*) reachabilityForInternetConnection;
    {
        struct sockaddr_in zeroAddress;
        bzero(&zeroAddress, sizeof(zeroAddress));
        zeroAddress.sin_len = sizeof(zeroAddress);
        zeroAddress.sin_family = AF_INET;
        return [self reachabilityWithAddress: &zeroAddress];
    }
    
    + (Reachability*) reachabilityForLocalWiFi;
    {
        struct sockaddr_in localWifiAddress;
        bzero(&localWifiAddress, sizeof(localWifiAddress));
        localWifiAddress.sin_len = sizeof(localWifiAddress);
        localWifiAddress.sin_family = AF_INET;
        // IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
        localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
        Reachability* retVal = [self reachabilityWithAddress: &localWifiAddress];
        if(retVal!= NULL)
        {
            retVal->localWiFiRef = YES;
        }
        return retVal;
    }
    
    #pragma mark Network Flag Handling
    
    - (NetworkStatus) localWiFiStatusForFlags: (SCNetworkReachabilityFlags) flags
    {
        PrintReachabilityFlags(flags, "localWiFiStatusForFlags");
    
        BOOL retVal = NotReachable;
        if((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect))
        {
            retVal = ReachableViaWiFi;
        }
        return retVal;
    }
    
    - (NetworkStatus) networkStatusForFlags: (SCNetworkReachabilityFlags) flags
    {
        PrintReachabilityFlags(flags, "networkStatusForFlags");
        if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
        {
            // if target host is not reachable
            return NotReachable;
        }
    
        BOOL retVal = NotReachable;
    
        if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
        {
            // if target host is reachable and no connection is required
            //  then we'll assume (for now) that your on Wi-Fi
            retVal = ReachableViaWiFi;
        }
    
    
        if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
             (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))
        {
            // ... and the connection is on-demand (or on-traffic) if the
            //     calling application is using the CFSocketStream or higher APIs
    
            if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0)
            {
                // ... and no [user] intervention is needed
                retVal = ReachableViaWiFi;
            }
        }
    
        if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
        {
            // ... but WWAN connections are OK if the calling application
            //     is using the CFNetwork (CFSocketStream?) APIs.
            retVal = ReachableViaWWAN;
        }
        return retVal;
    }
    
    - (BOOL) connectionRequired;
    {
        NSAssert(reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef");
        SCNetworkReachabilityFlags flags;
        if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
        {
            return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
        }
        return NO;
    }
    
    - (NetworkStatus) currentReachabilityStatus
    {
        NSAssert(reachabilityRef != NULL, @"currentNetworkStatus called with NULL reachabilityRef");
        NetworkStatus retVal = NotReachable;
        SCNetworkReachabilityFlags flags;
        if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
        {
            if(localWiFiRef)
            {
                retVal = [self localWiFiStatusForFlags: flags];
            }
            else
            {
                retVal = [self networkStatusForFlags: flags];
            }
        }
        return retVal;
    }
    @end
    

    Then I added it to GlobalShareClass

    GlobalShareClass.h

    #import "Reachability.h"
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    
    @interface GlobalShareClass : NSObject
    
    + (GlobalShareClass *)sharedInstance;
    + (BOOL)isConnectedInternet;
    + (void)showAlertController:(NSString *)strAltTit :(NSString *)strAltMsg passViewController:(UIViewController *)view;
    
    @end
    

    GlobalShareClass.m

    #import "GlobalShareClass.h"
    
    @implementation GlobalShareClass
    
    + (GlobalShareClass *)sharedInstance {
       static GlobalShareClass* _shareInstane = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _shareInstane = [[GlobalShareClass alloc] init];
        });
        return _shareInstane;
    }
    
    + (BOOL)isConnectedInternet {
        Reachability *r = [Reachability reachabilityWithHostName:@"www.google.com"];
        NetworkStatus internetStatus = [r currentReachabilityStatus];
        if ((internetStatus != ReachableViaWiFi) && (internetStatus != ReachableViaWWAN)) {
            return NO;
        }
        return YES;
    }
    
    + (void)showAlertController:(NSString *)strAltTit :(NSString *)strAltMsg passViewController:(UIViewController *)view{
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:strAltTit message:strAltMsg preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *actionOK = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
        }];
        [alert addAction:actionOK];
        [view presentViewController:alert animated:YES completion:nil];
    }
    

    Then call the GlobalShareCalss in ViewController

    ViewController.h

    #import <UIKit/UIKit.h>
    #import "GlobalShareClass.h"
    
    @interface ViewController : UIViewController{
          GlobalShareClass *globalShare;
    }
    
    - (IBAction)actionCheckInternet:(id)sender;
    
    @end
    

    ViewController.m

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    
    - (void)viewDidLoad {
    
        globalShare = [GlobalShareClass sharedInstance];
    }
    
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    
    #pragma mark - UIButton Action method
    
    - (IBAction)actionCheckInternet:(id)sender {
          if([GlobalShareClass isConnectedInternet]) {
                .....//Your Code
          }
          else{
                [GlobalShareClass showAlertController:@"No Internet Connection" :nil passViewController:self];
          }
    }
    
    @end