7

Combine & CoreLocation, Part 2 – Receiving & Handling Events

 3 years ago
source link: https://learningswift.brightdigit.com/combine-corelocation-receiving-handling-events/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

In our previous post, we talked about how to build a Publisher Factory or Publicist. In this post, we’ll talk about how receiving and handling Events in our ObservableObject from our new Publicist class.

Receiving and Handling Events in our ObservableObject

Our ObservableObject needs to accomplish a few things to update our SwiftUI while listening to our CoreLocation publishers. First of all we need to:

  1. Create the CoreLocation Manager
  2. Create our CoreLocation Publicist
  3. Connect the Publicist as the Delegate of the Manager
  4. Create our Authorization Status and Location Publishers

With this in mind, here is how that looks:

class CoreLocationObject: ObservableObject {
  @Published var authorizationStatus = CLAuthorizationStatus.notDetermined
  @Published var location: CLLocation?

  let manager: CLLocationManager
  let publicist: CLLocationManagerCombineDelegate

  var cancellables = [AnyCancellable]()

  init() {
    let manager = CLLocationManager()
    let publicist = CLLocationManagerPublicist()

    manager.delegate = publicist

    self.manager = manager
    self.publicist = publicist

    let authorizationPublisher = publicist.authorizationPublisher()
    let locationPublisher = publicist.locationPublisher()
	...

Next, we can actually deal with receiving and handling events from our Publishers. Firstly, let’s deal with the CLAuthorizationStatus.

Receiving and Handling Events with Sink and Assign

As far the CLAuthorizationStatus, there are two things we need to do when the authorization status changes:

  • Trigger Location Update when CLAuthorizationStatus is valid
  • Assign the CLAuthorizationStatus to our @Published CLAuthorizationStatus Property authorizationStatus

Using Sink For Calling Methods

Let’s first deal with triggering location updates by creating a method to begin those updates:

class CoreLocationObject: ObservableObject {
  @Published var authorizationStatus = CLAuthorizationStatus.notDetermined
  @Published var location: CLLocation?

  let manager: CLLocationManager

...

  func beginUpdates(_ authorizationStatus: CLAuthorizationStatus) {
    if authorizationStatus == .authorizedAlways || authorizationStatus == .authorizedWhenInUse {
      manager.startUpdatingLocation()
    }
  }
}

Our beginUpdate method will take in the CLAuthorizedStatus as it comes in from the Publisher. As a result, if the status allows for location update then it calls startUpdatingLocation on the CLLocationManager.

With this in mind, let’s connect the authorizationPublisher to the beginUpdates method:

class CoreLocationObject: ObservableObject {

  ...
  var cancellables = [AnyCancellable]()

  init() {
    let publicist = CLLocationManagerPublicist()
   
    ...
    
    let authorizationPublisher = publicist.authorizationPublisher()

    // trigger an update when authorization changes
    authorizationPublisher
      .sink(receiveValue: beginUpdates)
      .store(in: &cancellables)
	...

In this case, we are using sink and passing a reference to our new method. sink returns a Cancellable which we’ll need to make sure is disposed properly. Therefore, we use .store to make sure that Cancellable is stored correctly in our `cancelables` Array.

Now we should be correctly starting location updates when permission is granted. Next, let’s store the CLAuthorizationStatus in our @Published property so we can display it in our SwiftUI view.

Receiving and Handling Events with Assign (in iOS 13 and iOS 14)

One of the first things you learn as an iOS developer using UIKit is the importance of making UI updates on the main dispatch queue. In other words, failure to do this results in incorrect UI updates or even crashing your app. Likewise, in SwiftUI, you need to specify UI updates on the main dispatch queue. Therefore we can use receive to ensure updates are on the main DispatchQueue.

...
      authorizationPublisher
        // since this is used in the UI,
        //  it needs to be on the main DispatchQueue
        .receive(on: DispatchQueue.main)
		...

As far as assigning we have a few options, we can stick with sink to assign our value the published property:

...
      authorizationPublisher
        // since this is used in the UI,
        //  it needs to be on the main DispatchQueue
        .receive(on: DispatchQueue.main)
		.sink(receiveValue: {
          self.authorizationStatus = $0
        })
...

However, we could simplify this with assign:

...
      authorizationPublisher
        // since this is used in the UI,
        //  it needs to be on the main DispatchQueue
        .receive(on: DispatchQueue.main)
        // store the value in the authorizationStatus property
        .assign(to: &$authorizationStatus)
...

This new assign available in all the new 2020 OSes (iOS 14, macOS 11, watchOS 7, etc…) allows us to assign the value directly to the publisher using the & (for an in-out parameter) and $ (for the publisher).

Additionally no Cancellable is returned and therefore not needed to be stored.

Sink vs Assign when Receiving and Handling Events

Combine-ing-the-Old-with-the-New.001-1024x204.png

Choosing the correct method can be difficult but here’s some guidelines:

In the next blog post, I’ll cover some more advance functional programming techniques in Combine when we transform our CLLocation to something more useful.

…when the next post is out
By clicking submit, you agree to share your email address with the site owner and Mailchimp to receive marketing, updates, and other emails from the site owner. Use the unsubscribe link in those emails to opt out at any time.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK