diff --git a/Xamarin.Essentials/Connectivity/Connectivity.ios.cs b/Xamarin.Essentials/Connectivity/Connectivity.ios.cs index 62067d5..e52d2df 100644 --- a/Xamarin.Essentials/Connectivity/Connectivity.ios.cs +++ b/Xamarin.Essentials/Connectivity/Connectivity.ios.cs @@ -1,36 +1,40 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; +using System.Collections.Generic; namespace Xamarin.Essentials { public static partial class Connectivity { - static void StartListeners() => - Reachability.ReachabilityChanged += ReachabilityChanged; + static ReachabilityListener listener; - static async void ReachabilityChanged(object sender, EventArgs e) + static void StartListeners() { - await Task.Delay(100); - OnConnectivityChanged(); + listener = new ReachabilityListener(); + listener.ReachabilityChanged += OnConnectivityChanged; } - static void StopListeners() => - Reachability.ReachabilityChanged -= ReachabilityChanged; + static void StopListeners() + { + if (listener == null) + return; + + listener.ReachabilityChanged -= OnConnectivityChanged; + listener.Dispose(); + listener = null; + } public static NetworkAccess NetworkAccess { get { - var remoteHostStatus = Reachability.RemoteHostStatus(); var internetStatus = Reachability.InternetConnectionStatus(); + if (internetStatus == NetworkStatus.ReachableViaCarrierDataNetwork || internetStatus == NetworkStatus.ReachableViaWiFiNetwork) + return NetworkAccess.Internet; - var isConnected = (internetStatus == NetworkStatus.ReachableViaCarrierDataNetwork || - internetStatus == NetworkStatus.ReachableViaWiFiNetwork) || - (remoteHostStatus == NetworkStatus.ReachableViaCarrierDataNetwork || - remoteHostStatus == NetworkStatus.ReachableViaWiFiNetwork); + var remoteHostStatus = Reachability.RemoteHostStatus(); + if (remoteHostStatus == NetworkStatus.ReachableViaCarrierDataNetwork || remoteHostStatus == NetworkStatus.ReachableViaWiFiNetwork) + return NetworkAccess.Internet; - return isConnected ? NetworkAccess.Internet : NetworkAccess.None; + return NetworkAccess.None; } } diff --git a/Xamarin.Essentials/Connectivity/Connectivity.ios.reachability.cs b/Xamarin.Essentials/Connectivity/Connectivity.ios.reachability.cs index 6346ff2..93df7c5 100644 --- a/Xamarin.Essentials/Connectivity/Connectivity.ios.reachability.cs +++ b/Xamarin.Essentials/Connectivity/Connectivity.ios.reachability.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Net; using System.Threading.Tasks; using CoreFoundation; @@ -17,174 +16,27 @@ namespace Xamarin.Essentials static class Reachability { - static string hostName = "www.microsoft.com"; - - /// - /// Checks if reachable without requiring a connection - /// - /// - /// - 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; - - // Since the network stack will automatically try to get the WAN up, - // probe that - if ((flags & NetworkReachabilityFlags.IsWWAN) != 0) - noConnectionRequired = true; - - return isReachable && noConnectionRequired; - } - - /// - /// Checks if host is reachable - /// - /// - /// - /// - internal static bool IsHostReachable(string host, int port) - { - if (string.IsNullOrWhiteSpace(host)) - return false; - - if (!IPAddress.TryParse(host + ":" + port, out var address)) - { - Debug.WriteLine(host + ":" + port + " is not valid"); - return false; - } - using (var r = new NetworkReachability(host)) - { - if (r.TryGetFlags(out var flags)) - { - return IsReachableWithoutRequiringConnection(flags); - } - } - return false; - } - - /// - /// Is the host reachable with the current network configuration - /// - /// - /// - internal static bool IsHostReachable(string host) - { - if (string.IsNullOrWhiteSpace(host)) - return false; - - using (var r = new NetworkReachability(host)) - { - if (r.TryGetFlags(out var flags)) - { - return IsReachableWithoutRequiringConnection(flags); - } - } - return false; - } - - /// - /// Raised every time there is an interesting reachable event, - /// we do not even pass the info as to what changed, and - /// we lump all three status we probe into one - /// - internal static event EventHandler ReachabilityChanged; - - static async void OnChange(NetworkReachabilityFlags flags) - { - await Task.Delay(100); - ReachabilityChanged?.Invoke(null, EventArgs.Empty); - } - - static NetworkReachability defaultRouteReachability; - - static bool IsNetworkAvailable(out NetworkReachabilityFlags flags) - { - if (defaultRouteReachability == null) - { - var ip = new IPAddress(0); - defaultRouteReachability = new NetworkReachability(ip); - defaultRouteReachability.SetNotification(OnChange); - defaultRouteReachability.Schedule(CFRunLoop.Main, CFRunLoop.ModeDefault); - } - if (!defaultRouteReachability.TryGetFlags(out flags)) - return false; - return IsReachableWithoutRequiringConnection(flags); - } - - static NetworkReachability remoteHostReachability; + internal const string HostName = "www.microsoft.com"; internal static NetworkStatus RemoteHostStatus() { - NetworkReachabilityFlags flags; - bool reachable; - - if (remoteHostReachability == null) + using (var remoteHostReachability = new NetworkReachability(HostName)) { - remoteHostReachability = new NetworkReachability(hostName); + var reachable = remoteHostReachability.TryGetFlags(out var flags); - // Need to probe before we queue, or we wont get any meaningful values - // this only happens when you create NetworkReachability from a hostname - reachable = remoteHostReachability.TryGetFlags(out flags); + if (!reachable) + return NetworkStatus.NotReachable; - remoteHostReachability.SetNotification(OnChange); - remoteHostReachability.Schedule(CFRunLoop.Main, CFRunLoop.ModeDefault); + if (!IsReachableWithoutRequiringConnection(flags)) + return NetworkStatus.NotReachable; + + if ((flags & NetworkReachabilityFlags.IsWWAN) != 0) + return NetworkStatus.ReachableViaCarrierDataNetwork; + + return NetworkStatus.ReachableViaWiFiNetwork; } - else - { - reachable = remoteHostReachability.TryGetFlags(out flags); - } - - if (!reachable) - return NetworkStatus.NotReachable; - - if (!IsReachableWithoutRequiringConnection(flags)) - return NetworkStatus.NotReachable; - - if ((flags & NetworkReachabilityFlags.IsWWAN) != 0) - return NetworkStatus.ReachableViaCarrierDataNetwork; - - return NetworkStatus.ReachableViaWiFiNetwork; } - /// - /// Checks internet connection status - /// - /// - internal static IEnumerable GetActiveConnectionType() - { - var status = new List(); - - var defaultNetworkAvailable = IsNetworkAvailable(out var flags); - - // If it's a WWAN connection.. - if ((flags & NetworkReachabilityFlags.IsWWAN) != 0) - { - status.Add(NetworkStatus.ReachableViaCarrierDataNetwork); - } - else if (defaultNetworkAvailable) - { - 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; - } - - /// - /// Checks internet connection status - /// - /// public static NetworkStatus InternetConnectionStatus() { var status = NetworkStatus.NotReachable; @@ -203,9 +55,8 @@ namespace Xamarin.Essentials // 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) + if (((flags & NetworkReachabilityFlags.ConnectionOnDemand) != 0 || (flags & NetworkReachabilityFlags.ConnectionOnTraffic) != 0) && + (flags & NetworkReachabilityFlags.InterventionRequired) == 0) { status = NetworkStatus.ReachableViaWiFiNetwork; } @@ -213,22 +64,100 @@ namespace Xamarin.Essentials return status; } - /// - /// Dispose - /// - internal static void Dispose() + internal static IEnumerable GetActiveConnectionType() { - if (remoteHostReachability != null) + var status = new List(); + + var defaultNetworkAvailable = IsNetworkAvailable(out var flags); + + // If it's a WWAN connection.. + if ((flags & NetworkReachabilityFlags.IsWWAN) != 0) { - remoteHostReachability.Dispose(); - remoteHostReachability = null; + status.Add(NetworkStatus.ReachableViaCarrierDataNetwork); + } + else if (defaultNetworkAvailable) + { + 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); } - if (defaultRouteReachability != null) + return status; + } + + internal static bool IsNetworkAvailable(out NetworkReachabilityFlags flags) + { + var ip = new IPAddress(0); + using (var defaultRouteReachability = new NetworkReachability(ip)) { - defaultRouteReachability.Dispose(); - defaultRouteReachability = null; + 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; + + // Since the network stack will automatically try to get the WAN up, + // probe that + if ((flags & NetworkReachabilityFlags.IsWWAN) != 0) + noConnectionRequired = true; + + return isReachable && noConnectionRequired; + } + } + + class ReachabilityListener : IDisposable + { + NetworkReachability defaultRouteReachability; + NetworkReachability remoteHostReachability; + + public ReachabilityListener() + { + var ip = new IPAddress(0); + defaultRouteReachability = new NetworkReachability(ip); + defaultRouteReachability.SetNotification(OnChange); + defaultRouteReachability.Schedule(CFRunLoop.Main, CFRunLoop.ModeDefault); + + 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); + + remoteHostReachability.SetNotification(OnChange); + remoteHostReachability.Schedule(CFRunLoop.Main, CFRunLoop.ModeDefault); + } + + public event Action ReachabilityChanged; + + public void Dispose() + { + defaultRouteReachability?.Dispose(); + defaultRouteReachability = null; + remoteHostReachability?.Dispose(); + remoteHostReachability = null; + } + + async void OnChange(NetworkReachabilityFlags flags) + { + // Add in artifical delay so the connection status has time to change + // else it will return true no matter what. + await Task.Delay(100); + + ReachabilityChanged?.Invoke(); + } } }