How To Detect Internet Connectivity with NWPathMonitor

Written by LearnAppMaking on April 2 2021 in App Development, iOS

How To Detect Internet Connectivity with NWPathMonitor

Detecting an iOS app’s internet connectivity is useful if you want to check if the internet is reachable prior to making a network request. For example, you may want to periodically sync your app’s local data to the cloud – if the iPhone has a cellular or WiFi connection. Reachability and NWPathMonitor can help with that!

Before iOS 12, a code snippet called Reachability was the de facto approach to determining if the internet could be accessed. Since iOS 12 you use a better approach, called NWPathMonitor, that makes detecting internet reachability easier and more insightful.

In this tutorial, we’ll discuss:

  • How you can check internet connectivity with Reachability (prior to iOS 12)
  • How you can check internet connectivity with NWPathMonitor (iOS 12+)
  • How to get connectivity updates, in case the network changes
  • Practical use cases for internet connectivity detection in iOS apps

Ready? Let’s go.

  1. Detect Internet Access with Reachability
  2. Detect Internet Access with NWPathMonitor
  3. Use Cases in Practical iOS Development
  4. Further Reading

Detect Internet Access with Reachability

If your app supports iOS versions before (or including) iOS 12, you’ll want to use the older Reachability component. It uses the native SCNetworkReachability class to create a network socket, and to listen for changes. Fortunately, that’s all abstracted away so we can deal with the high-level events instead.

Remember, the goal here is to check if your app can connect to the internet, find what kind of connection that is (WiFi, cellular, etc.), and to stay updated on connectivity status changes.

You can use Reachability as follows. First, you’ll create a closure like this:

let onUpdate = { reachability in

    if reachability.connection == .wifi {
        print("We have WiFi!")
    } else if reachability.connection == .cellular {
        print("We have 3G/4G/5G!")
    } else {
        print("No internet :-(")
    }
}

Then, we’re creating a Reachability object and assign the onUpdate closure to two of its properties:

let reachability = Reachability()
reachability?.whenReachable = onUpdate
reachability?.whenUnreachable = onUpdate

The closure we defined earlier is now called whenever the network connectivity status changes. We could have used two different closures, but it’s simpler to use just one. After all, the connection property only has 3 states: .none, .cellular and .wifi.

It’s also smart to create a reachability property in your code, so the object is retained throughout the lifetime of your app. It’s also an effective use case for a singleton, in fact.

Finally, we need to start the reachability component to get connectivity updates. Here’s how:

do {
    try reachability.startNotifier()
} else {
    print("Error starting Reachability...")
}

In the above code we use error handling to check if the notifier could be started. When it can’t, we can respond by default with a state that has no internet connection.

When using Reachability, don’t use a hard-coded IP address to check if a particular webserver is reachable! Use a domain name instead (if needed at all), otherwise your app becomes incompatible with either IPv4 or IPv6 networks. Keep in mind that any 3rd-party service – how stable or permanent it may seem – can also have network connectivity issues. It’s a dependency, so treat it with the proper precautions and maintenance.

Detect Internet Access with NWPathMonitor

At WWDC 2018, Apple introduced the NWPathMonitor component as part of the Network framework for iOS 12 and up. This means we won’t have to rely on 3rd-party code anymore to monitor network changes.

The way NWPathMonitor works is similar to Reachability, but it gives a lot more information about the network we’re connected to. How do you use it? In short, we’ll need to create the NWPathMonitor, register an update handler, and start the update notifier.

First, let’s set up the component itself:

let monitor = NWPathMonitor()

Again, you’ll want to create a strong reference to this NWPathMonitor object somewhere, so it’s retained in memory. Adding it as a property to your app delegate or app struct is one approach to do that.

You can also provide the NWPathMonitor initializer with the type of network you want to monitor. Like this:

let monitor = NWPathMonitor(requiredInterfaceType: .wifi)

This will monitor changes for just the WiFi network. You can also get updates on these types of interfaces:

  • .cellular for 3G/4G/5G
  • .loopback for localhost or lo0
  • .other for virtual networks or unknown network types
  • .wifi for good ol’ WiFi
  • .wiredEthernet if you managed to plug an RJ45 in your iPhone… (no, really, the Network framework works on iOS, macOS and tvOS)

Once you’ve set up the component, you can register for changes in network connectivity. Like this:

monitor.pathUpdateHandler = { path in

    if path.status == .satisfied {
        print("Yay! We have internet!")
    }
}

In the above closure, the path parameter of type NWPath contains a lot of information about the network:

  • isExpensive returns true when the path uses an interface that’s considered expensive, such as 5G via a paid cellular data plan
  • status, of type NWPathStatus, indicates if a network request could be successfully made over this network path
  • availableInterfaces contains an array of network interfaces that can be used for this path
  • supportsDNS, supportsIPv4 and supportsIPv6 can be used to determine if the path can use DNS, IPv4 and IPv6 (esp. helpful for local networking)

You can use the function usesInterfaceType(_:) to check which interface type this network path uses. This is the most effective way to figure out if your app is connected over WiFi, cellular or ethernet.

Here’s how:

if path.usesInterfaceType(.wifi) {
    print("It's WiFi!")
} else if path.usesInterfaceType(.cellular) {
    print("5G FTW!!!")
}

When the status property has value .satisfied, you can be certain there’s internet connectivity via that network path. When it’s .unsatisfied, the network path isn’t available and there is no internet access.

Finally, just as before, we need to start the monitor to receive connectivity updates. You can do this by assigning the monitor to a background dispatch queue, like this:

let queue = DispatchQueue.global(qos: .background)
monitor.start(queue: queue)

The monitor will now start receiving network path updates. So, whenever the network changes, the closure will get called and you can respond to changes in internet connectivity.

You can stop receiving connectivity updates by calling cancel() on the NWPathMonitor instance. Like this:

monitor.cancel()

An interesting scenario occurs when you set requiredInterfaceType to .cellular, sign on to a WiFi network, and then wait for network updates. The path will show as “unsatisfied (Interface type cellular is required by parameters)” and path.usesInterfaceType(.wifi) will return true. This kind of fine-grained control and information is perfect for mediating the best-available network path, such as downgrading to cellular when WiFi won’t work.

Use Cases in Practical iOS Development

You might wonder why you’d want to monitor for internet connectivity when you can just make an HTTPS request and check if it succeeds. Let’s discuss the use cases for practical connectivity detection.

It’s important to understand that simply waiting for a request to succeed has a few disadvantages:

  • It’s a waste of resources, because you’re making a request of which you’re not sure it’ll succeed
  • It’s a waste of a user’s time, because most requests that will fail due to internet connectivity will only do so after a timeout of a few seconds (up to 30s)
  • It’s more complex to code, because in a request’s return handler, you’ll need to recover from connectivity issues (on top of data issues)

If you know that your app is connected to the internet before making a request, you can more efficiently make that request. When there’s no connectivity, you don’t even have to attempt to make that request.

Some apps also rely on an active internet connection. Take database platforms like Firebase and Parse Server, for example. They support a so-called offline mode, in which data that’s written to the database is temporarily cached locally when there’s no active internet connection. When the connection comes online, data is synced with the cloud.

An app’s users don’t notice anything from this behavior, because they can use the app regardless of having internet connectivity or not. When the network comes back online, changes they made in the app are sent to the server as if there was no lapse in connectivity at all. This functionality is crucial in making offline-first apps. It’s a great use case for NWPathMonitor!

Further Reading

The NWPathMonitor component gives you more insight in the state of network connectivity, and allows you to make sensible choices about network requests prior to making them.

The component is more advanced than Reachability, although the simplest approach is still as easy as registering a callback and starting to monitor network changes. Neat!

Want to learn more? Check out these resources:

LearnAppMaking

LearnAppMaking

At LearnAppMaking.com, app developers learn how to build and launch awesome iOS apps.