Determining Reachability & Internet Connectivity Using Combine
Subscribing to changes in Reachability & Internet Connectivity State Using a Combine Publisher
Preface: This article was originally written whilst iOS 14 was in beta and was intended to serve as the third article in a series of articles on Internet connectivity within iOS. In fact, it ended up being published subsequently to Detecting Internet Connectivity using OpenCombine which was written at a later date. The intended ordering of articles in the series is as follows:
1) Solving the Captive Portal Problem on iOS
2) Detecting Internet Access on iOS 12+
3) Determining Reachability & Internet Connectivity Using Combine
4) Detecting Internet Connectivity using OpenCombine
Minor adjustments have been made to bring the article up to date with the release of iOS 16.
Reactive Programming & iOS
Functional reactive programming has proven a popular paradigm in recent years within a number of software development ecosystems and the iOS development community is no exception. The previous few years gave rise to a number of third party frameworks which endeavored to fill the gap left by the lack of native FRP support in iOS prior to iOS 13. These include frameworks such as ReactiveCocoa, RxSwift, ReactiveSwift, BrightFutures, ReSwift, PromiseKit and more.
iOS 13 introduced Apple’s reactive programming framework Combine, and with the release of iOS 16 many apps will have either already adopted it or be in the position to limit support to iOS 13 (or above) and adopt it if they so choose.
Combine introduces the concepts of a Publisher and Subscriber (following the traditional publisher-subscriber pattern) whereby a publisher emits a sequence of events to which a subscriber may elect to be notified. One useful application of this model might be to create a publisher to which we can subscribe in order to let our app know of changes in Internet connection state.
Detecting Internet Connectivity
In the past Reachability was used by many iOS developers as the de facto means of determining whether or not an app had an Internet connection -whether by using Apple’s original sample code directly or a third-party adaptation of this.
In a previous blog post, Solving the Captive Portal Problem, I discussed how Reachability doesn’t provide a true measure of whether Internet connectivity is present but rather, indicates only that an interface is available that might allow a connection. This means that there are many scenarios in which the use of Reachability may yield as misleading result indicating that a connection is available where in fact there is not. This might include when connecting to a public WiFi network with a captive portal such as in the local coffee shop or when connecting to an access point or network which isn’t connected to the Internet. iOS solves the captive portal problem through implementation of the WISPr standard.
Connectivity is an open-source framework available under MIT license which wraps Reachability in order to listen for changes in network link state and endeavors to replicate iOS’s means of detecting captive portals in order to provide a reliable measure of whether or not Internet connectivity is present.
The advent of iOS 12 saw the introduction of the Network framework and NWPathMonitor which improves on Reachability (for more information check out a follow up blog post Detecting Internet Access on iOS 12+) and thus the Connectivity framework uses NWPathMonitor over Reachability for detecting changes in network link state on iOS 12 and above.
This framework has been updated to allow users on iOS 13 and above to make use of Combine in order to allow an app to be notified of changes in connectivity with only a few elegant lines of code.
Traditionally Connectivity allows developers to be notified of changes in Internet connectivity by assigning a closure to the whenConnected
and whenDisconnected
properties of the Connectivity object which gets invoked by the library when Internet connectivity changes. Alternatively, developers may observe the default NotificationCenter
listening for either a ConnectivityDidChange
or ReachabilityDidChange
notification to listen for changes in connectivity or reachability respectively.
Combine Publishers
Combine provides us with a more elegant means of observing changes in Internet connectivity. On iOS 13 and above, Connectivity provides a Publisher which emits an event every time a change in Internet connectivity occurs.
In the example code above (which is provided as part of the sample app in the Connectivity repository on GitHub), we first create an instance of Connectivity.Publisher
in order that we can receive an event each time the app’s Internet connection state changes.
The next step erases the type of publisher from Connectivity.Publisher to AnyPublisher using eraseToAnyPublisher
. This is performed as a means of abstraction so that any subsequent code downstream of this function call (in this case of call to sink
) is now working with an instance of AnyPublisher
rather than aConnectivity.Publisher
instance. This helps us keep the downstream code independent of any of the specific implementation details of Connectivity.Publisher
.
Just as you might use a protocol to abstract away from away from the details of the class or struct which implements that protocol, eraseToAnyPublisher
performs a similar function for a Publisher. Using a protocol rather than a specific implementation means that should the implementation change in future (so long as it still conforms to the protocol), the calling code which uses the protocol reference needn’t change at all. eraseToAnyPublisher
performs a similar function for us when working with publishers meaning that any subsequent function calls using the publisher needn’t change at all should the implementation of the publisher change in future since the downstream code is working with a generic AnyPublisher
type rather than a more specific implementation.
The final part is probably the most important in which we create a subscriber which listens / subscribes to the series of events emitted by the Connectivity.Publisher
. It’s possible to create your own subscriber but Combine provides two subscribers out of the box which are suitable for most use cases.
The built-in subscribers provided by Combine are:
- Sink — A subscriber which can be used to receive an unlimited number of values as well as notification of a completion or error.
- Assign — As the name suggests, assigns a sequence of values to the property specified by a given key path.
A More Elegant Syntax
Connectivity was designed to provide an interface as similar to Reachability as possible in order to make learning how to use the framework as simple as possible for developers who have used Reachability in the past. It is compatible with Objective-C and supports iOS 9 and above so that as many apps as possible are supported. Reachability is used as a component to determine whether a network interface state change occurs prior to iOS 12, with NWPathMonitor being used on iOS 12 and above.
For developers who need only support iOS 13 & above, Hyperconnectivity is a spin-off of the Connectivity project whose goal is to provide the functionality of Connectivity as part of a pure Swift project. By ditching compatibility with Objective-C, Hyperconnectivity is able to reimagine what Reachability would look like written using all that modern Swift has to offer. The intention was that users of Hyperconnectivity would favour the use of Combine publishers over the closure-based syntax of Connectivity (Update: Connectivity now also offers Combine publishers on iOS 13 & above).
Hyperconnectivity provides two publishers:
Hyperconnectivity.Publisher
(aliased asPublishers.Connectivity
) — notifies the caller of changes in true Internet connectivity providing the same functionality for the detection of captive portals as Connectivity does.Publishers.Reachability
— For apps unconcerned with whether an actual Internet connection is present and simply need to know whether the Internet is reachable or not, this publisher is provided to emulate the behavior of Reachability using the newer NWPathMonitor. This publisher does not provide detection of captive portals — it will only report whether or not a network interface is available e.g. whether cellular or WiFi is enabled.
A comparison of the functionality offered by Connectivity versus Hyperconnectivity can be found on the Hyperconnectivity GitHub repository. The process of subscribing to be notified of changes in Internet connectivity follows much the same pattern as when using Connectivity however:
Summary
- Hyperconnectivity is a framework which eschews Objective-C support in favour of a more modern syntax but only supports iOS 13 and above.
- Connectivity provides Obj-C and backwards compatibility to iOS 9.
- Both Connectivity & Hyperconnectivity provide Combine publishers to allow clients to be notified of changes in Connectivity or Reachability where on iOS 13 or above.
- If you found this article of interest you may find the subsequent article in this series, Detecting Internet Connectivity using OpenCombine, of interest which demonstrates detection of Internet connectivity using OpenCombine in place of Combine.
The full code is provided as part of the sample app in the Connectivity repository on GitHub. Both Connectivity and Hyperconnectivity can be found open-sourced on GitHub under MIT license and are compatible with Cocoapods, Carthage and Swift Package Manager.