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.
- We add an notification observer which listens for notification for the network connection status
- We assume notification bundles the necessary metadata into
userInfo
We unwrap this info to get necessary details of network toggle - 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
- Start a network monitoring with singleton
AFNetworkReachabilityManager
object - Add the block
setReachabilityStatusChange
which will get called every time network status changes - 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
- 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 - If the network state changes, we send a notification with respective
userInfo
metadata. - Assign current network state to
previousNetworkReachabilityStatus
- 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!