A modern, type-safe, actor-based network path monitoring utility for Apple platforms.
NetworkPathMonitor provides an easy and safe way to observe network connectivity changes using Swift Concurrency, AsyncStream, callbacks, and notifications.
Need real network connectivity testing? Check out NetworkConnectivityKit - performs actual HTTP requests to detect true internet connectivity, including captive portals.
- 🚦 Real-time network status monitoring based on
NWPathMonitor - 🧑💻 Actor isolation for thread safety (Swift Concurrency)
- 🌀 AsyncStream support for async/await style observation
- 🛎️ Callback and NotificationCenter support
- ⏳ Debounce mechanism to avoid frequent updates
- 📊 Rich NetworkPath with sequence tracking and update reasons
- 🛠️ Simple API, easy integration
- Swift 5.10 or later
- iOS 13.0+, macOS 10.15+, tvOS 13.0+, watchOS 6.0+, visionOS 1.0+
Add the following to your Package.swift:
.package(url:"https://github.com/codingiran/NetworkPathMonitor.git", from:"0.1.5")Or use Xcode:File > Add Packages... and enter the repository URL.
import NetworkPathMonitor
letmonitor=NetworkPathMonitor()await monitor.fire()
// Check current status
letisConnected=await monitor.isPathSatisfied
// Stop monitoring
await monitor.invalidate()import NetworkPathMonitor
letmonitor=NetworkPathMonitor()await monitor.fire()Task{forawaitpathinawait monitor.pathUpdates {print("Network status changed: \(path.status)")print("Is first update: \(path.isFirstUpdate)")print("Update reason: \(path.updateReason)")}}import NetworkPathMonitor
letmonitor=NetworkPathMonitor()await monitor.pathOnChange{ path inprint("Network changed: \(path.status)")print("Is first update: \(path.isFirstUpdate)")print("Update reason: \(path.updateReason)")}await monitor.fire()import NetworkPathMonitor
letobserver=NotificationCenter.default.addObserver(
forName:NetworkPathMonitor.networkStatusDidChangeNotification,
object:nil,
queue:.main
){ notification iniflet newPath = notification.userInfo?["newPath"]as?NetworkPath{print("Network status changed to: \(newPath.status)")print("Is first update: \(newPath.isFirstUpdate)")print("Update reason: \(newPath.updateReason)")}}You can set a debounce interval to avoid frequent updates:
letmonitor=NetworkPathMonitor(debounceInterval:.seconds(1.0)) // 1 second debounceNetworkPathMonitor uses a convenient Interval enum for specifying debounce intervals:
// Different interval types
letmonitor1=NetworkPathMonitor(debounceInterval:.nanoseconds(500_000_000)) // 0.5 seconds
letmonitor2=NetworkPathMonitor(debounceInterval:.milliseconds(500)) // 0.5 seconds
letmonitor3=NetworkPathMonitor(debounceInterval:.seconds(0.5)) // 0.5 seconds
letmonitor4=NetworkPathMonitor(debounceInterval:.minutes(1)) // 1 minute
letmonitor5=NetworkPathMonitor(debounceInterval:.hours(1)) // 1 hourNetworkPathMonitor now provides rich sequence tracking capabilities through the NetworkPath type. Each path update includes sequence information and update reasons:
letmonitor=NetworkPathMonitor()await monitor.fire()Task{forawaitpathinawait monitor.pathUpdates {
// Check if this is the first update after initial connection
if path.isFirstUpdate {print("First network update received")}
// Access the previous path for comparison
iflet previousPath = path.sequence.previousPath {print("Previous status: \(previousPath.status)")print("Previous interfaces: \(previousPath.usedInterfaces.names)")}
// Get update reason
switch path.updateReason {case.initial:print("Initial path when monitor started")case.physicalChange:print("Physical network interface changed")case.uncertain:print("Network status changed for uncertain reason")}}}.initial: The path is the initial path when the monitor is started.physicalChange: The path has changed due to a physical interface change (e.g., switching from WiFi to Cellular).uncertain: The reason for the update is uncertain (e.g., network configuration changes)
Each path update has a sequence index that increments with each update:
print("Sequence index: \(path.sequence.index)")print("Is initial path: \(path.sequence.isInitial)")init(queue:DispatchQueue=..., debounceInterval:Interval=.zero)queue: The dispatch queue for the underlying NWPathMonitor.debounceInterval: Debounce interval using convenient Interval enum. Default is .zero (no debounce).
isActive: Whether monitoring is active.currentPath: The latestNetworkPath.isPathSatisfied: Whether the current path is satisfied (connected).
fire(): Start monitoring.invalidate(): Stop monitoring.pathOnChange(_:): Register a callback for path changes.pathUpdates: AsyncStream of NetworkPath updates.
MIT License. See LICENSE for details.
Contributions are welcome! Please open issues or submit pull requests.