iOS - Checking the reachability status of network internet connection in Swift

iOS - Checking the reachability status of network internet connection in Swift

Today I am going to write about classic problem we all face in the app, Detecting network outage. There are times with bad internet connections or no connection at all. This could cause severe distress to app user or even bad ratings. Worse, if network outages continue and there is no way to let user know that app is not a problem, but internet connection is, user might even uninstall the app.

Although there are some improvement developer can add to make it better. For example, caching most frequently accessed content or showing placeholder in case network connection is slow or lost. However, certain times high quality and time sensitive content comes from online resources and if the request fails, it is best to let user know of it. (In both the cases going from online to offline and other way around) In this case it is left to user how to deal with persisting network issue.

I have used Apple's native Reachability framework before. However, as I noticed this framework could very well detect the initial network state, but right after network changes it failes to consistently give me related notifications whether it went from on-off or other way around

As a solution, I moved to using AFNetworking (4.0). This worked really well for me for my side projects. Let's see how it is done.

First off, you will have to create a podfile and add the AFNetworking dependency to it as follows.


platform :ios, ‘8.0’
inhibit_all_warnings!
use_frameworks!

xcodeproj '<your_project_name>'

target '<your_project_name>' do
    pod 'AFNetworking', '~> 4.0'
end

Please note that how I have used a greedy operator. Using which it will only use all the minor version of 4.0. However, it will not pull any of the next major versions (5.0, 6.0 and so on).

When the app starts, you will have to start listening for your network reachability status change notifications. This is usually done in AppDelegate which lives for the lifetime of the app. It's better to keep that code inside the class that you know will outlive your application. It may be a singleton or AppDelegate is your choice which listens to network status change and posts notification as it gets toggled.

Please note that I have used Swift 5.0 for this tutorial. Please let me know if you face any issues making it work for the future Swift versions

First off import the AFNetworking module in concerned Swift file


import AFNetworking

Now add the following code which will emit the notification upon receiving network status change from AFNetworkReachabilityManager


@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    var previousNetworkReachabilityStatus: AFNetworkReachabilityStatus = .unknown
    
    .....
    ...
    ..
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        .....
        
        let networkReachabilityChangedNotification = NSNotification.Name("NetworkReachabilityChanged")

        let sharedReachabilityManager = AFNetworkReachabilityManager.shared()
        sharedReachabilityManager.startMonitoring()

        sharedReachabilityManager.setReachabilityStatusChange { status in
            let reachabilityStatus = AFStringFromNetworkReachabilityStatus(status)
            var reachableOrNot = ""

            switch status {
            case .reachableViaWWAN, .reachableViaWiFi:
                // Reachable.
                reachableOrNot = "Reachable"
            case .notReachable:
                // Not reachable.
                reachableOrNot = "Not Reachable"
            default:
                // Network status unknown
                reachableOrNot = "Unknown"
            }

            // Any class which has observer for this notification will be able to report loss or recovery of network connection

            if (status != self.previousNetworkReachabilityStatus) {
                NotificationCenter.default.post(name: networkReachabilityChangedNotification, object: nil, userInfo: [
                    "reachabilityStatus" : "Connection Status : \(reachabilityStatus)",
                    "reachableOrNot" : "Network Connection \(reachableOrNot)"
                ])
            }
            self.previousNetworkReachabilityStatus = status
        }
        
        ....
        ...               
    }
}



And you can add observers anywhere in your project to capture the network state change and take appropriate action on that screen,



override func viewDidLoad() {
    super.viewDidLoad()
    .....
    ...
    
    NotificationCenter.default.addObserver(self, selector: #selector(notificationReceived(notification:)), name: NSNotification.Name("NetworkReachabilityChanged"), object: nil)
}

@objc func notificationReceived(notification: Notification) {
    guard let userInfo = notification.userInfo else {
        return
    }
    
    print(userInfo["reachabilityStatus"]!)
    print(userInfo["reachableOrNot"]!)
}


Let's see what the code above does.

  1. We add an notification observer which listens for notification for the network connection status
  2. We assume notification bundles the necessary metadata into userInfo We unwrap this info to get necessary details of network toggle
  3. Once we get the required info, we log it in the console

If you toggle between nework state, you will see a nice output like this in the console,

Connection Status : Reachable via WiFi
Network Connection Reachable
Connection Status : Not Reachable
Network Connection Not Reachable

Below is a summary of what we are doing in the above code

  1. Start a network monitoring with singleton AFNetworkReachabilityManager object
  2. Add the block setReachabilityStatusChange which will get called every time network status changes
  3. Maintain and populate local variable which will enclose the metadata about current network status and will be sent over to respective observers upon network change
  4. We use local variable previousNetworkReachabilityStatus to keep track of previous network status. We will send notification out only if current network status is different from the one previously observed
  5. If the network state changes, we send a notification with respective userInfo metadata.
  6. Assign current network state to previousNetworkReachabilityStatus
  7. Add the observer to related screen to capture the change in network state and trigger follow-up actions

Now, let's go to an observer. Speaking of observer you need not add it to every viewController since that would mean lot of repetition. You may wish to put observer in one of the root view controller which can pass notification to other controllers which it owns.

Instead of logging message to console, you can also display an error message to user, dim the background or disable certain controls in order to avoid user from doing unwanted activities while network is off.
If proper care is not taken, doing certain online activities may cause an app to crash shifting blame from network to app

This should be it as long as detecting network connection status change is concerned for an iOS app. I have used and I can assure this enough that this is pretty reliable API and you are safe to use it for production use. Let me know if you have any questions concerning this post or anything that related to mobile development. Happy to Help!