简体   繁体   中英

iOS/iPhone Reachability - How to only check when internet is lost/not reachable using Reachability.m/.h

Currently i am using the class by apple reachability.m/.h and it works, except it notifies me for any change, where as i would like to only notify the user if the network is not reachable. Currently if i have a internet connection and then loose the network it tells me. However when you reconnect to the network it also tells me, which i do not want. I want it to only tell me when there is a loss/no network.

I believe it has something to do with the call:

- (void)viewWillAppear:(BOOL)animated
{
    // check for internet connection
    [[NSNotificationCenter defaultCenter]
          addObserver:self
             selector:@selector(checkNetworkStatus:)
                 name:kReachabilityChangedNotification
               object:nil];

    internetReachable = [[Reachability
                         reachabilityForInternetConnection] retain];
    [internetReachable startNotifier];

    // check if a pathway to a random host exists
    hostReachable = [[Reachability reachabilityWithHostName:
                     @"www.google.ca"] retain];
    [hostReachable startNotifier];

    // now patiently wait for the notification
}

when calling -[NSNotificationCenter addObserver:selector:name:object:] , does the name have any other function then being literally a name? this is my first time using NSNotificationCenter so i am not well versed in this matter.

EDIT:

Here is my checkNetworkStatus function: (The problem is i am getting "NotReachable" as the network connection is coming back and NSAlert goes off multiple times)

- (void) checkNetworkStatus:(NSNotification *)notice
{
        // called after network status changes
NetworkStatus internetStatus = [internetReachable currentReachabilityStatus];
switch (internetStatus)

{
    case NotReachable:
    {
        UIAlertView * alert  = [[UIAlertView alloc] initWithTitle:@"Network Failed" message:@"Please check your connection and try again." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil ];
        [alert show];
        NSLog(@"The internet is down.");

        break;

    }
    case ReachableViaWiFi:
    {               
        NSLog(@"The internet is working via WIFI.");

        break;

    }
    case ReachableViaWWAN:
    {
        NSLog(@"The internet is working via WWAN.");

        break;

    }
}

NetworkStatus hostStatus = [hostReachable currentReachabilityStatus];
switch (hostStatus)

{
    case NotReachable:
    {
        NSLog(@"A gateway to the host server is down.");

        break;

    }
    case ReachableViaWiFi:
    {
        NSLog(@"A gateway to the host server is working via WIFI.");

        break;

    }
    case ReachableViaWWAN:
    {
        NSLog(@"A gateway to the host server is working via WWAN.");

        break;

    }
}

}

Reachability will send a notification when the status has changed, but what you do with that notification is entirely up to you. If you don't want to tell the user that the network is back, you don't have to.

The "name" parameter in the NSNotificationCenter method indicates what notification you are subscribing to. When an object posts a notification, it does so with a particular name.

如果您仅使用IP地址替换www.hostname.com,它将仅提醒一次而不是多次。

I just started playing around with Reachability and hopefully what I discovered is of use to you.

With regards to multiple 'Not Reachable' while reconnecting, could it be linked to this ? Here the poster brought up the definition of 'reachable' for a remote host. I'm guessing while reconnecting the package is not able to go through successfully?

Another possibility is in Reachability Readme.txt

IMPORTANT: Reachability must use DNS to resolve the host name before it can determine the Reachability of that host, and this may take time on certain network connections. Because of this, the API will return NotReachable until name resolution has completed. This delay may be visible in the interface on some networks.

Maybe give it the IP directly and see if it helps?

With Reachability 2.2, you can add

[hostReach connectionRequired];

before

[internetReachable startNotifier];

to solve this problem.

runmad answered this problem here: https://stackoverflow.com/a/2157858/623260

We can check rechability using this code

add class Reachability.h

#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.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");

                //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 = (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, 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] autorelease];
                    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] autorelease];
                    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

and use via direct calling method in appdel class using

  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(checkNetworkStatus:) name:kReachabilityChangedNotification object:nil];



 -(void) checkNetworkStatus:(NSNotification *)notice
{

 Reachability* internetReachable;
  BOOL isInternetActive;
// called after network status changes
NetworkStatus internetStatus = [internetReachable currentReachabilityStatus];
switch (internetStatus)
{
    case NotReachable:
    {
        NSLog(@"The internet is down.");
        isInternetActive = NO;

        break;
    }
    case ReachableViaWiFi:
    {
        NSLog(@"The internet is working via WIFI.");
        isInternetActive = YES;

        break;
    }
    case ReachableViaWWAN:
    {
        NSLog(@"The internet is working via WWAN.");
        isInternetActive = YES;

        break;
    }
 }

 NetworkStatus hostStatus = [hostReachable currentReachabilityStatus];
switch (hostStatus)
{
    case NotReachable:
    {
        NSLog(@"A gateway to the host server is down.");
        //            self.hostActive = NO;

        break;
    }
    case ReachableViaWiFi:
    {
        NSLog(@"A gateway to the host server is working via WIFI.");
        //            self.hostActive = YES;

        break;
    }
    case ReachableViaWWAN:
    {
        NSLog(@"A gateway to the host server is working via WWAN.");
        //            self.hostActive = YES;

        break;
    }
 }
}

I would implement the whole Reachability class, tie it into your code as necessary and remove or comment out parts of Reachability. Just remove, one by one, the things you do not want to be notified of. Obviously, you need a good understanding of obj-c, the Reachability class, notifications, etc., but it can be done.

One thing you can do is unsubscribe to the changed notification NSNotificationCenter removeObserver... while you're processing one in the callback. Add back the observer before returning.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM