diff --git a/README.md b/README.md index bb5cb7a..2fec2b1 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ SJOPaperboy An easy to use library that lets you implement background updates in your app that run whenever the user enters or exits a specified location. -![SJOPaperboyViewController](https://raw.github.com/blork/SJOPaperboy/master/screenshot.png) +![SJOPaperboyViewController](screenshot.png) Dependancies diff --git a/SJOPaperboyExample.xcodeproj/project.pbxproj b/SJOPaperboyExample.xcodeproj/project.pbxproj index 071e8d0..7037ece 100644 --- a/SJOPaperboyExample.xcodeproj/project.pbxproj +++ b/SJOPaperboyExample.xcodeproj/project.pbxproj @@ -22,6 +22,8 @@ 22261C0516D1153C00AD9271 /* IPInsetLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 22261C0316D1153C00AD9271 /* IPInsetLabel.m */; }; 22261C0716D1156100AD9271 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22261C0616D1156100AD9271 /* CoreLocation.framework */; }; 22261C0916D1156600AD9271 /* AddressBookUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22261C0816D1156600AD9271 /* AddressBookUI.framework */; }; + F43CAE9716DD031600532F0F /* SJOReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = F43CAE9616DD031600532F0F /* SJOReachability.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + F43CAE9916DD044700532F0F /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F43CAE9816DD044700532F0F /* SystemConfiguration.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -47,6 +49,9 @@ 22261C0416D1153C00AD9271 /* IPInsetLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IPInsetLabel.h; sourceTree = ""; }; 22261C0616D1156100AD9271 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; }; 22261C0816D1156600AD9271 /* AddressBookUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBookUI.framework; path = System/Library/Frameworks/AddressBookUI.framework; sourceTree = SDKROOT; }; + F43CAE9516DD031600532F0F /* SJOReachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SJOReachability.h; sourceTree = ""; }; + F43CAE9616DD031600532F0F /* SJOReachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SJOReachability.m; sourceTree = ""; }; + F43CAE9816DD044700532F0F /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -54,6 +59,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + F43CAE9916DD044700532F0F /* SystemConfiguration.framework in Frameworks */, 22261C0916D1156600AD9271 /* AddressBookUI.framework in Frameworks */, 22261C0716D1156100AD9271 /* CoreLocation.framework in Frameworks */, 22261BDF16D1150300AD9271 /* UIKit.framework in Frameworks */, @@ -85,6 +91,7 @@ 22261BDD16D1150300AD9271 /* Frameworks */ = { isa = PBXGroup; children = ( + F43CAE9816DD044700532F0F /* SystemConfiguration.framework */, 22261C0816D1156600AD9271 /* AddressBookUI.framework */, 22261C0616D1156100AD9271 /* CoreLocation.framework */, 22261BDE16D1150300AD9271 /* UIKit.framework */, @@ -129,6 +136,8 @@ 22261BFC16D1152300AD9271 /* SJOPaperboyViewController.h */, 22261BFD16D1152300AD9271 /* SJOPaperboyLocationManager.m */, 22261BFE16D1152300AD9271 /* SJOPaperboyLocationManager.h */, + F43CAE9516DD031600532F0F /* SJOReachability.h */, + F43CAE9616DD031600532F0F /* SJOReachability.m */, ); name = Paperboy; sourceTree = ""; @@ -205,6 +214,7 @@ 22261C0016D1152300AD9271 /* SJOPaperboyViewController.m in Sources */, 22261C0116D1152300AD9271 /* SJOPaperboyLocationManager.m in Sources */, 22261C0516D1153C00AD9271 /* IPInsetLabel.m in Sources */, + F43CAE9716DD031600532F0F /* SJOReachability.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -319,6 +329,7 @@ 22261BFA16D1150300AD9271 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/SJOPaperboyExample/Paperboy.strings b/SJOPaperboyExample/Paperboy.strings index 84a3d0c..82829e9 100644 --- a/SJOPaperboyExample/Paperboy.strings +++ b/SJOPaperboyExample/Paperboy.strings @@ -15,6 +15,7 @@ "region_monitoring_not_available_cancel" = "Okay"; "background_updates" = "Background Updates"; +"mobile_updates" = "Use Mobile Data"; "getting_location" = "Getting Location..."; "add_location" = "Add Current Location"; "clear_locations" = "Clear Locations"; \ No newline at end of file diff --git a/SJOPaperboyExample/SJOPaperboyLocationManager.m b/SJOPaperboyExample/SJOPaperboyLocationManager.m index fb2dbc9..662f517 100644 --- a/SJOPaperboyExample/SJOPaperboyLocationManager.m +++ b/SJOPaperboyExample/SJOPaperboyLocationManager.m @@ -7,6 +7,15 @@ // #import "SJOPaperboyLocationManager.h" +#import "SJOPaperboyViewController.h" +#import "SJOReachability.h" + +@interface SJOPaperboyLocationManager (){ + UIBackgroundTaskIdentifier backgroundTask; +} + +@property (nonatomic, strong) SJOReachability *wifiReachability; +@end @implementation SJOPaperboyLocationManager @@ -17,6 +26,8 @@ - (id)init self.locationManager = [[CLLocationManager alloc] init]; self.locationManager.delegate = self; self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters; + backgroundTask = UIBackgroundTaskInvalid; + self.wifiReachability = [SJOReachability reachabilityForLocalWiFi]; } return self; } @@ -65,9 +76,47 @@ -(void)locationChanged } } - if (self.locationChangedBlock) { + if ([self canCallLocationChangedBlock]) { self.locationChangedBlock(); + } else { + // Start a Reachability notifier to let us know when we're back on wifi + [self.wifiReachability startNotifier]; + + __weak SJOPaperboyLocationManager *weakSelf = self; + id observer = nil; + observer = [[NSNotificationCenter defaultCenter] addObserverForName:kSJOReachabilityChangedNotification + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification *note) { + // This will be called when we're back on wifi + [weakSelf.wifiReachability stopNotifier]; + [[NSNotificationCenter defaultCenter] removeObserver:observer]; + weakSelf.locationChangedBlock(); + }]; + if (backgroundTask == UIBackgroundTaskInvalid){ + backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ + [weakSelf.wifiReachability stopNotifier]; + [[NSNotificationCenter defaultCenter] removeObserver:observer]; + + [[UIApplication sharedApplication] endBackgroundTask:backgroundTask]; + backgroundTask = UIBackgroundTaskInvalid; + }]; + } } } +- (BOOL) canCallLocationChangedBlock +{ + BOOL requiresWifi = ![SJOPaperboyViewController isMobileUpdatingEnabled]; + BOOL isOnWifi = ([self.wifiReachability currentReachabilityStatus] == ReachableViaWiFi); + + BOOL canUpdate = YES; + + if (requiresWifi && !isOnWifi){ + canUpdate = NO; + } + + return (self.locationChangedBlock != nil && canUpdate); +} + @end diff --git a/SJOPaperboyExample/SJOPaperboyViewController.h b/SJOPaperboyExample/SJOPaperboyViewController.h index c05d0fc..025f9ff 100644 --- a/SJOPaperboyExample/SJOPaperboyViewController.h +++ b/SJOPaperboyExample/SJOPaperboyViewController.h @@ -15,6 +15,7 @@ @property (nonatomic, strong) CLLocationManager *locationManager; + (BOOL) isBackgroundUpdatingEnabled; ++ (BOOL) isMobileUpdatingEnabled; + (NSArray*) locationsForUpdate; @end diff --git a/SJOPaperboyExample/SJOPaperboyViewController.m b/SJOPaperboyExample/SJOPaperboyViewController.m index b2f631d..1d0f522 100644 --- a/SJOPaperboyExample/SJOPaperboyViewController.m +++ b/SJOPaperboyExample/SJOPaperboyViewController.m @@ -12,6 +12,7 @@ #define kBackgroundUpdates @"paperboy_background_updates" #define kLocations @"paperboy_location_array" +#define kMobileUpdates @"paperboy_mobile_updates" @interface SJOPaperboyViewController () @@ -108,7 +109,11 @@ - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger { switch (section) { case 0: - return 1; + if ([SJOPaperboyViewController isBackgroundUpdatingEnabled]){ + return 2; + } else { + return 1; + } case 1: return 2; case 2:{ @@ -135,21 +140,40 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N switch (indexPath.section) { case 0: { - UISwitch* toggleSwitch = [[UISwitch alloc] init]; - - cell.userInteractionEnabled = [CLLocationManager regionMonitoringAvailable]; - toggleSwitch.userInteractionEnabled = [CLLocationManager regionMonitoringAvailable]; - - toggleSwitch.on = [[NSUserDefaults standardUserDefaults] boolForKey:kBackgroundUpdates]; - cell.accessoryView = toggleSwitch; - cell.selectionStyle = UITableViewCellSelectionStyleNone; - cell.textLabel.text = NSLocalizedStringFromTable(@"background_updates", @"Paperboy", nil); - cell.imageView.image = nil; - cell.textLabel.textAlignment = NSTextAlignmentLeft; - cell.textLabel.font = [UIFont boldSystemFontOfSize:16]; - cell.textLabel.textColor = [UIColor blackColor]; - [toggleSwitch addTarget:self action:@selector(toggleBackgroundUpdates:) forControlEvents:UIControlEventValueChanged]; - + switch (indexPath.row) { + case 0: + { + UISwitch* toggleSwitch = [[UISwitch alloc] init]; + + cell.userInteractionEnabled = [CLLocationManager regionMonitoringAvailable]; + toggleSwitch.userInteractionEnabled = [CLLocationManager regionMonitoringAvailable]; + + toggleSwitch.on = [[NSUserDefaults standardUserDefaults] boolForKey:kBackgroundUpdates]; + cell.accessoryView = toggleSwitch; + cell.selectionStyle = UITableViewCellSelectionStyleNone; + cell.textLabel.text = NSLocalizedStringFromTable(@"background_updates", @"Paperboy", nil); + cell.imageView.image = nil; + cell.textLabel.textAlignment = NSTextAlignmentLeft; + cell.textLabel.font = [UIFont boldSystemFontOfSize:16]; + cell.textLabel.textColor = [UIColor blackColor]; + [toggleSwitch addTarget:self action:@selector(toggleBackgroundUpdates:) forControlEvents:UIControlEventValueChanged]; + break; + } + case 1: + { + UISwitch* toggleSwitch = [[UISwitch alloc] init]; + toggleSwitch.on = [SJOPaperboyViewController isMobileUpdatingEnabled]; + cell.accessoryView = toggleSwitch; + cell.selectionStyle = UITableViewCellSelectionStyleNone; + cell.textLabel.text = NSLocalizedStringFromTable(@"mobile_updates", @"Paperboy", nil); + cell.imageView.image = nil; + cell.textLabel.textAlignment = NSTextAlignmentLeft; + cell.textLabel.font = [UIFont boldSystemFontOfSize:16]; + cell.textLabel.textColor = [UIColor blackColor]; + [toggleSwitch addTarget:self action:@selector(toggleMobileUpdates:) forControlEvents:UIControlEventValueChanged]; + break; + } + } break; } case 1: @@ -365,17 +389,31 @@ -(void) toggleBackgroundUpdates:(id)sender [userDefaults synchronize]; NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 2)]; + NSIndexPath *mobileDataIndexPath = [NSIndexPath indexPathForRow:1 inSection:0]; + + [self.tableView beginUpdates]; if (backgroundUpdatesEnabled) { self.numberOfSections = 3; + [self.tableView insertRowsAtIndexPaths:@[mobileDataIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; [self.tableView insertSections:indexes withRowAnimation:UITableViewRowAnimationAutomatic]; } else { self.numberOfSections = 1; + [self.tableView deleteRowsAtIndexPaths:@[mobileDataIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; [self.tableView deleteSections:indexes withRowAnimation:UITableViewRowAnimationAutomatic]; } + [self.tableView endUpdates]; [self updateGeofencedLocations]; } +-(void) toggleMobileUpdates:(id)sender +{ + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + BOOL mobileUpdatesEnabled = ![userDefaults boolForKey:kMobileUpdates]; + [userDefaults setBool:mobileUpdatesEnabled forKey:kMobileUpdates]; + [userDefaults synchronize]; +} + #pragma mark Static helpers + (BOOL) isBackgroundUpdatingEnabled @@ -397,4 +435,10 @@ + (NSArray*) locationsForUpdate return [NSArray arrayWithArray:locations]; } ++ (BOOL) isMobileUpdatingEnabled +{ + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + return [userDefaults boolForKey:kMobileUpdates]; +} + @end diff --git a/SJOPaperboyExample/SJOReachability.h b/SJOPaperboyExample/SJOReachability.h new file mode 100644 index 0000000..4ac3c51 --- /dev/null +++ b/SJOPaperboyExample/SJOReachability.h @@ -0,0 +1,89 @@ +/* + + File: Reachability.h + Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs. + + Version: 2.2 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under + Apple's copyrights in this original Apple software (the "Apple Software"), to + use, reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions + of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may be used + to endorse or promote products derived from the Apple Software without specific + prior written permission from Apple. Except as expressly stated in this notice, + no other rights or licenses, express or implied, are granted by Apple herein, + including but not limited to any patent rights that may be infringed by your + derivative works or by other works in which the Apple Software may be + incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR + DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF + CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF + APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2010 Apple Inc. All Rights Reserved. + +*/ + + +#import +#import +#import + +typedef enum { + NotReachable = 0, + ReachableViaWiFi, + ReachableViaWWAN +} SJONetworkStatus; +#define kSJOReachabilityChangedNotification @"kSJONetworkReachabilityChangedNotification" + +@interface SJOReachability: NSObject +{ + BOOL localWiFiRef; + SCNetworkReachabilityRef reachabilityRef; +} + +//reachabilityWithHostName- Use to check the reachability of a particular host name. ++ (SJOReachability*) reachabilityWithHostName: (NSString*) hostName; + +//reachabilityWithAddress- Use to check the reachability of a particular IP address. ++ (SJOReachability*) 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 ++ (SJOReachability*) reachabilityForInternetConnection; + +//reachabilityForLocalWiFi- checks whether a local wifi connection is available. ++ (SJOReachability*) reachabilityForLocalWiFi; + +//Start listening for reachability notifications on the current run loop +- (BOOL) startNotifier; +- (void) stopNotifier; + +- (SJONetworkStatus) 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 + + diff --git a/SJOPaperboyExample/SJOReachability.m b/SJOPaperboyExample/SJOReachability.m new file mode 100644 index 0000000..c80b199 --- /dev/null +++ b/SJOPaperboyExample/SJOReachability.m @@ -0,0 +1,273 @@ +/* + + File: Reachability.m + Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs. + + Version: 2.2 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under + Apple's copyrights in this original Apple software (the "Apple Software"), to + use, reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions + of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may be used + to endorse or promote products derived from the Apple Software without specific + prior written permission from Apple. Except as expressly stated in this notice, + no other rights or licenses, express or implied, are granted by Apple herein, + including but not limited to any patent rights that may be infringed by your + derivative works or by other works in which the Apple Software may be + incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR + DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF + CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF + APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2010 Apple Inc. All Rights Reserved. + +*/ + +#import +#import +#import +#import +#import +#import + +#import + +#import "SJOReachability.h" + +#define kShouldPrintReachabilityFlags 0 + +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 SJOReachability +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: [SJOReachability 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]; + + SJOReachability* noteObject = (SJOReachability*) info; + // Post a notification to notify the client that the network reachability changed. + [[NSNotificationCenter defaultCenter] postNotificationName: kSJOReachabilityChangedNotification 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]; +} + ++ (SJOReachability*) reachabilityWithHostName: (NSString*) hostName; +{ + SJOReachability* 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; +} + ++ (SJOReachability*) reachabilityWithAddress: (const struct sockaddr_in*) hostAddress; +{ + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress); + SJOReachability* retVal = NULL; + if(reachability!= NULL) + { + retVal= [[[self alloc] init] autorelease]; + if(retVal!= NULL) + { + retVal->reachabilityRef = reachability; + retVal->localWiFiRef = NO; + } + } + return retVal; +} + ++ (SJOReachability*) reachabilityForInternetConnection; +{ + struct sockaddr_in zeroAddress; + bzero(&zeroAddress, sizeof(zeroAddress)); + zeroAddress.sin_len = sizeof(zeroAddress); + zeroAddress.sin_family = AF_INET; + return [self reachabilityWithAddress: &zeroAddress]; +} + ++ (SJOReachability*) reachabilityForLocalWiFi; +{ + struct sockaddr_in localWifiAddress; + bzero(&localWifiAddress, sizeof(localWifiAddress)); + localWifiAddress.sin_len = sizeof(localWifiAddress); + localWifiAddress.sin_family = AF_INET; + // IN_LINKLOCALNETNUM is defined in as 169.254.0.0 + localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); + SJOReachability* retVal = [self reachabilityWithAddress: &localWifiAddress]; + if(retVal!= NULL) + { + retVal->localWiFiRef = YES; + } + return retVal; +} + +#pragma mark Network Flag Handling + +- (SJONetworkStatus) localWiFiStatusForFlags: (SCNetworkReachabilityFlags) flags +{ + PrintReachabilityFlags(flags, "localWiFiStatusForFlags"); + + BOOL retVal = NotReachable; + if((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect)) + { + retVal = ReachableViaWiFi; + } + return retVal; +} + +- (SJONetworkStatus) 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; +} + +- (SJONetworkStatus) currentReachabilityStatus +{ + NSAssert(reachabilityRef != NULL, @"currentNetworkStatus called with NULL reachabilityRef"); + SJONetworkStatus retVal = NotReachable; + SCNetworkReachabilityFlags flags; + if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) + { + if(localWiFiRef) + { + retVal = [self localWiFiStatusForFlags: flags]; + } + else + { + retVal = [self networkStatusForFlags: flags]; + } + } + return retVal; +} +@end diff --git a/screenshot.png b/screenshot.png index 38db6b8..415cd6a 100644 Binary files a/screenshot.png and b/screenshot.png differ