You should not manually check for internet connectivity in mobile apps
Simply because it makes no sense in 99% of applications that use HTTP, and it leads to a large number of other problems.
I have seen this kind of class in many apps, and almost always the same usage pattern. Here is an example from Stack Overflow.
actor ConnectionService {
static func monitorNetwork() -> AsyncStream<Bool>{
AsyncStream { continuation in
let monitor = NWPathMonitor()
monitor.pathUpdateHandler = { path in
switch path.status {
case .satisfied:
continuation.yield(true)
case .unsatisfied, .requiresConnection:
continuation.yield(false)
@unknown default:
continuation.yield(false)
}
}
monitor.start(queue: DispatchQueue(label: "InternetConnectionMonitor"))
continuation.onTermination = { _ in
monitor.cancel()
}
}
}
static func isConnected() async -> Bool{
typealias Continuation = CheckedContinuation<Bool, Never>
return await withCheckedContinuation({ (continuation: Continuation) in
let monitor = NWPathMonitor()
monitor.pathUpdateHandler = { path in
monitor.cancel()
switch path.status {
case .satisfied:
continuation.resume(returning: true)
case .unsatisfied, .requiresConnection:
continuation.resume(returning: false)
@unknown default:
continuation.resume(returning: false)
}
}
monitor.start(queue: DispatchQueue(label: "InternetConnectionMonitor"))
})
}
}Then this code is used elsewhere before making some request, supposedly to improve the user experience, for example before requesting some data.
guard await ConnectionService.isConnected() else {
networkError = true
return
}
await fetch()The problem with this code is that it completely ignores how networking and HTTP actually work, makes the surrounding code messy, and in general solves no real problems, except for some imaginary concern for the user with a reminder to “turn on the internet.” But let’s go step by step why this is absolutely pointless and harmful.
First, HTTP has caching, and the response to this request may already be cached. This “helpful” check simply blocks the user from interacting with the app when the data is actually available, and you artificially refuse to show it.
Second, even in this contrived example there is already a problem. You are most likely handling errors incorrectly and making error handling more complex: you end up with pseudo-errors mixed together with real errors. At the very least, throw an exception and store it as your error state to display in the UI.
Third, the HTTP client itself will return a network error if it fails to connect to the server, including an error indicating that there is no internet connection. These are the errors that should be handled and displayed in the interface, if necessary.
Fourth, in practice, a “no network connection” state almost never occurs. Meanwhile, ordinary network errors (such as 404 or response timeouts), which for some reason are often not handled properly, happen all the time.
This code is convenient only for QA engineers, because they turn on airplane mode instead of simulating real network errors, and for developers, because they simply do not implement proper error handling.
Such classes can be used, but in other scenarios, for example, when an error has already occurred and you want to reset an automatic retry timeout so that you can immediately retry once the network is restored (a much rarer case). Another possible use is low-level network work (TCP/UDP sockets), but this kind of interaction is programmed very rarely and is a completely different story.
To summarize: in normal HTTP-based applications, just do not use this, and handle errors properly. And for those who use it this way, you can send them this article so they read it and stop doing this.