From 4a459bf3770d710de075ea8b0306dc1f20b2cf14 Mon Sep 17 00:00:00 2001 From: Kevin McKee Date: Fri, 11 Jul 2025 09:07:32 -0700 Subject: [PATCH 1/3] Singleton Network Monitor --- Sources/OAuthKit/Network/NetworkMonitor.swift | 7 +++++-- Sources/OAuthKit/OAuth.swift | 2 +- Tests/OAuthKitTests/UtilityTests.swift | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Sources/OAuthKit/Network/NetworkMonitor.swift b/Sources/OAuthKit/Network/NetworkMonitor.swift index d3ae851..5083af5 100644 --- a/Sources/OAuthKit/Network/NetworkMonitor.swift +++ b/Sources/OAuthKit/Network/NetworkMonitor.swift @@ -11,7 +11,10 @@ import Observation /// An `Observable` type that publishes network reachability information. @MainActor @Observable -public final class NetworkMonitor { +public final class NetworkMonitor: Sendable { + + // The shared singleton network monitor. + public static let shared: NetworkMonitor = .init() @ObservationIgnored private let pathMonitor = NWPathMonitor() @@ -29,7 +32,7 @@ public final class NetworkMonitor { } /// Initializer. - public init() { } + private init() { } /// Starts the network monitor (conforms to AsyncSequence). public func start() async { diff --git a/Sources/OAuthKit/OAuth.swift b/Sources/OAuthKit/OAuth.swift index b659812..2b6bd12 100644 --- a/Sources/OAuthKit/OAuth.swift +++ b/Sources/OAuthKit/OAuth.swift @@ -57,7 +57,7 @@ public final class OAuth: Sendable { private var tasks = [Task<(), any Error>]() @ObservationIgnored - private let networkMonitor = NetworkMonitor() + private let networkMonitor: NetworkMonitor = .shared /// Configuration option determining if tokens should be auto refreshed or not. @ObservationIgnored diff --git a/Tests/OAuthKitTests/UtilityTests.swift b/Tests/OAuthKitTests/UtilityTests.swift index 4b3b3ca..979ef83 100644 --- a/Tests/OAuthKitTests/UtilityTests.swift +++ b/Tests/OAuthKitTests/UtilityTests.swift @@ -153,7 +153,7 @@ struct UtilityTests { @MainActor @Test("Network Monitor") func whenNetworkMonitoring() async throws { - let monitor = NetworkMonitor() + let monitor: NetworkMonitor = .shared #expect(monitor.isOnline == false) withObservationTracking { _ = monitor.isOnline From b74270cac87df8e1ed24fc516b36226c966c63bb Mon Sep 17 00:00:00 2001 From: Kevin McKee Date: Fri, 11 Jul 2025 09:43:28 -0700 Subject: [PATCH 2/3] Added unit tests around network monitor isMonitoring flag. --- Sources/OAuthKit/Network/NetworkMonitor.swift | 5 +++++ Tests/OAuthKitTests/UtilityTests.swift | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Sources/OAuthKit/Network/NetworkMonitor.swift b/Sources/OAuthKit/Network/NetworkMonitor.swift index 5083af5..a493f6c 100644 --- a/Sources/OAuthKit/Network/NetworkMonitor.swift +++ b/Sources/OAuthKit/Network/NetworkMonitor.swift @@ -19,6 +19,9 @@ public final class NetworkMonitor: Sendable { @ObservationIgnored private let pathMonitor = NWPathMonitor() + /// Flag indicating if monitoring is currently active or not. + public var isMonitoring = false + /// Returns true if the network has an available wifi interface. public var onWifi = false /// Returns true if the network has an available cellular interface. @@ -36,6 +39,8 @@ public final class NetworkMonitor: Sendable { /// Starts the network monitor (conforms to AsyncSequence). public func start() async { + guard !isMonitoring else { return } + isMonitoring.toggle() for await path in pathMonitor { handle(path: path) } diff --git a/Tests/OAuthKitTests/UtilityTests.swift b/Tests/OAuthKitTests/UtilityTests.swift index 979ef83..a73f248 100644 --- a/Tests/OAuthKitTests/UtilityTests.swift +++ b/Tests/OAuthKitTests/UtilityTests.swift @@ -162,8 +162,15 @@ struct UtilityTests { #expect(monitor.isOnline) } } - Task { + Task { @MainActor in await monitor.start() + #expect(monitor.isMonitoring) + } + // The second call to monitor.start() should simply bail as monitoring + // is already happening and not toggle the internal `isMonitoring` flag. + Task { @MainActor in + await monitor.start() + #expect(monitor.isMonitoring) } } } From 11fc49f7cd4d0b6f59b5ecbc77383e9d913cd6c1 Mon Sep 17 00:00:00 2001 From: Kevin McKee Date: Fri, 11 Jul 2025 09:48:48 -0700 Subject: [PATCH 3/3] Private set for isMonitoring --- Sources/OAuthKit/Network/NetworkMonitor.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/OAuthKit/Network/NetworkMonitor.swift b/Sources/OAuthKit/Network/NetworkMonitor.swift index a493f6c..2a978d9 100644 --- a/Sources/OAuthKit/Network/NetworkMonitor.swift +++ b/Sources/OAuthKit/Network/NetworkMonitor.swift @@ -20,7 +20,7 @@ public final class NetworkMonitor: Sendable { private let pathMonitor = NWPathMonitor() /// Flag indicating if monitoring is currently active or not. - public var isMonitoring = false + public private(set) var isMonitoring = false /// Returns true if the network has an available wifi interface. public var onWifi = false