If there is one thing that I have learned in my career it's that network connectivity is not a binary state. A network connection goes through many different transitions when setting up the connection, transmitting data, and closing down the connection. In most cases, a client side mobile or desktop application can simply send the network request and if the client application is offline the request will immediately fail and the application can update the user accordingly (See article here). However, there are some cases where it can be handy to know what the state of the connection in your client application. For example, if there is a client VPN involved or possibly if real time communication is being used between the client and server. And this is what prompted me to write the NetworkConnectivity package. The NetworkConnectivity package is a Swift package developed for detecting the state of a TCP connection. That way your application has greater insight into when a VPN or a pub-sub communication strategy might be cycling between network connection states. This allows your application to effectively present the user with real time updates on the state of the client's connectivity.
The NetworkConnectivity package can be found on my Github account here.
Taking a Closer Look at NetworkConnectivity 👨💻
The NetworkConnectivity package is a Swift Package developed using Swift 4.2 and the Network Framework to setup and detect the connection state of a TCP connection. The operational class in the package is built using a shared singleton. This singleton, NetworkConnectivity.swift, is setup and sends delegate messages back to a conforming view controller to act upon. So, for example, if you wanted to observe the state of a connection at the start of your program, when that connection changes state the change could be sent to your program's base view controller to update the user upon a condition that might make connectivity unusable. Let's take a look at how this is done. First, the NetworkConnectivity singleton is setup with a host name.
public func setup(with hostURL: String) { if self.tcpStreamAlive { print("TCP Stream is already setup.") } self.host = hostURL guard hostURL.count > 0, self.validateHost(hostURL: hostURL) else { print("Error, invalid host.") return } if #available(iOS 12.0, OSX 10.14, *) { setupNWConnection() } else { print("Network framework only available for iOS 12 or macOS 10.14 or later.") } }
Next, Network Framework set's up a hostEndpoint and nwConnection object to observe state change using the state handler: self.stateDidChange(to:). The nwConnection object then starts this connection on the .global() background queue of the application.
@available(iOS 12.0, OSX 10.14, *) private func setupNWConnection() { print("Setting up nwConnection") let hostEndpoint = NWEndpoint.Host.init(self.host) let nwConnection = NWConnection(host: hostEndpoint, port: 80, using: .tcp) nwConnection.stateUpdateHandler = self.stateDidChange(to:) self.setupReceive(on: nwConnection) nwConnection.start(queue: DispatchQueue.global()) }
When the state changes the self.stateDidChange(to:) method is used to notify the notifyDelegateOnChange() method that a new status has been received. Along with a new status, an online flag is sent to represent if the connection is able/unable to send or receive data on that connection. This online logic is determined by the state of the connection and could be altered accordingly.
@available(iOS 12.0, OSX 10.14, *) private func stateDidChange(to state: NWConnection.State) { switch state { case .setup: self.notifyDelegateOnChange(newStatusFlag: false, connectivityStatus: "setup") self.tcpStreamAlive = true break case .waiting: self.notifyDelegateOnChange(newStatusFlag: false, connectivityStatus: "waiting") self.tcpStreamAlive = true break case .ready: self.notifyDelegateOnChange(newStatusFlag: true, connectivityStatus: "ready") self.tcpStreamAlive = true break case .failed(let error): let errorMessage = "Error: \(error.localizedDescription)" self.notifyDelegateOnChange(newStatusFlag: false, connectivityStatus: errorMessage) self.tcpStreamAlive = false case .cancelled: self.notifyDelegateOnChange(newStatusFlag: false, connectivityStatus: "cancelled") self.tcpStreamAlive = false self.setupNWConnection() break case .preparing: self.notifyDelegateOnChange(newStatusFlag: false, connectivityStatus: "preparing") self.tcpStreamAlive = true } }
From there, a view controller in a macOS or iOS application can be used to receive that status change and send the user updates accordingly in the UI.
class BaseViewController: ViewController { func viewDidLoad() { let _ = NetworkConnectivity.shared.setup(with: "agnosticdev.com") } } extension BaseViewController: NetworkConnectivityDelegate { public func networkStatusChanged(online: Bool, connectivityStatus: String) { if online { // handle online status } else { // handle offline status } } }
In Summary ⌛️
In summary I wanted to create the NetworkConnectivity Package because I still see a need for connectivity detection outside the traditionally used techniques today. If you are interested in this topic too, please take a look at the NetworkConnectivity package on my Github page and try it out. I'd love to hear what you have to say if you find this package useful as well. Thank you very much for reading and if you have any questions, please feel free to leave a comment below and I will get back to you as soon as possible.