Search code examples
iosswiftnotificationsreachability

How to know whether actual internet is available or not in Swift?


I want to get notified when my WiFi Router color changes from Green to Red

I am making an app which will tell you whether you are online or offline from menu bar in Swift which is open source & can be found at https://github.com/deadcoder0904/net-alert

I want to know if its possible to get notified when WiFi color changes in Swift.

I can't constantly ping my server to know that the color changed as this would be wasting internet resources.

So is it possible to know this in Swift?


Solution

  • First of all, I'm going to answer this from iOS perspective. But your GitHub demo is for macOS. I think the basics are the same.

    I'll go to solve this in protocol oriented approach.


    Update:

    After quite a lot of searching I've come across the magnificent implementation of Connectivity wrapper. There is even a descriptive blogpost Solving the Captive Portal Problem on iOS if you want to know more about it. This implementation is capable of dealing with actual internet available / not-available states.

    Note: Don't like to read further? Here is the working version that I'm going to illustrate briefly. Navigation bar will reflect with Green & Red color for different connection status.

    Define a protocol:

    This protocol will help any interested object to be notified when any change in connectivity occurs.

    protocol ConnectivityNotifiable {
        var connectivity: Connectivity { get }
        func startNotifyingConnectivityChangeStatus()
        func stopNotifyingConnectivityChangeStatus()
        func connectivityChanged(toStatus: ConnectivityStatus)
    }
    
    // Provide some default implementation through protocol extension
    extension ConnectivityNotifiable {
        func startNotifyingConnectivityChangeStatus() {
            connectivity.isPollingEnabled = true
            connectivity.startNotifier()
            connectivity.whenConnected = { connectivity in
                self.connectivityChanged(toStatus: connectivity.status)
            }
            connectivity.whenDisconnected = { connectivity in
                self.connectivityChanged(toStatus: connectivity.status)
            }
        }
        func stopNotifyingConnectivityChangeStatus() {
            connectivity.stopNotifier()
        }
    }
    

    Conform to the protocol:

    Conforming to the ConnectivityNotifiable protocol will add the functionality to any interested object to be notified when Connectivity status changes. Something like monitoring.

    class ViewController: UIViewController, ConnectivityNotifiable {
    
        // ConnectivityNotifiable protocol requirement
        let connectivity = Connectivity()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // Invoke the default implementation of the ConnectivityNotifiable protocol
            // requirement to be able to be notified
            startNotifyingConnectivityChangeStatus()
    
            // Reminder:
            // Don't forget to invoke stopNotifyingConnectivityChangeStatus() when
            // you are done or when the lifecycle of your controller ends
        }
    
        // ConnectivityNotifiable protocol requirement
        func connectivityChanged(toStatus: ConnectivityStatus) {
            // Everytime any change happens in the network connectivity 
            // this method will be invoked with appropriate connection status
            switch toStatus {
    
            case .connected, 
                 .connectedViaWiFi, 
                 .connectedViaCellular:
                // Connected/Internet available. Update any UI component
    
            case .notConnected, 
                 .connectedViaWiFiWithoutInternet, 
                 .connectedViaCellularWithoutInternet:
                // Disconnected/Internet not available. Update any UI component
            }
        }
    }
    


    Old Answer

    Note: If you are using the latest release of the Reachability, you won't need the NotificationCenter based solution to get reachability change notification. It works completely fine with closure based approach.


    Don't like to know how to achieve this? Here is a working version made for iOS platform. Clone the repo and check yourself. Navigation bar will reflect with Green, Orange & Red color for different connection status.

    Define a protocol:

    This protocol will help any interested object to be notified when any change in reachability occurs.

    protocol Reachable {
        var reachability: Reachability { get }
        func startMonitoringReachabilityChangeStatus()
        func reachabilityChanged(to: Reachability.Connection)
    }
    
    extension Reachable {
        func startMonitoringReachabilityChangeStatus() {
            do {
                try reachability.startNotifier()
            } catch {
                print(error.localizedDescription)
            }
            reachability.whenReachable = { reachability in
                self.reachabilityChanged(to: reachability.connection)
            }
            reachability.whenUnreachable = { reachability in
                self.reachabilityChanged(to: reachability.connection)
            }
        }
    }
    

    Conform to the protocol:

    Conforming to the Reachable protocol will add the functionality to any interested object to be notified when reachability status changes. Something like monitoring.

    class ViewController: UIViewController, Reachable {
    
        // Reachable protocol requirement
        let reachability: Reachability = Reachability()!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // initial reachability checkup
            reachabilityChanged(to: reachability.connection)
    
            // Invoke the default implementation of the Reachable protocol requirement 
            // to be able to be notified
            startMonitoringReachabilityChangeStatus()
        }
    
        // Reachable protocol requirement
        func reachabilityChanged(to: Reachability.Connection) {
            // Everytime any change happens in the network connectivity 
            // this method will be invoked with appropriate connection status
            switch to {
            case .wifi:
                DispatchQueue.main.async {
                    // Update any UI component
                }
            case .cellular:
                DispatchQueue.main.async {
                    // Update any UI component
                }
            case .none:
                DispatchQueue.main.async {
                    // Update any UI component
                }
            }
        }
    }