Skip to content

Instantly share code, notes, and snippets.

@Aayush9029
Created January 10, 2025 04:08
Show Gist options
  • Select an option

  • Save Aayush9029/1eed269a8d34873bb1e4398bdec9fb68 to your computer and use it in GitHub Desktop.

Select an option

Save Aayush9029/1eed269a8d34873bb1e4398bdec9fb68 to your computer and use it in GitHub Desktop.
Network Monitor
//
// NetworkMonitorClient.swift
// Notto
//
// Created by Aayush Pokharel on 2025-01-09.
//
import Dependencies
import Foundation
import Network
public struct NetworkMonitorClient {
public struct NetworkStatus: Equatable {
public var isConnected: Bool
public var connectionType: NWInterface.InterfaceType?
public init(isConnected: Bool, connectionType: NWInterface.InterfaceType? = nil) {
self.isConnected = isConnected
self.connectionType = connectionType
}
}
public var start: @Sendable () -> AsyncStream<NetworkStatus>
public var stop: @Sendable () -> Void
public init(
start: @escaping @Sendable () -> AsyncStream<NetworkStatus>,
stop: @escaping @Sendable () -> Void
) {
self.start = start
self.stop = stop
}
}
// MARK: - Live Implementation
extension NetworkMonitorClient: DependencyKey {
public static let liveValue = NetworkMonitorClient(
start: {
AsyncStream { continuation in
let monitor = NWPathMonitor()
let queue = DispatchQueue(label: "NetworkMonitorClient")
monitor.pathUpdateHandler = { path in
let status = NetworkStatus(
isConnected: path.status == .satisfied,
connectionType: path.availableInterfaces.first?.type
)
continuation.yield(status)
}
continuation.onTermination = { _ in
monitor.cancel()
}
monitor.start(queue: queue)
}
},
stop: {} // Stop is handled by continuation.onTermination
)
}
// MARK: - Test Implementation
extension NetworkMonitorClient: TestDependencyKey {
public static let testValue = Self(
start: { .finished },
stop: {}
)
}
// MARK: - Preview Implementation
extension NetworkMonitorClient {
static let previewOnline = Self(
start: {
AsyncStream { continuation in
continuation.yield(.init(isConnected: true, connectionType: .wifi))
}
},
stop: {}
)
static let previewOffline = Self(
start: {
AsyncStream { continuation in
continuation.yield(.init(isConnected: false))
}
},
stop: {}
)
}
// MARK: - Dependency Registration
public extension DependencyValues {
var networkMonitor: NetworkMonitorClient {
get { self[NetworkMonitorClient.self] }
set { self[NetworkMonitorClient.self] = newValue }
}
}
import SwiftUI
struct NetworkMonitorView: View {
let viewModel: NetworkMonitorViewModel
var body: some View {
VStack {
if let status = viewModel.networkStatus {
HStack {
Image(systemName: status.isConnected ? "wifi" : "wifi.slash")
.foregroundColor(status.isConnected ? .green : .red)
Text(status.isConnected ? "Connected" : "Offline")
.foregroundColor(status.isConnected ? .primary : .red)
if let connectionType = status.connectionType {
Text("(\(String(describing: connectionType)))")
.foregroundStyle(.secondary)
}
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(8)
} else {
ProgressView()
}
}
.onAppear {
viewModel.startMonitoring()
}
.onDisappear {
viewModel.stopMonitoring()
}
}
}
#Preview("WiFi Connected") {
let viewModel = withDependencies {
$0.networkMonitor = .previewOnline
} operation: {
NetworkMonitorViewModel()
}
NetworkMonitorView(viewModel: viewModel)
}
#Preview("WiFi Not Conencted") {
let viewModel = withDependencies {
$0.networkMonitor = .previewOffline
} operation: {
NetworkMonitorViewModel()
}
NetworkMonitorView(viewModel: viewModel)
}
import Dependencies
import SwiftUI
@Observable
class NetworkMonitorViewModel {
@ObservationIgnored
@Dependency(\.networkMonitor) var networkMonitor
private var monitorTask: Task<Void, Never>?
var networkStatus: NetworkMonitorClient.NetworkStatus?
func startMonitoring() {
monitorTask = Task {
for await status in networkMonitor.start() {
networkStatus = status
}
}
}
func stopMonitoring() {
monitorTask?.cancel()
monitorTask = nil
}
deinit {
stopMonitoring()
}
}
@Aayush9029
Copy link
Author

image image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment