Skip to content

Instantly share code, notes, and snippets.

@norio-nomura
Created March 17, 2009 00:08
Show Gist options
  • Save norio-nomura/80184 to your computer and use it in GitHub Desktop.
Save norio-nomura/80184 to your computer and use it in GitHub Desktop.
//
// Reachability.h
//
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
@interface Reachability : NSObject {
NSString *hostName;
SCNetworkReachabilityFlags reachabilityFlags;
SCNetworkReachabilityRef reachabilityRef;
CFMutableArrayRef runLoops;
}
@property (nonatomic,readonly) NSString *hostName;
@property (nonatomic) BOOL enableNotification;
@property (nonatomic,readonly) NSString *notificationName;
@property (nonatomic,readonly) BOOL isReachableWithoutRequiringConnection;
@property (nonatomic,readonly) BOOL isReachableWithWWAN;
@property (readonly) SCNetworkReachabilityFlags reachabilityFlags;
- (id)initWithHostName:(NSString*)aHostName;
- (void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector;
- (void)removeObserver:(id)notificationObserver;
@end
//
// Reachability.m
//
#import "Reachability.h"
@interface Reachability(private)
@property (nonatomic,assign) SCNetworkReachabilityFlags reachabilityFlags;
- (void)updateReachabilityFlagsIfEnableNotificationIsNo;
- (BOOL)isScheduledOnRunLoop:(NSRunLoop *)inRunLoop;
- (void)scheduleOnRunLoop:(NSRunLoop *)inRunLoop;
- (void)unscheduleFromRunLoop:(NSRunLoop*)inRunLoop;
@end
@implementation Reachability
@synthesize hostName;
@synthesize reachabilityFlags;
- (void)dealloc {
[hostName release];
if (reachabilityRef) {
for (CFIndex index = 0; index < CFArrayGetCount(runLoops); index++) {
CFRunLoopRef runLoop = (CFRunLoopRef)CFArrayGetValueAtIndex(runLoops, index);
SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, runLoop, kCFRunLoopDefaultMode);
}
}
CFRelease(reachabilityRef);
CFRelease(runLoops);
[super dealloc];
}
- (void)setEnableNotification:(BOOL)flag {
@synchronized(self) {
NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop];
if (!flag && [self isScheduledOnRunLoop:mainRunLoop]) {
[self unscheduleFromRunLoop:mainRunLoop];
} else if (flag && ![self isScheduledOnRunLoop:mainRunLoop]) {
[self scheduleOnRunLoop:mainRunLoop];
}
}
}
- (BOOL)enableNotification {
@synchronized(self) {
return [self isScheduledOnRunLoop:[NSRunLoop mainRunLoop]];
}
}
- (NSString*)notificationName {
return [NSString stringWithFormat:@"kNetworkReachabilityChangedNotification(%@)",hostName];
}
- (BOOL)isReachableWithoutRequiringConnection {
@synchronized(self) {
[self updateReachabilityFlagsIfEnableNotificationIsNo];
// kSCNetworkReachabilityFlagsReachable indicates that the specified nodename or address can
// be reached using the current network configuration.
BOOL isReachable = reachabilityFlags & kSCNetworkReachabilityFlagsReachable;
// This flag indicates that the specified nodename or address can
// be reached using the current network configuration, but a
// connection must first be established.
//
// If the flag is false, we don't have a connection. But because CFNetwork
// automatically attempts to bring up a WWAN connection, if the WWAN reachability
// flag is present, a connection is not required.
BOOL noConnectionRequired = !(reachabilityFlags & kSCNetworkReachabilityFlagsConnectionRequired);
if (reachabilityFlags & kSCNetworkReachabilityFlagsIsWWAN) {
noConnectionRequired = YES;
}
return (isReachable && noConnectionRequired) ? YES : NO;
}
}
- (BOOL)isReachableWithWWAN {
@synchronized(self) {
[self updateReachabilityFlagsIfEnableNotificationIsNo];
BOOL isReachable = reachabilityFlags & kSCNetworkReachabilityFlagsReachable;
BOOL isWWAN = reachabilityFlags & kSCNetworkReachabilityFlagsIsWWAN;
return (isReachable && isWWAN) ? YES : NO;
}
}
- (id)initWithHostName:(NSString*)aHostName {
if (self = [super init]) {
hostName = [aHostName retain];
runLoops = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
}
return self;
}
- (void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector {
self.enableNotification = YES;
[[NSNotificationCenter defaultCenter] addObserver:notificationObserver
selector:notificationSelector
name:self.notificationName
object:self];
}
- (void)removeObserver:(id)notificationObserver {
[[NSNotificationCenter defaultCenter] removeObserver:notificationObserver
name:self.notificationName
object:self];
}
#pragma mark Private Method
- (void)updateReachabilityFlagsIfEnableNotificationIsNo {
@synchronized(self) {
if (!self.enableNotification) {
if (!reachabilityRef) {
reachabilityRef = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [hostName UTF8String]);
}
if (!SCNetworkReachabilityGetFlags(reachabilityRef,&reachabilityFlags)) {
reachabilityFlags = 0;
}
}
}
}
- (BOOL)isScheduledOnRunLoop:(NSRunLoop *)inRunLoop {
@synchronized(self) {
CFRunLoopRef runLoop = [inRunLoop getCFRunLoop];
return CFArrayContainsValue(runLoops, CFRangeMake(0, CFArrayGetCount(runLoops)), runLoop);
}
}
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Reachability *reachability = info;
@synchronized(reachability) {
reachability->reachabilityFlags = flags;
}
[[NSNotificationCenter defaultCenter] postNotificationName:[reachability notificationName] object:info];
[pool release];
}
- (void)scheduleOnRunLoop:(NSRunLoop *)inRunLoop {
@synchronized(self) {
if (hostName && inRunLoop) {
if (!reachabilityRef) {
reachabilityRef = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [hostName UTF8String]);
}
if (reachabilityRef && ![self isScheduledOnRunLoop:inRunLoop]) {
CFRunLoopRef runLoop = [inRunLoop getCFRunLoop];
SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL};
if (SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context) &&
SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, runLoop, kCFRunLoopDefaultMode)) {
CFArrayAppendValue(runLoops, runLoop);
}
}
}
}
}
- (void)unscheduleFromRunLoop:(NSRunLoop*)inRunLoop {
@synchronized(self) {
if (reachabilityRef && [self isScheduledOnRunLoop:inRunLoop]) {
CFRunLoopRef runLoop = [inRunLoop getCFRunLoop];
if (SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, runLoop, kCFRunLoopDefaultMode)) {
CFIndex index = CFArrayGetFirstIndexOfValue(runLoops, CFRangeMake(0, CFArrayGetCount(runLoops)), runLoop);
if (index != kCFNotFound) {
CFArrayRemoveValueAtIndex(runLoops, index);
}
}
}
}
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment