From 122f9cc800ea9ff3b5ddaecdf86db2ac1f625239 Mon Sep 17 00:00:00 2001 From: KarthikRajaKalaimani <92777139+KarthikRajaKalaimani@users.noreply.github.com> Date: Tue, 20 May 2025 17:27:10 +0530 Subject: [PATCH 1/4] Update Connectivity.ios.tvos.macos.reachability.cs Fixed Connectivity.ConnectivityChanged not fired on iOS --- .../Connectivity.ios.tvos.macos.reachability.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs b/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs index 376fcc6f5d49..8de30bcaf2b8 100644 --- a/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs +++ b/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs @@ -166,5 +166,14 @@ void OnRestrictedStateChanged(CTCellularDataRestrictedState state) } #pragma warning restore BI1234 #endif + + 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(400); + + ReachabilityChanged?.Invoke(); + } } } From bb4bd04ad5930c4c051608fb4ad010c3cf573be8 Mon Sep 17 00:00:00 2001 From: KarthikRajaKalaimani <92777139+KarthikRajaKalaimani@users.noreply.github.com> Date: Mon, 23 Jun 2025 15:45:13 +0530 Subject: [PATCH 2/4] Modified the fix. --- ...onnectivity.ios.tvos.macos.reachability.cs | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs b/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs index 8de30bcaf2b8..cdb708f2e2fb 100644 --- a/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs +++ b/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs @@ -169,10 +169,24 @@ void OnRestrictedStateChanged(CTCellularDataRestrictedState state) 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(400); - + // This function waits up to 1 second, checking the device’s network status every 100 milliseconds. + // If the network status changes, it immediately triggers the ReachabilityChanged event. + var initialAccess = Connectivity.NetworkAccess; + const int pollingIntervalMs = 100; + const int maxWaitTimeMs = 1000; + int elapsedTime = 0; + + while (elapsedTime < maxWaitTimeMs) + { + await Task.Delay(pollingIntervalMs); + elapsedTime += pollingIntervalMs; + var currentAccess = Connectivity.NetworkAccess; + if (currentAccess != initialAccess) + { + ReachabilityChanged?.Invoke(); + return; + } + } ReachabilityChanged?.Invoke(); } } From 027242ab8fc62c66c4c36b2dc5235d8678f14900 Mon Sep 17 00:00:00 2001 From: KarthikRajaKalaimani <92777139+KarthikRajaKalaimani@users.noreply.github.com> Date: Tue, 24 Mar 2026 13:25:58 +0530 Subject: [PATCH 3/4] Resolved the build error --- .../src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs b/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs index cdb708f2e2fb..514b90f2ad89 100644 --- a/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs +++ b/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs @@ -7,6 +7,7 @@ #endif using CoreFoundation; using Network; +using SystemConfiguration; namespace Microsoft.Maui.Networking { From 0360028f1714d164ebb089dfe7732ec8467b03c1 Mon Sep 17 00:00:00 2001 From: KarthikRajaKalaimani <92777139+KarthikRajaKalaimani@users.noreply.github.com> Date: Tue, 24 Mar 2026 13:41:10 +0530 Subject: [PATCH 4/4] fix improved based on the suggestion --- ...onnectivity.ios.tvos.macos.reachability.cs | 44 +++++++++++++++---- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs b/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs index 514b90f2ad89..d86796c2e8dc 100644 --- a/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs +++ b/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs @@ -109,6 +109,8 @@ class ReachabilityListener : IDisposable const int ConnectionStatusChangeDelayMs = 100; NWPathMonitor pathMonitor; + CancellationTokenSource cts = new CancellationTokenSource(); + int pendingCallbacks; internal ReachabilityListener() { @@ -130,6 +132,10 @@ internal ReachabilityListener() internal void Dispose() { + cts?.Cancel(); + cts?.Dispose(); + cts = null; + if (pathMonitor != null) { pathMonitor.SnapshotHandler = null; @@ -170,25 +176,45 @@ void OnRestrictedStateChanged(CTCellularDataRestrictedState state) async void OnChange(NetworkReachabilityFlags flags) { - // This function waits up to 1 second, checking the device’s network status every 100 milliseconds. + // Deduplicate: both watchers may fire for the same network change. + // Only the first callback runs the polling loop; subsequent ones are no-ops. + if (Interlocked.Increment(ref pendingCallbacks) > 1) + return; + + var token = cts?.Token ?? default; + if (token.IsCancellationRequested) + return; + + // This function waits up to 1 second, checking the device's network status every 100 milliseconds. // If the network status changes, it immediately triggers the ReachabilityChanged event. var initialAccess = Connectivity.NetworkAccess; const int pollingIntervalMs = 100; const int maxWaitTimeMs = 1000; int elapsedTime = 0; - while (elapsedTime < maxWaitTimeMs) + try { - await Task.Delay(pollingIntervalMs); - elapsedTime += pollingIntervalMs; - var currentAccess = Connectivity.NetworkAccess; - if (currentAccess != initialAccess) + while (elapsedTime < maxWaitTimeMs) { - ReachabilityChanged?.Invoke(); - return; + await Task.Delay(pollingIntervalMs, token); + elapsedTime += pollingIntervalMs; + var currentAccess = Connectivity.NetworkAccess; + if (currentAccess != initialAccess) + { + ReachabilityChanged?.Invoke(); + return; + } } + ReachabilityChanged?.Invoke(); + } + catch (OperationCanceledException) + { + // Listener was disposed during polling, don't fire event + } + finally + { + Interlocked.Exchange(ref pendingCallbacks, 0); } - ReachabilityChanged?.Invoke(); } } }