-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Replace deprecated NetworkReachability with NWPathMonitor on iOS/macOS #32354
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
42c6985
04e9faa
d191b18
d63d96c
e26987f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,12 +1,11 @@ | ||||||||||||||||||||||||||||||
| using System; | ||||||||||||||||||||||||||||||
| using System.Collections.Generic; | ||||||||||||||||||||||||||||||
| using System.Net; | ||||||||||||||||||||||||||||||
| using System.Threading.Tasks; | ||||||||||||||||||||||||||||||
| #if !(MACCATALYST || MACOS) | ||||||||||||||||||||||||||||||
| using CoreTelephony; | ||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||
| using CoreFoundation; | ||||||||||||||||||||||||||||||
| using SystemConfiguration; | ||||||||||||||||||||||||||||||
| using Network; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| namespace Microsoft.Maui.Networking | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
|
|
@@ -19,144 +18,111 @@ enum NetworkStatus | |||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| static class Reachability | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| internal const string HostName = "www.microsoft.com"; | ||||||||||||||||||||||||||||||
| static NWPathMonitor sharedMonitor; | ||||||||||||||||||||||||||||||
| static readonly object monitorLock = new object(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| internal static NetworkStatus RemoteHostStatus() | ||||||||||||||||||||||||||||||
| static NWPathMonitor SharedMonitor | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| using (var remoteHostReachability = new NetworkReachability(HostName)) | ||||||||||||||||||||||||||||||
| get | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| var reachable = remoteHostReachability.TryGetFlags(out var flags); | ||||||||||||||||||||||||||||||
| lock (monitorLock) | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| if (sharedMonitor == null) | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| sharedMonitor = new NWPathMonitor(); | ||||||||||||||||||||||||||||||
| sharedMonitor.SetQueue(DispatchQueue.DefaultGlobalQueue); | ||||||||||||||||||||||||||||||
| sharedMonitor.Start(); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| return sharedMonitor; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if (!reachable) | ||||||||||||||||||||||||||||||
| return NetworkStatus.NotReachable; | ||||||||||||||||||||||||||||||
| static NWPath GetCurrentPath() | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| return SharedMonitor?.CurrentPath; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if (!IsReachableWithoutRequiringConnection(flags)) | ||||||||||||||||||||||||||||||
| return NetworkStatus.NotReachable; | ||||||||||||||||||||||||||||||
| internal static NetworkStatus RemoteHostStatus() | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| var path = GetCurrentPath(); | ||||||||||||||||||||||||||||||
| if (path == null || path.Status != NWPathStatus.Satisfied) | ||||||||||||||||||||||||||||||
| return NetworkStatus.NotReachable; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| #if __IOS__ | ||||||||||||||||||||||||||||||
| if ((flags & NetworkReachabilityFlags.IsWWAN) != 0) | ||||||||||||||||||||||||||||||
| return NetworkStatus.ReachableViaCarrierDataNetwork; | ||||||||||||||||||||||||||||||
| if (path.UsesInterfaceType(NWInterfaceType.Cellular)) | ||||||||||||||||||||||||||||||
| return NetworkStatus.ReachableViaCarrierDataNetwork; | ||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return NetworkStatus.ReachableViaWiFiNetwork; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| return NetworkStatus.ReachableViaWiFiNetwork; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| internal static NetworkStatus InternetConnectionStatus() | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| var status = NetworkStatus.NotReachable; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| var defaultNetworkAvailable = IsNetworkAvailable(out var flags); | ||||||||||||||||||||||||||||||
| var path = GetCurrentPath(); | ||||||||||||||||||||||||||||||
| if (path == null || path.Status != NWPathStatus.Satisfied) | ||||||||||||||||||||||||||||||
| return NetworkStatus.NotReachable; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| #if __IOS__ | ||||||||||||||||||||||||||||||
| // If it's a WWAN connection.. | ||||||||||||||||||||||||||||||
| if ((flags & NetworkReachabilityFlags.IsWWAN) != 0) | ||||||||||||||||||||||||||||||
| status = NetworkStatus.ReachableViaCarrierDataNetwork; | ||||||||||||||||||||||||||||||
| if (path.UsesInterfaceType(NWInterfaceType.Cellular)) | ||||||||||||||||||||||||||||||
| return NetworkStatus.ReachableViaCarrierDataNetwork; | ||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // If the connection is reachable and no connection is required, then assume it's WiFi | ||||||||||||||||||||||||||||||
| if (defaultNetworkAvailable) | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| status = NetworkStatus.ReachableViaWiFiNetwork; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // If the connection is on-demand or on-traffic and no user intervention | ||||||||||||||||||||||||||||||
| // is required, then assume WiFi. | ||||||||||||||||||||||||||||||
| if (((flags & NetworkReachabilityFlags.ConnectionOnDemand) != 0 || (flags & NetworkReachabilityFlags.ConnectionOnTraffic) != 0) && | ||||||||||||||||||||||||||||||
| (flags & NetworkReachabilityFlags.InterventionRequired) == 0) | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| status = NetworkStatus.ReachableViaWiFiNetwork; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return status; | ||||||||||||||||||||||||||||||
| return NetworkStatus.ReachableViaWiFiNetwork; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| internal static IEnumerable<NetworkStatus> GetActiveConnectionType() | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| var status = new List<NetworkStatus>(); | ||||||||||||||||||||||||||||||
| var path = GetCurrentPath(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| var defaultNetworkAvailable = IsNetworkAvailable(out var flags); | ||||||||||||||||||||||||||||||
| if (path == null || path.Status != NWPathStatus.Satisfied) | ||||||||||||||||||||||||||||||
| return status; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| #if __IOS__ | ||||||||||||||||||||||||||||||
| // If it's a WWAN connection. | ||||||||||||||||||||||||||||||
| if ((flags & NetworkReachabilityFlags.IsWWAN) != 0) | ||||||||||||||||||||||||||||||
| if (path.UsesInterfaceType(NWInterfaceType.Cellular)) | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| status.Add(NetworkStatus.ReachableViaCarrierDataNetwork); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| else if (defaultNetworkAvailable) | ||||||||||||||||||||||||||||||
| else if (path.UsesInterfaceType(NWInterfaceType.Wifi) || path.UsesInterfaceType(NWInterfaceType.Wired)) | ||||||||||||||||||||||||||||||
| #else | ||||||||||||||||||||||||||||||
| // If the connection is reachable and no connection is required, then assume it's WiFi | ||||||||||||||||||||||||||||||
| if (defaultNetworkAvailable) | ||||||||||||||||||||||||||||||
| if (path.UsesInterfaceType(NWInterfaceType.Wifi) || path.UsesInterfaceType(NWInterfaceType.Wired)) | ||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| status.Add(NetworkStatus.ReachableViaWiFiNetwork); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| else if (((flags & NetworkReachabilityFlags.ConnectionOnDemand) != 0 || (flags & NetworkReachabilityFlags.ConnectionOnTraffic) != 0) && | ||||||||||||||||||||||||||||||
| (flags & NetworkReachabilityFlags.InterventionRequired) == 0) | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| // If the connection is on-demand or on-traffic and no user intervention | ||||||||||||||||||||||||||||||
| // is required, then assume WiFi. | ||||||||||||||||||||||||||||||
| status.Add(NetworkStatus.ReachableViaWiFiNetwork); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return status; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| internal static bool IsNetworkAvailable(out NetworkReachabilityFlags flags) | ||||||||||||||||||||||||||||||
| internal static bool IsNetworkAvailable() | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| var ip = new IPAddress(0); | ||||||||||||||||||||||||||||||
| using (var defaultRouteReachability = new NetworkReachability(ip)) | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| if (!defaultRouteReachability.TryGetFlags(out flags)) | ||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return IsReachableWithoutRequiringConnection(flags); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| internal static bool IsReachableWithoutRequiringConnection(NetworkReachabilityFlags flags) | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| // Is it reachable with the current network configuration? | ||||||||||||||||||||||||||||||
| var isReachable = (flags & NetworkReachabilityFlags.Reachable) != 0; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Do we need a connection to reach it? | ||||||||||||||||||||||||||||||
| var noConnectionRequired = (flags & NetworkReachabilityFlags.ConnectionRequired) == 0; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| #if __IOS__ | ||||||||||||||||||||||||||||||
| // Since the network stack will automatically try to get the WAN up, | ||||||||||||||||||||||||||||||
| // probe that | ||||||||||||||||||||||||||||||
| if ((flags & NetworkReachabilityFlags.IsWWAN) != 0) | ||||||||||||||||||||||||||||||
| noConnectionRequired = true; | ||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return isReachable && noConnectionRequired; | ||||||||||||||||||||||||||||||
| var path = GetCurrentPath(); | ||||||||||||||||||||||||||||||
| return path != null && path.Status == NWPathStatus.Satisfied; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| class ReachabilityListener : IDisposable | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| NetworkReachability defaultRouteReachability; | ||||||||||||||||||||||||||||||
| NetworkReachability remoteHostReachability; | ||||||||||||||||||||||||||||||
| // Delay to allow connection status to stabilize before notifying listeners | ||||||||||||||||||||||||||||||
| const int ConnectionStatusChangeDelayMs = 100; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| NWPathMonitor pathMonitor; | ||||||||||||||||||||||||||||||
| Action<NWPath> pathUpdateHandler; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| internal ReachabilityListener() | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| var ip = new IPAddress(0); | ||||||||||||||||||||||||||||||
| defaultRouteReachability = new NetworkReachability(ip); | ||||||||||||||||||||||||||||||
| #pragma warning disable CA1422 // obsolete in MacCatalyst 15, iOS 13 | ||||||||||||||||||||||||||||||
| defaultRouteReachability.SetNotification(OnChange); | ||||||||||||||||||||||||||||||
| defaultRouteReachability.Schedule(CFRunLoop.Main, CFRunLoop.ModeDefault); | ||||||||||||||||||||||||||||||
| #pragma warning restore CA1422 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| remoteHostReachability = new NetworkReachability(Reachability.HostName); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Need to probe before we queue, or we wont get any meaningful values | ||||||||||||||||||||||||||||||
| // this only happens when you create NetworkReachability from a hostname | ||||||||||||||||||||||||||||||
| remoteHostReachability.TryGetFlags(out var flags); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| #pragma warning disable CA1422 // obsolete in MacCatalyst 15, iOS 13 | ||||||||||||||||||||||||||||||
| remoteHostReachability.SetNotification(OnChange); | ||||||||||||||||||||||||||||||
| remoteHostReachability.Schedule(CFRunLoop.Main, CFRunLoop.ModeDefault); | ||||||||||||||||||||||||||||||
| #pragma warning restore CA1422 | ||||||||||||||||||||||||||||||
| pathMonitor = new NWPathMonitor(); | ||||||||||||||||||||||||||||||
| pathUpdateHandler = async (NWPath path) => | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| // Add in artificial delay so the connection status has time to change | ||||||||||||||||||||||||||||||
| await Task.Delay(ConnectionStatusChangeDelayMs); | ||||||||||||||||||||||||||||||
| ReachabilityChanged?.Invoke(); | ||||||||||||||||||||||||||||||
|
Comment on lines
+138
to
+140
|
||||||||||||||||||||||||||||||
| // Add in artificial delay so the connection status has time to change | |
| await Task.Delay(ConnectionStatusChangeDelayMs); | |
| ReachabilityChanged?.Invoke(); | |
| try | |
| { | |
| // Add in artificial delay so the connection status has time to change | |
| await Task.Delay(ConnectionStatusChangeDelayMs); | |
| ReachabilityChanged?.Invoke(); | |
| } | |
| catch (Exception ex) | |
| { | |
| // Optionally log the exception, or ignore | |
| System.Diagnostics.Debug.WriteLine($"Exception in ReachabilityListener pathUpdateHandler: {ex}"); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of creating a Action it would be ideal to have a method instead. It can be async void if needed here as there is an awaited Task.Delay
Uh oh!
There was an error while loading. Please reload this page.