Воспроизводимость iOS/iPhone - как проверить, когда интернет потерян/недоступен с помощью Reachability.m/.h
В настоящее время я использую класс с помощью достижимости apple.m/.h, и он работает, за исключением того, что он уведомляет меня о любых изменениях, где я хотел бы только уведомить пользователя, если сеть недоступна. В настоящее время, если у меня есть подключение к Интернету, а затем потеряйте сеть, это говорит мне. Однако, когда вы снова подключаетесь к сети, это также говорит мне, чего я не хочу. Я хочу, чтобы он говорил только, когда есть сеть потерь/нет.
Я считаю, что это имеет какое-то отношение к вызову:
- (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
}
при вызове -[NSNotificationCenter addObserver:selector:name:object:]
, имеет ли имя какая-либо другая функция, а значит, это буквально имя? это мой первый раз, используя NSNotificationCenter, поэтому я не очень разбираюсь в этом вопросе.
EDIT:
Вот моя функция checkNetworkStatus: (Проблема в том, что я получаю "NotReachable", когда сетевое соединение возвращается и NSAlert отключается несколько раз)
- (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;
}
}
}
Ответы
Ответ 1
Достижимость отправит уведомление, когда статус изменился, но то, что вы делаете с этим уведомлением, полностью зависит от вас. Если вы не хотите сообщать пользователю, что сеть вернулась, вам не нужно.
Параметр "имя" в методе NSNotificationCenter указывает, к какому уведомлению вы подписываетесь. Когда объект отправляет уведомление, он делает это с определенным именем.
Ответ 2
Если вы замените www.hostname.com только IP-адресом, он будет предупреждать только один раз, а не несколько раз.
Ответ 3
Я только начал играть с Reachability, и, надеюсь, то, что я обнаружил, вам полезно.
Что касается множественного "Не достижимого" при повторном подключении, можно ли связать его с этим? Здесь плакат поднял определение "достижимый" для удаленного хоста. Я предполагаю, что при повторном подключении пакета не удается пройти успешно?
Другая возможность - в Reachability Readme.txt
ВАЖНО: Досягаемость должна использовать DNS для разрешения имени хоста перед
может определить Досягаемость этого хоста, и на это может потребоваться время
определенных сетевых подключений. Из-за этого API вернется
NotReachable до завершения разрешения имен. Эта задержка может быть
видимый в интерфейсе в некоторых сетях.
Может быть, дать ему IP-адрес напрямую и посмотреть, помогает ли он?
Ответ 4
С Reachability 2.2 вы можете добавить
[hostReach connectionRequired];
перед
[internetReachable startNotifier];
чтобы решить эту проблему.
runmad ответил на эту проблему:
fooobar.com/questions/517837/...
Ответ 5
Я бы выполнил весь класс Reachability, привязал его к вашему коду по мере необходимости и удалил или прокомментировал части Reachability. Просто удалите, один за другим, то, о чем вы не хотите получать уведомления. Очевидно, вам нужно хорошее понимание obj-c, класса Reachability, уведомлений и т.д., Но это можно сделать.
Ответ 6
Одна вещь, которую вы можете сделать, - отказаться от подписки на измененное уведомление NSNotificationCenter removeObserver... пока вы обрабатываете один в обратном вызове. Добавьте обратно наблюдателя перед возвратом.
Ответ 7
Мы можем проверить возможность повторного использования с помощью этого кода
добавить класс 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
Достижимость .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
и использовать метод прямого вызова в классе appdel, используя
[[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;
}
}
}