Detecting Internet Connectivity using OpenCombine

You’ve been tasked with writing an app that needs support ageing devices yet you still want to use modern programming techniques — what next?

Ross Butler
7 min readJan 26, 2022
Photo by JJ Ying on Unsplash

This article is the third in a series on detecting Internet connectivity in iOS apps. In the first article, Solving the Captive Portal Problem on iOS I wrote about how Apple’s Reachability sample code (and open source variants) was once the defacto method used by iOS developers to determine whether or not an Internet connection was available but covered that it did not provide a means of doing so reliably. Subsequently, in Detecting Internet Access on iOS 12, I wrote about how iOS 12 introduced the Network framework which provided an in-built method of detecting changes in network interface state rather than needing to include Reachability sample code (or an open source alternative).

Reactive Programming & iOS

If you are already familiar with reactive programming & Combine feel free to skip down to the next section on the Challenges of Combine or the subsequent section on Internet Connectivity & Combine.

In the early days of iOS app development, nearly all apps followed the Model-View-Controller (MVC) architectural pattern as the template for a new iOS project generated by Xcode followed this pattern and it was in the manner in which Apple expected iOS apps to be built.

As more sophisticated apps with larger codebases began to emerge developers frequently ran into issues such as the App Delegate or a View Controller violating the Single Responsibility Principle and being used to perform any number of tasks resulting in these components frequently becoming bloated and difficult to navigate. These components also suffered from being hard to unit test.

Developers began to formulate solutions to these problems, and particularly as developers from other platforms joined the iOS ecosphere, they brought with them ideas from the platforms from which they’d just switched. This resulted in the proposal of a number of different architectural patterns which could be applied to iOS development including MVP, MVVM, Elm and VIPER to name but a few.

Arguably, Model-View-ViewModel (MVVM) grew to be one of the most popular architectural patterns applied to the development of new iOS apps. Originally developed at Microsoft and used in popular technologies such as Windows Presentation Foundation (WPF), Silverlight and .NET, this pattern introduced the concept of the view model, distinct from the model which encapsulated the business logic, which more accurately reflected the state of the UI. Presenting all of the data and logic required by the view in a readily-consumable form (usually involving transforming the data held by the model to achieve this) using a view model meant that the amount of logic required by a view could be kept to a minimum whilst at the same time the view model could easily be unit tested in contrast to a View Controller.

With the MVVM architectural pattern and similar alternatives growing in popularity, new challenges arose such as how best to keep the view model and the interface components in sync with one another? Frequently, ensuring that a property in the view model was used to populate a text field and then any changes made by the user to that text field propagated back to the property in the view model involved writing large amounts of error-prone “glue” code to ensure that these properties and their associated UI components were kept in sync.

At a similar time, reactive programming was rapidly gaining traction in web development and provided a solution to the challenge of having to write large amounts of glue code to keep the view model and UI in sync with one another. Frameworks such as ReactiveSwift and RxSwift emerged based on the ideas popularised in web development, including components which operated on an event stream allowing view model properties to be updated as changes to the UI were made and vice-versa meaning that large amounts of boilerplate code were no longer necessary.

Eventually Apple observed the popularity of these programming concepts and frameworks and created their own reactive framework — Combine. Being built into iOS (and being endorsed by Apple) ensured that Combine was sure to gain traction with developers in contrast to frameworks such as RxSwift which were not baked into the system.

The Challenges of Combine

With iOS 15 out in the wild, it’s perfectly plausible for iOS developers to build apps using Combine (even given that Combine is only available in iOS 13 and beyond) following the golden rule that most iOS apps support the latest and the previous version of iOS currently released.

Unfortunately however, not all iOS apps (even greenfield ones) can afford the luxury of the having to support only the latest two versions of iOS and have to cater for the wider pool of iOS devices still in service. How then, can we apply modern reactive programming techniques whilst still providing support for the majority of iOS devices still in use?

One option is that we could opt for a third-party reactive framework such as RxSwift (which supports back to iOS 9 at the time of writing). However if we anticipate that we might want to raise the deployment target of our app in future and switch to using Combine then a framework such as RxSwift might not prove the best choice given the amount of time it would take a remove the framework from our app in the future (given that reactive frameworks are pervasive throughout apps and difficult to isolate to within a single component as we might try to do with most third party frameworks) and switch to Combine syntax.

Thankfully a couple of frameworks have emerged in the form of OpenCombine and CombineX which aim to provide open source implementations of the Combine framework. By implementing the same syntax as Combine and providing support for iOS devices prior to iOS 13 (effectively acting as a shim for Combine), these frameworks allow developers to develop using Combine syntax whilst supporting older devices and then move to Combine proper at some point in the future when it becomes feasible to raise the deployment target for the app.

Internet Connectivity & Combine

Combine provides publishers for all sorts of things allowing us to observe changes in UserDefaults, the results of a URLSession invocation or even to merge multiple publishers into a single event stream. However one notable omission is the ability to subscribe to a publisher to observe changes in Internet connectivity state.

Connectivity is an open source framework which aside from solving such issues as the capture portal problem (see Solving the Captive Portal Problem on iOS) provides Combine publishers on iOS 13 and above, allowing a developer to observe changes in Internet connectivity using Combine syntax. Hyperconnectivity, an offshoot of the Connectivity project, also provides Combine publishers allowing developers to observe Internet connection status, eschewing support for iOS 9 (and the SystemConfiguration framework) in favour of a more modern elegant syntax (using the Network framework introduced in iOS 12). Since in this article we’re concerned with providing support for using Combine with older versions of iOS we’ll focus on the former rather than the latter.

OpenConnectivity is a lightweight Swift package which allows developers using OpenCombine to utilise publishers on platforms to observe changes in Internet connectivity where Combine proper is unavailable i.e. prior to iOS 13. To make use of it simply integrate the package using Swift Package Manager (SPM) as normal using the package URL.

Next, you’ll likely want to integrate the framework into your app in such a way as it is contained to within a single component such that references to the framework don’t “leak” throughout your app making it harder to remove in the future should you choose to do so (the fact that references to RxSwift are leaked across your app’s codebase is one of the major disadvantages of using it as opposed to Combine).

Here’s an example of how this can be achieved:

Note that in a real app you likely wouldn’t want to observe Internet connectivity changes from the App Delegate.

In the above example we create a new model object ConnectivityResult which is used as part of the result type of the publisher whilst the publisher itself is type-erased to AnyPublisher. This leaves us free to subscribe to the publisher to observe changes in Internet connectivity without having to worry about leaking library objects into the app’s codebase.

Summary

OpenConnectivity allows you to use Combine syntax to observe changes in Internet connectivity even on platforms where Combine is unavailable.

Advantages

  • Using OpenConnectivity allows you to use reactive programming paradigms in the form of OpenCombine even prior to iOS 13 to observe changes in Internet connectivity.
  • Integration is simple using Swift Package Manager.
  • Using OpenConnectivity as opposed to Reachability (SystemConfiguration) or the Network framework provides robustness and the ability to detect captive portals (see previous articles).
  • Using OpenCombine rather than an alternative such as RxSwift means that when you’re ready to switch to Combine, the transition should be a relatively simple one rather than a painful ordeal.
  • When the time comes that you wish to switch from OpenCombine to Combine proper, simply switch from using OpenConnectivity to Connectivity with minimal effort.

Disadvantages

  • OpenCombine doesn’t yet provide all of the functionality that Combine offers.
  • RxSwift (and some of its rivals) have been in development far longer than OpenCombine and thus offers more functionality and at the time of writing more integration options (e.g. Cocoapods and Carthage).

OpenConnectivity can be found open-sourced on GitHub under MIT license and is compatible with Swift Package Manager.

--

--

Ross Butler

Apps Engineering Manager @ Go City. All views are my own.