initial commit, iOS network native library
This commit is contained in:
195
Network/Platforms/iOS/PlatformConnectivity.cs
Normal file
195
Network/Platforms/iOS/PlatformConnectivity.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net;
|
||||
using CoreFoundation;
|
||||
using CoreTelephony;
|
||||
using SystemConfiguration;
|
||||
|
||||
namespace Blahblah.Library.Network;
|
||||
|
||||
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>")]
|
||||
partial class Connectivity
|
||||
{
|
||||
static readonly Lazy<CTCellularData> cellularData = new(() => new CTCellularData());
|
||||
|
||||
static ReachabilityListener? listener;
|
||||
|
||||
private static partial NetworkStatus PlatformGetStatus()
|
||||
{
|
||||
var restricted = cellularData.Value.RestrictedState == CTCellularDataRestrictedState.Restricted;
|
||||
|
||||
var internetStatus = InternetConnectionStatus();
|
||||
if (internetStatus == NetworkStatus.ReachableViaCarrierDataNetwork && !restricted)
|
||||
{
|
||||
return NetworkStatus.ReachableViaCarrierDataNetwork;
|
||||
}
|
||||
if (internetStatus == NetworkStatus.ReachableViaWiFiNetwork)
|
||||
{
|
||||
return NetworkStatus.ReachableViaWiFiNetwork;
|
||||
}
|
||||
|
||||
var remoteStatus = RemoteHostStatus();
|
||||
if (remoteStatus == NetworkStatus.ReachableViaCarrierDataNetwork && !restricted)
|
||||
{
|
||||
return NetworkStatus.ReachableViaCarrierDataNetwork;
|
||||
}
|
||||
if (remoteStatus == NetworkStatus.ReachableViaWiFiNetwork)
|
||||
{
|
||||
return NetworkStatus.ReachableViaWiFiNetwork;
|
||||
}
|
||||
|
||||
return NetworkStatus.NotReachable;
|
||||
}
|
||||
|
||||
static NetworkStatus RemoteHostStatus()
|
||||
{
|
||||
using var remote = new NetworkReachability(hostName);
|
||||
|
||||
if (!remote.TryGetFlags(out var flags))
|
||||
{
|
||||
return NetworkStatus.NotReachable;
|
||||
}
|
||||
|
||||
if (!IsReachableWithoutRequiringConnection(flags))
|
||||
{
|
||||
return NetworkStatus.NotReachable;
|
||||
}
|
||||
|
||||
if ((flags & NetworkReachabilityFlags.IsWWAN) != 0)
|
||||
{
|
||||
return NetworkStatus.ReachableViaCarrierDataNetwork;
|
||||
}
|
||||
|
||||
return NetworkStatus.ReachableViaWiFiNetwork;
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static NetworkStatus InternetConnectionStatus()
|
||||
{
|
||||
var status = NetworkStatus.NotReachable;
|
||||
|
||||
var defaultNetworkAvailable = IsNetworkAvailable(out var flags);
|
||||
|
||||
// If the connection is reachable and no connection is required, then assume it's WiFi
|
||||
if (defaultNetworkAvailable)
|
||||
{
|
||||
status = NetworkStatus.ReachableViaWiFiNetwork;
|
||||
}
|
||||
else if ((flags & NetworkReachabilityFlags.IsWWAN) != 0)
|
||||
{
|
||||
// If it's a WWAN connection..
|
||||
status = NetworkStatus.ReachableViaCarrierDataNetwork;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
private static bool IsNetworkAvailable(out NetworkReachabilityFlags flags)
|
||||
{
|
||||
var ip = new IPAddress(0);
|
||||
using var route = new NetworkReachability(ip);
|
||||
|
||||
if (!route.TryGetFlags(out flags))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsReachableWithoutRequiringConnection(flags);
|
||||
}
|
||||
|
||||
private static partial void StartListeners()
|
||||
{
|
||||
StopListeners();
|
||||
|
||||
listener = new ReachabilityListener();
|
||||
listener.ReachabilityChanged += OnConnectivityChanged;
|
||||
}
|
||||
|
||||
private static partial void StopListeners()
|
||||
{
|
||||
if (listener == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
listener.ReachabilityChanged -= OnConnectivityChanged;
|
||||
listener.Dispose();
|
||||
listener = null;
|
||||
}
|
||||
|
||||
class ReachabilityListener : IDisposable
|
||||
{
|
||||
NetworkReachability? routeReachability;
|
||||
NetworkReachability? remoteReachability;
|
||||
|
||||
public event Action? ReachabilityChanged;
|
||||
|
||||
public ReachabilityListener()
|
||||
{
|
||||
var ip = new IPAddress(0);
|
||||
routeReachability = new NetworkReachability(ip);
|
||||
routeReachability.SetNotification(OnChange);
|
||||
routeReachability.Schedule(CFRunLoop.Main, CFRunLoop.ModeDefault);
|
||||
|
||||
remoteReachability = new NetworkReachability(hostName);
|
||||
|
||||
// Need to probe before we queue, or we wont get any meaningful values
|
||||
// this only happens when you create NetworkReachability from a hostname
|
||||
remoteReachability.TryGetFlags(out var flags);
|
||||
|
||||
remoteReachability.SetNotification(OnChange);
|
||||
remoteReachability.Schedule(CFRunLoop.Main, CFRunLoop.ModeDefault);
|
||||
|
||||
cellularData.Value.RestrictionDidUpdateNotifier = new(OnRestrictedStateChanged);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
routeReachability?.Dispose();
|
||||
routeReachability = null;
|
||||
remoteReachability?.Dispose();
|
||||
remoteReachability = null;
|
||||
|
||||
cellularData.Value.RestrictionDidUpdateNotifier = null;
|
||||
}
|
||||
|
||||
void OnRestrictedStateChanged(CTCellularDataRestrictedState state)
|
||||
{
|
||||
ReachabilityChanged?.Invoke();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user