Combine & CoreLocation, Part 1 – Publishers & Delegates
Tags: swift, ios, swiftui
Published: 2020-08-26 21:03:38

---
title: Combine & CoreLocation, Part 1 – Publishers & Delegates
published: true
description: Many APIs from Apple come from an era of Objective-C and Delegate Patterns. How do we adapt those APIs for SwiftUI and create Publishers from delegates?
canonical_url: https://learningswift.brightdigit.com/combine-corelocation-publishers-delegates/
tags: swift, iOS, swiftui
series: Combine & CoreLocation
cover_image: https://i1.wp.com/learningswift.brightdigit.com/wp-content/uploads/sites/2/2020/08/Combine-ing-the-Old-with-the-New-360iDev-August-2020.001-2.png?fit=640%2C336&ssl=1
---

Most of the APIs from Apple come from an era of Objective-C and the Delegation Pattern. With this in mind, the challenge is figuring how to adapt for SwiftUI. Specifically we want to create Publishers from delegates using Combine.

For instance, [my app Heartwitch is an Apple Watch app for live streamers](https://heartwitch.app). In this case, It uses HealthKit which implants the delegation pattern frequently. Additionally, I am using newer technologies such as Vapor 4, Independent Watch Apps, and most importantly SwiftUI.

* * *

In this series of articles, I’d like to go into detail regarding the process of adapting an older API for Combine. Specifically **we are going to be building a basic SwiftUI app which displays your latitude and longitude with CoreLocation.** This includes:

- Creating Publishers from Delegates
- Using Function Reactive Programming to Transform Values
- Understanding FlatMap and Built-In Publishers

For this part, we'll be getting into how to create a `Protocol` and `Class` which will act as a go-between for the _Delegation Pattern_ and the _Reactive Functional Programming_ of **SwiftUI** and **Combine**.

## Gonna Delegate Like It’s 2009

For over a decade, Apple frequently used the Delegation Pattern for giving developers the ability to respond, update, and act in place of a UI object. This pattern has a plethora of benefits especially in Objective-C. However, with Swift and especially SwiftUI, this pattern becomes awkward.

This is where it becomes necessary to make a delegate respond in such a way that SwiftUI can handle updates.

With Apple’s older APIs, we typically see this:

```swift
protocol NSDelegate : NSObjectProtocol {
  func manager(_ manager: NSManager, doneWith data: AnyObject)
  func manager(_ manager: NSManager, grantedPermission: Bool)
}
class NSManager : NSObject {
  weak var delegate : NSDelegate?
  
  func requestAuthorization() {}
  func doThing () {}
}
```

In the case of **CoreLocation** we see this:

```swift
protocol CLLocationManagerDelegate : NSObjectProtocol {
  func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation])
  func locationManager(_: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus)
}
class CLLocationManager : NSObject {
  weak var delegate : CLLocationManagerDelegate?
  
  func requestWhenInUseAuthorization() {}
  func startUpdatingLocation () {}
}
```

In other words, we’ll need to create Combine _Publishers_ which our `ObservableObject` can listen or react to. Once the `ObservableObject` reacts properly, then the `View` will update accordingly. In the end, we should see this in our application:

![What we building](https://learningswift.brightdigit.com/wp-content/uploads/sites/2/2020/08/CoreLocation-AppPreview-300x125.jpg)

Before we setup our publishers, let's scaffold our `View` and `ObservableObject`.

## SwiftUI Scaffolding

Let's first start by building our SwiftUI View. In this case, we'll be creating a SwiftUI view along with an `ObservableObject`.

```swift
struct LocationView: View {
  // CLLocationManager is basically a singleton so an EnvironmentObject ObservableObject makes sense
  @EnvironmentObject var locationObject: CoreLocationObject
  var body: some View {
    VStack {
      // use our extension method to display a description of the status
      Text("\(locationObject.authorizationStatus.description)")
        .onTapGesture {
          self.locationObject.authorize()
        }
      // use Optional.map to hide the Text if there's no location
      self.locationObject.location.map {
        Text($0.description)
      }
    }
  }
}
```

This `LocationView` will simply display one line with a description of the location with a line describing the `CLAuthorizationStatus` using this extension:

```swift
extension CLAuthorizationStatus: CustomStringConvertible {
  public var description: String {
    switch self {
    case .authorizedAlways:
      return "Always Authorized"
    case .authorizedWhenInUse:
      return "Authorized When In Use"
    case .denied:
      return "Denied"
    case .notDetermined:
      return "Not Determined"
    case .restricted:
      return "Restricted"
    @unknown default:
      return "🤷‍♂️"
    }
  }
}
```

Now let's go ahead and define our `ObservableObject`, named `CoreLocationObject`:

```swift
import Combine
import CoreLocation
import SwiftUI
class CoreLocationObject: ObservableObject {
  @Published var authorizationStatus = CLAuthorizationStatus.notDetermined
  @Published var location: CLLocation?
  init() { }
}
```

Lastly, make sure that you set the `EnvironmentObject` in your application using:

```swift
LocationView().environmentObject(CoreLocationObject())
```

Now, we have our scaffolding setup, let's plug-in _CoreLocation_.

## Extending Delegates into Combine Publishers

![How to interface between the delegate and the ObservableObject](https://learningswift.brightdigit.com/wp-content/uploads/sites/2/2020/08/Combine-ing-the-Old-with-the-New-360iDev-August-2020.001-2-1024x538.png)

With the Delegation Pattern, the Delegate (in this case `CoreLocationManagerDelegate`) will receive location updates. Therefore it is the ideal object to create publishers for our `ObservableObject`.

In order for our `ObservableObject` to react to _CoreLocation_ changes, the delegate will have to create _Publishers_ for us. With this in mind, I have extended to delegate to be a _**Publicist**_. That is to say the _Delegate_ will also be a _Publisher Factory_.

```swift
protocol CLLocationManagerCombineDelegate: CLLocationManagerDelegate {
  func authorizationPublisher() -> AnyPublisher<CLAuthorizationStatus, Never>
  func locationPublisher() -> AnyPublisher<[CLLocation], Never>
}
```

In the case of our application, we are displaying the authorization status of _Core Location_ as well as the latitude and longitude. Therefore, we only need two methods implemented for our publishers.

Here is the implementation of our new protocol:

```swift
class CLLocationManagerPublicist: NSObject, CLLocationManagerCombineDelegate {
  let authorizationSubject = PassthroughSubject<CLAuthorizationStatus, Never>()
  let locationSubject = PassthroughSubject<[CLLocation], Never>()
  func authorizationPublisher() -> AnyPublisher<CLAuthorizationStatus, Never> {
    return Just(CLLocationManager.authorizationStatus())
      .merge(with:
        authorizationSubject.compactMap { $0 }
      ).eraseToAnyPublisher()
  }
  func locationPublisher() -> AnyPublisher<[CLLocation], Never> {
    return locationSubject.eraseToAnyPublisher()
  }
  func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    locationSubject.send(locations)
  }
  func locationManager(_: CLLocationManager, didFailWithError _: Error) {
    // Implement to avoid crashes
    // Extra Credit: Create a publisher for errors :/
  }
  func locationManager(_: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
    authorizationSubject.send(status)
  }
}
```

Let’s breakdown how this class works.

## The Power of Type Erasing Publishers

**Our Publicist doesn’t need to hold onto any values. In the end, it solely serves the purpose of transforming data from the `CoreLocationManager` to the `ObservableObject`.** For this reason, we’ll be using a `PassthroughSubject` for the `CLLocation` and `CLAuthorizationStatus`. That is to say, `PassthroughSubject` doesn't hold any values as they receive values but passes them on.

With the `PassthroughSubject` properties in place, our delegate can send the values received from the delegate methods to the subjects.

Creating our first _publisher_ for `CLLocation` is fairly simple:

```swift
class CLLocationManagerPublicist: NSObject, CLLocationManagerCombineDelegate {
...
  let locationSubject = PassthroughSubject<[CLLocation], Never>()
  func locationPublisher() -> AnyPublisher<[CLLocation], Never> {
    return locationSubject.eraseToAnyPublisher()
  }
  func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    locationSubject.send(locations)
  }
...
}
```

What’s important to realize is that we need to implement type erasure using `eraseToAnyPublisher`. The introduction of SwiftUI and Combine included improvements to Swift. These improvement allow for powerful transformations which can result in fairly complex Generic Types. For instance our `authorizationPublisher` has a return type `AnyPublisher`:

```swift
  func authorizationPublisher() -> AnyPublisher<CLAuthorizationStatus, Never> {
    return Just(CLLocationManager.authorizationStatus())
      .merge(with:
        authorizationSubject.compactMap { $0 }
      ).eraseToAnyPublisher()
  }
```

Without `eraseToAnyPublisher`, the return type would be:

```swift
Publishers.Merge<Just<CLAuthorizationStatus>, Publishers.CompactMap<PassthroughSubject<CLAuthorizationStatus, Never>, CLAuthorizationStatus>>
```

Likewise with the `locationPublisher`, the return type would be:

```swift
PassthroughSubject<[CLLocation], Never>
```

In the end, this makes creating Protocols and Return Types fairly complex. **As far as the `ObservableObject` is concerned, it doesn’t care how the publisher is transformed but the result types returned.**

Therefore our Protocol only needs a `AnyPublisher` return type. In the end, we can both simplify and hide the method of functional transformation using `eraseToAnyPublisher`. Likewise, the implementation calls `eraseToAnyPublisher` to reduce the return type and match the Protocol's method signature.

Now that we have figured out how to creating matching Publisher types, let’s transform `CLAuthorizationStatu`s so it is usable within the view.

## Transforming Authorization Status into a Publisher

While our `locationSubject` reflects the values from _CoreLocation_, the `authorizationSubject` will be out of sync from the reality of _CoreLocation_’s status. For this reason, we’ll need to write some code include the initial status along with whatever the `PassthroughSubject` receives.

```swift
  func authorizationPublisher() -> AnyPublisher<CLAuthorizationStatus, Never> {
    return Just(CLLocationManager.authorizationStatus())
      .merge(with:
        authorizationSubject
      ).eraseToAnyPublisher()
  }
```

While `CoreLocationManagerDelegate` sends updates to `authorizationStatus`, we need to have access to the initial status via `CLLocationManager.authorizedStatus`. Luckily, Combine includes a built-in _Publisher_ for single values using `Just`.

`Just` gives us the initial value published however we need to include the remaining published values from our `PassthroughSubject`. For this reason, we can use `merge` to join the intial value with the result from the our `authorizationSubject`:

![How to transform CLAuthorizationStatus into something useful](https://learningswift.brightdigit.com/wp-content/uploads/sites/2/2020/08/optimized.gif)

We have the publisher factories setup now!

Interested in learning more about Combine, check out my podcast episode with Donny Wals:

{% podcast https://dev.to/empowerappsshow/practical-combine-with-donny-wals %}

## What's Next?

In our next part in this series, we'll learn how to use this implementation in our `ObservableObject` `CoreLocationObject`. **Specifically, we'll be getting into power of function programming within Reactive Functional Programming.** Enjoy!