diff --git a/lib/pal/posix/NetworkInformationImpl.mm b/lib/pal/posix/NetworkInformationImpl.mm index 24c7839eb..943c22667 100644 --- a/lib/pal/posix/NetworkInformationImpl.mm +++ b/lib/pal/posix/NetworkInformationImpl.mm @@ -20,7 +20,7 @@ class NetworkInformation : public NetworkInformationImpl, public std::enable_shared_from_this { - public: + public: /// /// /// @@ -59,21 +59,27 @@ virtual NetworkCost GetNetworkCost() /// /// Setup initial network information and start net monitor if requested. /// This cannot be put in constructor because we need to use shared_from_this. - /// - void SetupNetDetect(); + /// + void SetupNetDetect(); - private: - void UpdateType(NetworkType type) noexcept; - void UpdateCost(NetworkCost cost) noexcept; - std::string m_network_provider {}; + private: + void SetupModernNetDetect() API_AVAILABLE(macos(10.14), ios(12.0)); +#if ODW_LEGACY_REACHABILITY_REQUIRED + void SetupLegacyNetDetect(); +#endif + void UpdateType(NetworkType type) noexcept; + void UpdateCost(NetworkCost cost) noexcept; + std::string m_network_provider {}; - // iOS 12 and newer - nw_path_monitor_t m_monitor = nil; + // iOS 12+ / macOS 10.14+ + nw_path_monitor_t m_monitor = nil; - // iOS 11 and older - ODWReachability* m_reach = nil; - id m_notificationId = nil; - }; +#if ODW_LEGACY_REACHABILITY_REQUIRED + // Older Apple deployment targets still need the legacy fallback. + ODWReachability* m_reach = nil; + id m_notificationId = nil; +#endif + }; NetworkInformation::NetworkInformation(IRuntimeConfig& configuration) : NetworkInformationImpl(configuration) @@ -84,6 +90,7 @@ virtual NetworkCost GetNetworkCost() NetworkInformation::~NetworkInformation() noexcept { +#if ODW_LEGACY_REACHABILITY_REQUIRED if (@available(macOS 10.14, iOS 12.0, *)) { if (m_isNetDetectEnabled) @@ -99,108 +106,130 @@ virtual NetworkCost GetNetworkCost() [m_reach stopNotifier]; } } +#else + if (m_isNetDetectEnabled) + { + nw_path_monitor_cancel(m_monitor); + } +#endif } - void NetworkInformation::SetupNetDetect() + void NetworkInformation::SetupModernNetDetect() { - if (@available(macOS 10.14, iOS 12.0, *)) + auto weak_this = std::weak_ptr(shared_from_this()); + + m_monitor = nw_path_monitor_create(); + nw_path_monitor_set_queue(m_monitor, dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)); + nw_path_monitor_set_update_handler(m_monitor, ^(nw_path_t path) { - auto weak_this = std::weak_ptr(shared_from_this()); + auto strong_this = weak_this.lock(); + if (!strong_this) + { + return; + } - m_monitor = nw_path_monitor_create(); - nw_path_monitor_set_queue(m_monitor, dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)); - nw_path_monitor_set_update_handler(m_monitor, ^(nw_path_t path) + NetworkType type = NetworkType_Unknown; + NetworkCost cost = NetworkCost_Unknown; + nw_path_status_t status = nw_path_get_status(path); + bool connected = status == nw_path_status_satisfied || status == nw_path_status_satisfiable; + if (connected) { - auto strong_this = weak_this.lock(); - if (!strong_this) + if (nw_path_uses_interface_type(path, nw_interface_type_wifi)) { - return; + type = NetworkType_Wifi; } - - NetworkType type = NetworkType_Unknown; - NetworkCost cost = NetworkCost_Unknown; - nw_path_status_t status = nw_path_get_status(path); - bool connected = status == nw_path_status_satisfied || status == nw_path_status_satisfiable; - if (connected) + else if (nw_path_uses_interface_type(path, nw_interface_type_cellular)) { - if (nw_path_uses_interface_type(path, nw_interface_type_wifi)) - { - type = NetworkType_Wifi; - } - else if (nw_path_uses_interface_type(path, nw_interface_type_cellular)) - { - type = NetworkType_WWAN; - } - else if (nw_path_uses_interface_type(path, nw_interface_type_wired)) - { - type = NetworkType_Wired; - } - cost = nw_path_is_expensive(path) ? NetworkCost_Metered : NetworkCost_Unmetered; - if (@available(macOS 10.15, iOS 13.0, *)) + type = NetworkType_WWAN; + } + else if (nw_path_uses_interface_type(path, nw_interface_type_wired)) + { + type = NetworkType_Wired; + } + cost = nw_path_is_expensive(path) ? NetworkCost_Metered : NetworkCost_Unmetered; + if (@available(macOS 10.15, iOS 13.0, *)) + { + if (nw_path_is_constrained(path)) { - if (nw_path_is_constrained(path)) - { - cost = NetworkCost_Roaming; - } + cost = NetworkCost_Roaming; } } - strong_this->UpdateType(type); - strong_this->UpdateCost(cost); - }); - nw_path_monitor_start(m_monitor); - - // nw_path_monitor_start will invoke the callback for once. So if - // we don't want to listen for changes, we can just start the - // monitor and stop it right away. - if (!m_isNetDetectEnabled) - { - nw_path_monitor_cancel(m_monitor); } - } - else + strong_this->UpdateType(type); + strong_this->UpdateCost(cost); + }); + nw_path_monitor_start(m_monitor); + + // nw_path_monitor_start will invoke the callback for once. So if + // we don't want to listen for changes, we can just start the + // monitor and stop it right away. + if (!m_isNetDetectEnabled) { - auto weak_this = std::weak_ptr(shared_from_this()); + nw_path_monitor_cancel(m_monitor); + } + } - m_reach = [ODWReachability reachabilityForInternetConnection]; - void (^block)(NSNotification*) = ^(NSNotification*) - { - auto strong_this = weak_this.lock(); - if (!strong_this) - { - return; - } +#if ODW_LEGACY_REACHABILITY_REQUIRED + void NetworkInformation::SetupLegacyNetDetect() + { + auto weak_this = std::weak_ptr(shared_from_this()); - // NetworkCost information is not available until iOS 12. - // Just make the best guess here. - switch (m_reach.currentReachabilityStatus) - { - case NotReachable: - strong_this->UpdateType(NetworkType_Unknown); - strong_this->UpdateCost(NetworkCost_Unknown); - break; - case ReachableViaWiFi: - strong_this->UpdateType(NetworkType_Wifi); - strong_this->UpdateCost(NetworkCost_Unmetered); - break; - case ReachableViaWWAN: - strong_this->UpdateType(NetworkType_WWAN); - strong_this->UpdateCost(NetworkCost_Metered); - break; - } - }; - block(nil); // Update the initial status. + m_reach = [ODWReachability reachabilityForInternetConnection]; + void (^block)(NSNotification*) = ^(NSNotification*) + { + auto strong_this = weak_this.lock(); + if (!strong_this) + { + return; + } - if (m_isNetDetectEnabled) + // NetworkCost information is not available until iOS 12. + // Just make the best guess here. + switch (m_reach.currentReachabilityStatus) { - m_notificationId = - [[NSNotificationCenter defaultCenter] - addObserverForName: kNetworkReachabilityChangedNotification - object: nil - queue: nil - usingBlock: block]; - [m_reach startNotifier]; + case NotReachable: + strong_this->UpdateType(NetworkType_Unknown); + strong_this->UpdateCost(NetworkCost_Unknown); + break; + case ReachableViaWiFi: + strong_this->UpdateType(NetworkType_Wifi); + strong_this->UpdateCost(NetworkCost_Unmetered); + break; + case ReachableViaWWAN: + strong_this->UpdateType(NetworkType_WWAN); + strong_this->UpdateCost(NetworkCost_Metered); + break; } + }; + block(nil); // Update the initial status. + + if (m_isNetDetectEnabled) + { + m_notificationId = + [[NSNotificationCenter defaultCenter] + addObserverForName: kNetworkReachabilityChangedNotification + object: nil + queue: nil + usingBlock: block]; + [m_reach startNotifier]; + } + } +#endif + + void NetworkInformation::SetupNetDetect() + { +#if ODW_LEGACY_REACHABILITY_REQUIRED + if (@available(macOS 10.14, iOS 12.0, *)) + { + SetupModernNetDetect(); + } + else + { + SetupLegacyNetDetect(); } +#else + SetupModernNetDetect(); +#endif } void NetworkInformation::UpdateType(NetworkType type) noexcept @@ -229,4 +258,3 @@ virtual NetworkCost GetNetworkCost() } } PAL_NS_END - diff --git a/third_party/Reachability/ODWReachability.h b/third_party/Reachability/ODWReachability.h index b2d218417..57f3702f9 100644 --- a/third_party/Reachability/ODWReachability.h +++ b/third_party/Reachability/ODWReachability.h @@ -25,8 +25,10 @@ POSSIBILITY OF SUCH DAMAGE. */ +#import #import #import +#import /** @@ -40,6 +42,16 @@ extern NSString* const kNetworkReachabilityChangedNotification; +// Older Apple deployment targets still need the legacy SCNetworkReachability +// backend at runtime. Newer targets can compile directly to the modern path. +#if TARGET_OS_IPHONE +#define ODW_LEGACY_REACHABILITY_REQUIRED (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_12_0) +#elif TARGET_OS_OSX +#define ODW_LEGACY_REACHABILITY_REQUIRED (__MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_14) +#else +#define ODW_LEGACY_REACHABILITY_REQUIRED 0 +#endif + typedef NS_ENUM(NSInteger, ODWNetworkStatus) { // Apple NetworkStatus Compatible Names. NotReachable = 0, diff --git a/third_party/Reachability/ODWReachability.m b/third_party/Reachability/ODWReachability.m index 7947f7df0..76a040648 100644 --- a/third_party/Reachability/ODWReachability.m +++ b/third_party/Reachability/ODWReachability.m @@ -27,6 +27,8 @@ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF #import "ODWReachability.h" +#import + #import #import #import @@ -34,15 +36,42 @@ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF NSString *const kNetworkReachabilityChangedNotification = @"NetworkReachabilityChangedNotification"; +@class ODWReachability; + +@interface ODWReachabilityMonitorContext : NSObject + +#if __has_feature(objc_arc) +@property (nonatomic, weak) ODWReachability *owner; +#else +@property (nonatomic, unsafe_unretained) ODWReachability *owner; +#endif + +@end + +@implementation ODWReachabilityMonitorContext +@end + @interface ODWReachability () @property (nonatomic, assign) SCNetworkReachabilityRef reachabilityRef; @property (nonatomic, strong) dispatch_queue_t reachabilitySerialQueue; @property (nonatomic, strong) id reachabilityObject; +@property (nonatomic, strong) nw_path_monitor_t pathMonitor; +@property (nonatomic, strong) ODWReachabilityMonitorContext *pathMonitorContext; +@property (nonatomic, strong) dispatch_semaphore_t initialPathSemaphore; +@property (nonatomic, assign) nw_path_status_t currentPathStatus; +@property (nonatomic, assign) BOOL currentPathUsesWiFi; +@property (nonatomic, assign) BOOL currentPathUsesWWAN; +@property (nonatomic, assign) BOOL hasObservedPath; +@property (nonatomic, assign) BOOL monitorLocalWiFiOnly; -(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags; -(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags; +-(BOOL)ensureModernPathMonitor; +-(BOOL)awaitModernPathSnapshot; +-(void)handleModernPathUpdate:(nw_path_t)path; +-(void)notifyModernPathChange; @end @@ -62,9 +91,15 @@ -(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags; (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-', (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-', (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-', - (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-']; + (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-']; +} + +static BOOL ODWModernPathIsReachable(nw_path_status_t status) +{ + return status == nw_path_status_satisfied || status == nw_path_status_satisfiable; } +#if ODW_LEGACY_REACHABILITY_REQUIRED // Start listening for reachability notifications on the current run loop static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) { @@ -79,6 +114,7 @@ static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRea [reachability reachabilityChanged:flags]; } } +#endif @implementation ODWReachability @@ -99,23 +135,26 @@ +(ODWReachability*)reachabilityWithHostName:(NSString*)hostname +(instancetype)reachabilityWithHostname:(NSString*)hostname { +#if ODW_LEGACY_REACHABILITY_REQUIRED if (@available(macOS 10.14, iOS 12.0, *)) { - // Use URLSession for macOS 10.14 or higher +#endif + // Use Network.framework reachability for macOS 10.14 or higher. NSString *formattedHostname = hostname; if (![formattedHostname hasPrefix:@"https://"] && ![formattedHostname hasPrefix:@"http://"]) { formattedHostname = [NSString stringWithFormat:@"https://%@", hostname]; } NSURL *url = [NSURL URLWithString:formattedHostname]; + if (url == nil || url.host == nil) + { + NSLog(@"Invalid hostname"); + return nil; + } - NSURLSession *session = [NSURLSession sharedSession]; - __block ODWReachability *reachabilityInstance = [[self alloc] init]; + ODWReachability *reachabilityInstance = [[self alloc] init]; reachabilityInstance.url = url; - NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { - reachabilityInstance = [self handleReachabilityResponse:response error:error url:reachabilityInstance.url]; - }]; - [dataTask resume]; return reachabilityInstance; +#if ODW_LEGACY_REACHABILITY_REQUIRED } // Use SCNetworkReachability for macOS 10.14 or lower @@ -131,6 +170,7 @@ +(instancetype)reachabilityWithHostname:(NSString*)hostname } return nil; +#endif } +(ODWReachability *)reachabilityWithAddress:(void *)hostAddress @@ -140,19 +180,23 @@ +(ODWReachability *)reachabilityWithAddress:(void *)hostAddress return nil; } +#if ODW_LEGACY_REACHABILITY_REQUIRED if (@available(macOS 10.14, iOS 12.0, *)) { - // Use URLSession for macOS 10.14 or higher - NSString *addressString = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)hostAddress)->sin_addr)]; +#endif + // Use Network.framework reachability for macOS 10.14 or higher. + struct sockaddr_in *address = (struct sockaddr_in *)hostAddress; + if (address->sin_addr.s_addr == INADDR_ANY) + { + return [[self alloc] init]; + } + + NSString *addressString = [NSString stringWithUTF8String:inet_ntoa(address->sin_addr)]; NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"https://%@", addressString]]; - NSURLSession *session = [NSURLSession sharedSession]; - __block ODWReachability *reachabilityInstance = [[self alloc] init]; + ODWReachability *reachabilityInstance = [[self alloc] init]; reachabilityInstance.url = url; - NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { - reachabilityInstance = [self handleReachabilityResponse:response error:error url:reachabilityInstance.url]; - }]; - [dataTask resume]; - return reachabilityInstance; // Return the instance after resuming the data task + return reachabilityInstance; +#if ODW_LEGACY_REACHABILITY_REQUIRED } // Use SCNetworkReachability for macOS 10.14 or lower @@ -168,6 +212,7 @@ +(ODWReachability *)reachabilityWithAddress:(void *)hostAddress } return nil; +#endif } +(ODWReachability *)handleReachabilityResponse:(NSURLResponse *)response error:(NSError *)error url:(NSURL *)url @@ -199,6 +244,13 @@ +(ODWReachability *)handleReachabilityResponse:(NSURLResponse *)response error:( +(ODWReachability *)reachabilityForInternetConnection { +#if ODW_LEGACY_REACHABILITY_REQUIRED + if (@available(macOS 10.14, iOS 12.0, *)) + { + return [[self alloc] init]; + } +#endif + struct sockaddr_in zeroAddress; bzero(&zeroAddress, sizeof(zeroAddress)); zeroAddress.sin_len = sizeof(zeroAddress); @@ -209,6 +261,15 @@ +(ODWReachability *)reachabilityForInternetConnection +(ODWReachability*)reachabilityForLocalWiFi { +#if ODW_LEGACY_REACHABILITY_REQUIRED + if (@available(macOS 10.14, iOS 12.0, *)) + { + ODWReachability *reachability = [[self alloc] init]; + reachability.monitorLocalWiFiOnly = YES; + return reachability; + } +#endif + struct sockaddr_in localWifiAddress; bzero(&localWifiAddress, sizeof(localWifiAddress)); localWifiAddress.sin_len = sizeof(localWifiAddress); @@ -222,23 +283,147 @@ +(ODWReachability*)reachabilityForLocalWiFi // Initialization methods --(ODWReachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref +-(instancetype)init { self = [super init]; if (self != nil) { self.reachableOnWWAN = YES; - self.reachabilityRef = ref; + self.reachabilitySerialQueue = dispatch_queue_create("com.tonymillion.reachability", NULL); + self.currentPathStatus = nw_path_status_invalid; + } - // We need to create a serial queue. - // We allocate this once for the lifetime of the notifier. + return self; +} - self.reachabilitySerialQueue = dispatch_queue_create("com.tonymillion.reachability", NULL); +-(ODWReachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref +{ + self = [self init]; + if (self != nil) + { + self.reachabilityRef = ref; } return self; } +-(BOOL)ensureModernPathMonitor +{ + if (self.pathMonitor != nil) + { + return YES; + } + + self.hasObservedPath = NO; + self.currentPathStatus = nw_path_status_invalid; + self.currentPathUsesWiFi = NO; + self.currentPathUsesWWAN = NO; + self.initialPathSemaphore = dispatch_semaphore_create(0); + self.pathMonitor = self.monitorLocalWiFiOnly + ? nw_path_monitor_create_with_type(nw_interface_type_wifi) + : nw_path_monitor_create(); + + if (self.pathMonitor == nil) + { + return NO; + } + + ODWReachabilityMonitorContext *context = [[ODWReachabilityMonitorContext alloc] init]; + context.owner = self; + self.pathMonitorContext = context; +#if !__has_feature(objc_arc) + [context release]; +#endif + + nw_path_monitor_set_queue(self.pathMonitor, self.reachabilitySerialQueue); + nw_path_monitor_set_update_handler(self.pathMonitor, ^(nw_path_t path) { + ODWReachability *owner = context.owner; + if (owner == nil) + { + return; + } + + [owner handleModernPathUpdate:path]; + }); + nw_path_monitor_start(self.pathMonitor); + + return YES; +} + +-(BOOL)awaitModernPathSnapshot +{ + if (![self ensureModernPathMonitor]) + { + return NO; + } + + if (self.hasObservedPath) + { + return YES; + } + + if (self.initialPathSemaphore == nil) + { + return NO; + } + + // Avoid blocking reachability queries on the main thread before the first + // NWPathMonitor update arrives. Callers get a conservative "unknown yet" + // result until the async update handler records the first snapshot. + if ([NSThread isMainThread]) + { + return NO; + } + + long waitResult = dispatch_semaphore_wait( + self.initialPathSemaphore, + dispatch_time(DISPATCH_TIME_NOW, kTimeoutDurationInSeconds * NSEC_PER_SEC)); + return waitResult == 0 && self.hasObservedPath; +} + +-(void)handleModernPathUpdate:(nw_path_t)path +{ + self.currentPathStatus = nw_path_get_status(path); + self.currentPathUsesWiFi = nw_path_uses_interface_type(path, nw_interface_type_wifi); +#if TARGET_OS_IPHONE + self.currentPathUsesWWAN = nw_path_uses_interface_type(path, nw_interface_type_cellular); +#else + self.currentPathUsesWWAN = NO; +#endif + + BOOL firstPath = !self.hasObservedPath; + self.hasObservedPath = YES; + if (firstPath && self.initialPathSemaphore != nil) + { + dispatch_semaphore_signal(self.initialPathSemaphore); + } + + if (self.reachabilityObject == self) + { + [self notifyModernPathChange]; + } +} + +-(void)notifyModernPathChange +{ + if (ODWModernPathIsReachable(self.currentPathStatus)) + { + if (self.reachableBlock) + { + self.reachableBlock(self); + } + } + else if (self.unreachableBlock) + { + self.unreachableBlock(self); + } + + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:kNetworkReachabilityChangedNotification + object:self]; + }); +} + +(void)setTimeoutDurationInSeconds:(int)timeoutDuration { if (timeoutDuration >= kTimeoutDurationInSeconds) @@ -284,26 +469,22 @@ -(void)dealloc -(BOOL)startNotifier { +#if ODW_LEGACY_REACHABILITY_REQUIRED if (@available(macOS 10.14, iOS 12.0, *)) { - // Use URLSession for macOS 10.14 or higher - NSURLSession *session = [NSURLSession sharedSession]; - NSURLSessionDataTask *task = [session dataTaskWithURL:[self url] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { - if (error) { - NSLog(@"URLSession failed: %@", error.localizedDescription); - self.reachabilityObject = nil; - } else { - self.reachabilityObject = self; - [[NSNotificationCenter defaultCenter] postNotificationName:kNetworkReachabilityChangedNotification object:self]; +#endif + // Use NWPathMonitor for macOS 10.14 or higher. + if ([self ensureModernPathMonitor]) + { + self.reachabilityObject = self; + if ([self awaitModernPathSnapshot]) + { + [self notifyModernPathChange]; } - }]; - if (task) { - [task resume]; return YES; - } else { - NSLog(@"Failed to create URLSessionDataTask"); - return NO; } + return NO; +#if ODW_LEGACY_REACHABILITY_REQUIRED } // Use SCNetworkReachability for macOS 10.14 or lower @@ -346,14 +527,31 @@ -(BOOL)startNotifier // if we get here we fail at the internet self.reachabilityObject = nil; return NO; +#endif } -(void)stopNotifier { +#if ODW_LEGACY_REACHABILITY_REQUIRED if (@available(macOS 10.14, iOS 12.0, *)) { - // Use URLSession for macOS 10.14 or higher, no specific action is needed for URLSession +#endif + // Use NWPathMonitor for macOS 10.14 or higher. self.reachabilityObject = nil; + if (self.pathMonitor != nil) + { + self.pathMonitorContext.owner = nil; + nw_path_monitor_cancel(self.pathMonitor); + self.pathMonitor = nil; + } + self.pathMonitorContext = nil; + self.initialPathSemaphore = nil; + self.hasObservedPath = NO; + self.currentPathStatus = nw_path_status_invalid; + self.currentPathUsesWiFi = NO; + self.currentPathUsesWWAN = NO; +#if ODW_LEGACY_REACHABILITY_REQUIRED + return; } // Use SCNetworkReachability for macOS 10.14 or lower @@ -366,6 +564,7 @@ -(void)stopNotifier SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, NULL); #pragma clang diagnostic pop self.reachabilityObject = nil; +#endif } @@ -408,9 +607,12 @@ -(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags -(BOOL)isReachable { +#if ODW_LEGACY_REACHABILITY_REQUIRED if (@available(macOS 10.14, iOS 12.0, *)) { - return [self checkNetworkReachability:true]; +#endif + return [self awaitModernPathSnapshot] && ODWModernPathIsReachable(self.currentPathStatus); +#if ODW_LEGACY_REACHABILITY_REQUIRED } // for macOS 10.14 or lower @@ -423,12 +625,21 @@ -(BOOL)isReachable #pragma clang diagnostic pop return [self isReachableWithFlags:flags]; +#endif } -(BOOL)isReachableViaWWAN { #if TARGET_OS_IPHONE + BOOL modernWWANReachable = [self awaitModernPathSnapshot] && + ODWModernPathIsReachable(self.currentPathStatus) && + self.currentPathUsesWWAN; +#if ODW_LEGACY_REACHABILITY_REQUIRED + if (@available(macOS 10.14, iOS 12.0, *)) + { + return modernWWANReachable; + } SCNetworkReachabilityFlags flags = 0; @@ -444,16 +655,38 @@ -(BOOL)isReachableViaWWAN } } } -#endif return NO; +#endif +#if !ODW_LEGACY_REACHABILITY_REQUIRED + return modernWWANReachable; +#endif +#else + return NO; +#endif } -(BOOL)isReachableViaWiFi { +#if ODW_LEGACY_REACHABILITY_REQUIRED if (@available(macOS 10.14, iOS 12.0, *)) { - return [self checkNetworkReachability:true]; +#endif + if (![self awaitModernPathSnapshot] || !ODWModernPathIsReachable(self.currentPathStatus)) + { + return NO; + } +#if TARGET_OS_IPHONE + if (self.monitorLocalWiFiOnly) + { + return self.currentPathUsesWiFi; + } + + return !self.currentPathUsesWWAN; +#else + return self.monitorLocalWiFiOnly ? self.currentPathUsesWiFi : YES; +#endif +#if ODW_LEGACY_REACHABILITY_REQUIRED } // for macOS 10.14 or lower @@ -479,6 +712,7 @@ -(BOOL)isReachableViaWiFi } return NO; +#endif } @@ -491,9 +725,13 @@ -(BOOL)isConnectionRequired -(BOOL)connectionRequired { +#if ODW_LEGACY_REACHABILITY_REQUIRED if (@available(macOS 10.14, iOS 12.0, *)) { - return [self checkNetworkReachability:false]; +#endif + return [self awaitModernPathSnapshot] && + self.currentPathStatus == nw_path_status_satisfiable; +#if ODW_LEGACY_REACHABILITY_REQUIRED } // for macOS 10.14 or lower @@ -508,15 +746,19 @@ -(BOOL)connectionRequired } return NO; +#endif } // Dynamic, on demand connection? -(BOOL)isConnectionOnDemand { +#if ODW_LEGACY_REACHABILITY_REQUIRED if (@available(macOS 10.14, iOS 12.0, *)) { - return [self checkNetworkReachability:true]; +#endif + return NO; +#if ODW_LEGACY_REACHABILITY_REQUIRED } // for macOS 10.14 or lower @@ -532,15 +774,19 @@ -(BOOL)isConnectionOnDemand } return NO; +#endif } // Is user intervention required? -(BOOL)isInterventionRequired { +#if ODW_LEGACY_REACHABILITY_REQUIRED if (@available(macOS 10.14, iOS 12.0, *)) { - return [self checkNetworkReachability:false]; +#endif + return NO; +#if ODW_LEGACY_REACHABILITY_REQUIRED } // for macOS 10.14 or lower @@ -556,6 +802,7 @@ -(BOOL)isInterventionRequired } return NO; +#endif } @@ -579,23 +826,32 @@ -(ODWNetworkStatus)currentReachabilityStatus -(SCNetworkReachabilityFlags)reachabilityFlags { +#if ODW_LEGACY_REACHABILITY_REQUIRED if (@available(macOS 10.14, iOS 12.0, *)) { - __block SCNetworkReachabilityFlags flags = 0; - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - - NSURLSession *session = [NSURLSession sharedSession]; - NSURLSessionDataTask *task = [session dataTaskWithURL:[self url] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { - if (error == nil && data != nil) { - flags = kSCNetworkReachabilityFlagsReachable; +#endif + if (![self awaitModernPathSnapshot]) + { + return 0; } - dispatch_semaphore_signal(semaphore); - }]; - - [task resume]; - dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, kTimeoutDurationInSeconds * NSEC_PER_SEC)); - + + SCNetworkReachabilityFlags flags = 0; + if (ODWModernPathIsReachable(self.currentPathStatus)) + { + flags |= kSCNetworkReachabilityFlagsReachable; + } + if (self.currentPathStatus == nw_path_status_satisfiable) + { + flags |= kSCNetworkReachabilityFlagsConnectionRequired; + } +#if TARGET_OS_IPHONE + if (self.currentPathUsesWWAN) + { + flags |= kSCNetworkReachabilityFlagsIsWWAN; + } +#endif return flags; +#if ODW_LEGACY_REACHABILITY_REQUIRED } // for macOS 10.14 or lower @@ -610,6 +866,7 @@ -(SCNetworkReachabilityFlags)reachabilityFlags } return 0; +#endif } -(NSString*)currentReachabilityString