AutorÃa | Ultima modificación | Ver Log |
// Copyright 2017 Google//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.#import <Foundation/Foundation.h>#import "GoogleUtilities/Reachability/Public/GoogleUtilities/GULReachabilityChecker.h"#import "GoogleUtilities/Reachability/GULReachabilityChecker+Internal.h"#import "GoogleUtilities/Reachability/GULReachabilityMessageCode.h"#import "GoogleUtilities/Logger/Public/GoogleUtilities/GULLogger.h"static GULLoggerService kGULLoggerReachability = @"[GULReachability]";#if !TARGET_OS_WATCHstatic void ReachabilityCallback(SCNetworkReachabilityRef reachability,SCNetworkReachabilityFlags flags,void *info);static const struct GULReachabilityApi kGULDefaultReachabilityApi = {SCNetworkReachabilityCreateWithName,SCNetworkReachabilitySetCallback,SCNetworkReachabilityScheduleWithRunLoop,SCNetworkReachabilityUnscheduleFromRunLoop,CFRelease,};static NSString *const kGULReachabilityUnknownStatus = @"Unknown";static NSString *const kGULReachabilityConnectedStatus = @"Connected";static NSString *const kGULReachabilityDisconnectedStatus = @"Disconnected";#endif@interface GULReachabilityChecker ()@property(nonatomic, assign) const struct GULReachabilityApi *reachabilityApi;@property(nonatomic, assign) GULReachabilityStatus reachabilityStatus;@property(nonatomic, copy) NSString *host;#if !TARGET_OS_WATCH@property(nonatomic, assign) SCNetworkReachabilityRef reachability;#endif@end@implementation GULReachabilityChecker@synthesize reachabilityApi = reachabilityApi_;#if !TARGET_OS_WATCH@synthesize reachability = reachability_;#endif- (const struct GULReachabilityApi *)reachabilityApi {return reachabilityApi_;}- (void)setReachabilityApi:(const struct GULReachabilityApi *)reachabilityApi {#if !TARGET_OS_WATCHif (reachability_) {GULLogError(kGULLoggerReachability, NO,[NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode000],@"Cannot change reachability API while reachability is running. "@"Call stop first.");return;}reachabilityApi_ = reachabilityApi;#endif}@synthesize reachabilityStatus = reachabilityStatus_;@synthesize host = host_;@synthesize reachabilityDelegate = reachabilityDelegate_;- (BOOL)isActive {#if !TARGET_OS_WATCHreturn reachability_ != nil;#elsereturn NO;#endif}- (void)setReachabilityDelegate:(id<GULReachabilityDelegate>)reachabilityDelegate {if (reachabilityDelegate &&(![(NSObject *)reachabilityDelegate conformsToProtocol:@protocol(GULReachabilityDelegate)])) {GULLogError(kGULLoggerReachability, NO,[NSString stringWithFormat:@"I-NET%06ld", (long)kGULReachabilityMessageCode005],@"Reachability delegate doesn't conform to Reachability protocol.");return;}reachabilityDelegate_ = reachabilityDelegate;}- (instancetype)initWithReachabilityDelegate:(id<GULReachabilityDelegate>)reachabilityDelegatewithHost:(NSString *)host {self = [super init];if (!host || !host.length) {GULLogError(kGULLoggerReachability, NO,[NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode001],@"Invalid host specified");return nil;}if (self) {#if !TARGET_OS_WATCH[self setReachabilityDelegate:reachabilityDelegate];reachabilityApi_ = &kGULDefaultReachabilityApi;reachabilityStatus_ = kGULReachabilityUnknown;host_ = [host copy];reachability_ = nil;#endif}return self;}- (void)dealloc {reachabilityDelegate_ = nil;[self stop];}- (BOOL)start {#if TARGET_OS_WATCHreturn NO;#elseif (!reachability_) {reachability_ = reachabilityApi_->createWithNameFn(kCFAllocatorDefault, [host_ UTF8String]);if (!reachability_) {return NO;}SCNetworkReachabilityContext context = {0, /* version */(__bridge void *)(self), /* info (passed as last parameter to reachability callback) */NULL, /* retain */NULL, /* release */NULL /* copyDescription */};if (!reachabilityApi_->setCallbackFn(reachability_, ReachabilityCallback, &context) ||!reachabilityApi_->scheduleWithRunLoopFn(reachability_, CFRunLoopGetMain(),kCFRunLoopCommonModes)) {reachabilityApi_->releaseFn(reachability_);reachability_ = nil;GULLogError(kGULLoggerReachability, NO,[NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode002],@"Failed to start reachability handle");return NO;}}GULLogDebug(kGULLoggerReachability, NO,[NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode003],@"Monitoring the network status");return YES;#endif}- (void)stop {#if !TARGET_OS_WATCHif (reachability_) {reachabilityStatus_ = kGULReachabilityUnknown;reachabilityApi_->unscheduleFromRunLoopFn(reachability_, CFRunLoopGetMain(),kCFRunLoopCommonModes);reachabilityApi_->releaseFn(reachability_);reachability_ = nil;}#endif}#if !TARGET_OS_WATCH- (GULReachabilityStatus)statusForFlags:(SCNetworkReachabilityFlags)flags {GULReachabilityStatus status = kGULReachabilityNotReachable;// If the Reachable flag is not set, we definitely don't have connectivity.if (flags & kSCNetworkReachabilityFlagsReachable) {// Reachable flag is set. Check further flags.if (!(flags & kSCNetworkReachabilityFlagsConnectionRequired)) {// Connection required flag is not set, so we have connectivity.#if TARGET_OS_IOS || TARGET_OS_TVstatus = (flags & kSCNetworkReachabilityFlagsIsWWAN) ? kGULReachabilityViaCellular: kGULReachabilityViaWifi;#elif TARGET_OS_OSXstatus = kGULReachabilityViaWifi;#endif} else if ((flags & (kSCNetworkReachabilityFlagsConnectionOnDemand |kSCNetworkReachabilityFlagsConnectionOnTraffic)) &&!(flags & kSCNetworkReachabilityFlagsInterventionRequired)) {// If the connection on demand or connection on traffic flag is set, and user intervention// is not required, we have connectivity.#if TARGET_OS_IOS || TARGET_OS_TVstatus = (flags & kSCNetworkReachabilityFlagsIsWWAN) ? kGULReachabilityViaCellular: kGULReachabilityViaWifi;#elif TARGET_OS_OSXstatus = kGULReachabilityViaWifi;#endif}}return status;}- (void)reachabilityFlagsChanged:(SCNetworkReachabilityFlags)flags {GULReachabilityStatus status = [self statusForFlags:flags];if (reachabilityStatus_ != status) {NSString *reachabilityStatusString;if (status == kGULReachabilityUnknown) {reachabilityStatusString = kGULReachabilityUnknownStatus;} else {reachabilityStatusString = (status == kGULReachabilityNotReachable)? kGULReachabilityDisconnectedStatus: kGULReachabilityConnectedStatus;}GULLogDebug(kGULLoggerReachability, NO,[NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode004],@"Network status has changed. Code:%@, status:%@", @(status),reachabilityStatusString);reachabilityStatus_ = status;[reachabilityDelegate_ reachability:self statusChanged:reachabilityStatus_];}}#endif@end#if !TARGET_OS_WATCHstatic void ReachabilityCallback(SCNetworkReachabilityRef reachability,SCNetworkReachabilityFlags flags,void *info) {GULReachabilityChecker *checker = (__bridge GULReachabilityChecker *)info;[checker reachabilityFlagsChanged:flags];}#endif// This function used to be at the top of the file, but it was moved here// as a workaround for a suspected compiler bug. When compiled in Release mode// and run on an iOS device with WiFi disabled, the reachability code crashed// when calling SCNetworkReachabilityScheduleWithRunLoop, or shortly thereafter.// After unsuccessfully trying to diagnose the cause of the crash, it was// discovered that moving this function to the end of the file magically fixed// the crash. If you are going to edit this file, exercise caution and make sure// to test thoroughly with an iOS device under various network conditions.const NSString *GULReachabilityStatusString(GULReachabilityStatus status) {switch (status) {case kGULReachabilityUnknown:return @"Reachability Unknown";case kGULReachabilityNotReachable:return @"Not reachable";case kGULReachabilityViaWifi:return @"Reachable via Wifi";case kGULReachabilityViaCellular:return @"Reachable via Cellular Data";default:return [NSString stringWithFormat:@"Invalid reachability status %d", (int)status];}}