watchOS: Complications

Feb 7 2023 · Swift 5.6, watchOS 8.5, Xcode 13

Part 1: Introduction to Complications

08. Update with Push Notifications

Episode complete

Play next episode

Next
About this episode
Leave a rating/review
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 07. Update with Background URL Downloads Next episode: 09. Tint Complications

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Notes: 08. Update with Push Notifications

Note: At the time of writing, watchOS has a bug—verified by Apple—that sometimes prevents your app from registering for PushKit notifications. Apple told me it believes it has determined the root cause. However, there’s no ETA on when the fix will be available.

Transcript: 08. Update with Push Notifications

While background tasks and background URL downloads work well, they’re not always the best solution.

watchOS may kill your app, or it may crash. I know, your apps are 100% bug-free, but that stuff your colleague writes…

  • If you control the server your app pulls data from, it may make more sense to implement complication updates via push notifications.
  • Using PushKit, you can send up to 50 updates per day to your Apple Watch.

PushKit registration

Complication push notifications are a bit different from standard remote push notifications.

To start, we’ll create a Push Notification Provider. So, let’s add a new file for that.

import Foundation
import PushKit

final class PushNotificationProvider: NSObject {

}

You’ll conform to a delegate protocol in a moment, so, again, you must subclass NSObject.

final class PushNotificationProvider🟩: NSObject {

}

Initialize PushKit, and specify that the delegate methods should be called on the main UI thread.

  let registry = PKPushRegistry(queue: .main)

Then, set up the initializer, and assign this class as the delegate.

  override init() {
    super.init()
    registry.delegate = self
  }

To finish up initialization, specify the desiredPushTypes to let PushKit know you’re sending complication updates.

    registry.desiredPushTypes = [.complication]
  }

PKPushRegistryDelegate

Now to start implementing the delegate, open an extension and adopt PKPushRegistryDelegate.

extension PushNotificationProvider: PKPushRegistryDelegate {

}

The method we need, here, is pushRegistry(_:didUpdate:for:). watchOS calls this method when your app successfully registers with PushKit.

  func pushRegistry(
    _ registry: PKPushRegistry,
    didUpdate pushCredentials: PKPushCredentials,
    for type: PKPushType
  ) {
  
  }

PushKit suffers from the same usability annoyance as standard push notifications: watchOS gives you a Data token instead of a usable string.

So, you’ll need to convert the unusable Data token to a string that can be sent to your server.

    let token = pushCredentials.token.reduce("") {
      $0 + String(format: "%02x", $1)
    }
    print(token)

    // Send token to your server.

Be sure you somehow specify, on your server, that the token is for PushKit, not a normal push notification.

Receiving a push notification

Now that things are set up, we can handle receiving a PushKit notification with another delegate method:

func pushRegistry(
  _ registry: PKPushRegistry,
  didReceiveIncomingPushWith payload: PKPushPayload,
  for type: PKPushType
) async {

}

Notice that the method signature includes the async keyword. Having this method asynchronous makes life easier as the updates you perform are likely asynchronous.

The payload sent with the push notification is available in the payload’s dictionaryPayload property. We’re just going to print it out, here.

  print(payload.dictionaryPayload)

Once you take appropriate action based on the incoming payload, tell your complications to update.

  await ExtensionDelegate.updateActiveComplications()
}

Initialize the provider

The only step left is to initialize the Push Notification Provider, which we can do in UpdatesApp.swift.

private let push = PushNotificationProvider()

apns-topic

Note: There’s one special consideration when sending a PushKit notification. The apns-topic header should be the name of your extension’s bundle identifier with .complication appended to it.

For example, when sending a notification to the sample app, you would set the apns-topic to com.yourcompany.Updates.watchkitapp.watchkitextension.complication.

Testing

Other than the extra text you need to add to the apns-topic, there’s nothing special about testing push notifications, here. If you’re able to register your app for PushKit notifications, you can test it the same way you would do with an iPhone.