Skip to content

Commit 4b9d2e3

Browse files
committed
depend on AsyncDebouncer
1 parent dbb7ae7 commit 4b9d2e3

5 files changed

Lines changed: 34 additions & 68 deletions

File tree

Package.resolved

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ let package = Package(
2222
],
2323
dependencies: [
2424
.package(url: "https://github.com/codingiran/NetworkKit.git", .upToNextMajor(from: "0.2.8")),
25+
.package(url: "https://github.com/codingiran/AsyncTimer.git", .upToNextMajor(from: "0.0.6")),
2526
],
2627
targets: [
2728
// Targets are the basic building blocks of a package, defining a module or a test suite.
@@ -30,6 +31,7 @@ let package = Package(
3031
name: "NetworkPathMonitor",
3132
dependencies: [
3233
"NetworkKit",
34+
"AsyncTimer",
3335
],
3436
linkerSettings: [
3537
.linkedFramework("Network"),

[email protected]

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ let package = Package(
2222
],
2323
dependencies: [
2424
.package(url: "https://github.com/codingiran/NetworkKit.git", .upToNextMajor(from: "0.2.8")),
25+
.package(url: "https://github.com/codingiran/AsyncTimer.git", .upToNextMajor(from: "0.0.6")),
2526
],
2627
targets: [
2728
// Targets are the basic building blocks of a package, defining a module or a test suite.
@@ -30,6 +31,7 @@ let package = Package(
3031
name: "NetworkPathMonitor",
3132
dependencies: [
3233
"NetworkKit",
34+
"AsyncTimer",
3335
],
3436
linkerSettings: [
3537
.linkedFramework("Network"),

Sources/NetworkPathMonitor/NetworkPathMonitor.swift

Lines changed: 14 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
//
77

88
import Foundation
9+
#if compiler(>=6.0)
10+
public import AsyncTimer
11+
#else
12+
@_exported import AsyncTimer
13+
#endif
914

1015
// Enforce minimum Swift version for all platforms and build systems.
1116
#if swift(<5.10)
@@ -16,7 +21,7 @@ import Network
1621

1722
public enum NetworkPathMonitorInfo: Sendable {
1823
/// Current NetworkPathMonitor version.
19-
public static let version = "0.1.2"
24+
public static let version = "0.1.3"
2025
}
2126

2227
/// A class that monitors network path changes using `NWPathMonitor`.
@@ -26,8 +31,8 @@ public actor NetworkPathMonitor {
2631
/// The queue on which the network path monitor runs.
2732
private let monitorQueue: DispatchQueue
2833

29-
/// Debounce interval in seconds.
30-
private let debounceInterval: Interval
34+
/// The debouncer to debounce network path updates.
35+
private let debouncer: AsyncDebouncer
3136

3237
/// The network path update handler.
3338
private var networkPathUpdater: PathUpdateHandler?
@@ -38,9 +43,6 @@ public actor NetworkPathMonitor {
3843
/// A Boolean value that indicates whether the network path monitor is valid.
3944
public private(set) var isActive: Bool = false
4045

41-
/// Task for debouncing path updates.
42-
private var debounceTask: Task<Void, Never>?
43-
4446
/// Current network path.
4547
public private(set) var currentPath: NetworkPath
4648

@@ -54,24 +56,20 @@ public actor NetworkPathMonitor {
5456
/// - Parameter queue: The queue on which the network path monitor runs. Default is a serial queue with a unique label.
5557
/// - Parameter debounceInterval: Debounce interval. If set to 0, no debounce will be applied. Default is 0 seconds.
5658
public init(queue: DispatchQueue = .init(label: "com.networkPathMonitor.\(UUID())"),
57-
debounceInterval: Interval = .seconds(0))
59+
debounceInterval: Interval = .zero)
5860
{
59-
precondition(debounceInterval.nanoseconds >= 0, "debounceInterval must be greater than or equal to 0")
61+
precondition(debounceInterval.isValid, "debounceInterval must be greater than or equal to 0")
6062
monitorQueue = queue
6163
currentPath = NetworkPath(nwPath: networkMonitor.currentPath)
6264
previousYieldPath = currentPath
63-
self.debounceInterval = debounceInterval
65+
debouncer = AsyncDebouncer(debounceTime: debounceInterval)
6466
networkMonitor.pathUpdateHandler = { [weak self] path in
6567
guard let self else { return }
6668
Task { await self.handlePathUpdate(path) }
6769
}
6870
}
6971

7072
deinit {
71-
if let debounceTask {
72-
debounceTask.cancel()
73-
self.debounceTask = nil
74-
}
7573
pathUpdateContinuation = nil
7674
networkPathUpdater = nil
7775
networkMonitor.pathUpdateHandler = nil
@@ -99,24 +97,10 @@ public actor NetworkPathMonitor {
9997
private func handlePathUpdate(_ path: NWPath) async {
10098
// Before yielding, keep the previous sequence
10199
currentPath = NetworkPath(nwPath: path, sequence: previousYieldPath.sequence)
102-
103-
debounceTask?.cancel()
104-
guard debounceInterval.nanoseconds > 0 else {
105-
// No debounce, yield immediately
106-
debounceTask = nil
100+
// Yield the path by debouncer
101+
await debouncer.call { [weak self] in
102+
guard let self else { return }
107103
await yieldNetworkPath()
108-
return
109-
}
110-
// Debounce is active
111-
debounceTask = Task {
112-
do {
113-
try await Task.sleep(nanoseconds: UInt64(self.debounceInterval.nanoseconds))
114-
await self.yieldNetworkPath()
115-
} catch is CancellationError {
116-
// Task was cancelled, do nothing
117-
} catch {
118-
print("Error during debounce sleep: \(error)")
119-
}
120104
}
121105
}
122106

@@ -182,30 +166,3 @@ public extension NetworkPathMonitor {
182166
networkPathUpdater = handler
183167
}
184168
}
185-
186-
// MARK: - Duration Convenience
187-
188-
public extension NetworkPathMonitor {
189-
enum Interval: Sendable {
190-
case nanoseconds(_: Int)
191-
case microseconds(_: Int)
192-
case milliseconds(_: Int)
193-
case seconds(_: Double)
194-
case minutes(_: Int)
195-
196-
var nanoseconds: Int {
197-
switch self {
198-
case let .nanoseconds(value):
199-
return value
200-
case let .microseconds(value):
201-
return value * 1000
202-
case let .milliseconds(value):
203-
return value * 1_000_000
204-
case let .seconds(value):
205-
return Int(value * 1_000_000_000)
206-
case let .minutes(value):
207-
return value * 60 * 1_000_000_000
208-
}
209-
}
210-
}
211-
}

Tests/NetworkPathMonitorTests/NetworkPathMonitorTests.swift

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ class NetworkPathMonitorTests: XCTestCase, @unchecked Sendable {
1212
override func setUp() async throws {
1313
try await super.setUp()
1414
customQueue = DispatchQueue(label: "com.test.networkmonitor")
15-
// monitor = NetworkPathMonitor(queue: customQueue, debounceInterval: .seconds(1.5))
16-
monitor = NetworkPathMonitor(queue: customQueue)
15+
monitor = NetworkPathMonitor(queue: customQueue, debounceInterval: .seconds(1.5))
16+
// monitor = NetworkPathMonitor(queue: customQueue)
1717
}
1818

1919
override func tearDown() async throws {
@@ -104,19 +104,15 @@ class NetworkPathMonitorTests: XCTestCase, @unchecked Sendable {
104104

105105
await fulfillment(of: [expectation], timeout: 5.0)
106106
}
107-
107+
108108
@MainActor
109109
func testPathChangeHandlerPrint() async {
110-
let expectation = XCTestExpectation(description: "Path change handler called")
111-
112110
await monitor.pathOnChange { path in
113-
print(path.debugDescription)
114-
// expectation.fulfill()
111+
print("\(Date().timeIntervalSince1970) - \(path.description)")
115112
}
116113

117114
await monitor.fire()
118-
119-
await fulfillment(of: [expectation], timeout: .infinity)
115+
try? await Task.sleep(nanoseconds: 10000 * 1_000_000_000)
120116
}
121117

122118
// MARK: - Notification Tests
@@ -188,7 +184,7 @@ class NetworkPathMonitorTests: XCTestCase, @unchecked Sendable {
188184

189185
func measureNetworkTestInitialization(_ path: NWPath) {
190186
measure {
191-
let _ = NetworkPath(nwPath: path)
187+
_ = NetworkPath(nwPath: path)
192188
}
193189
}
194190
}

0 commit comments

Comments
 (0)