Home iOS & Swift Books watchOS With SwiftUI by Tutorials

6
Notifications Written by Scott Grosch

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Local and remote notifications are a great way to inform your users about new or relevant information. There may be new content available, it might be their turn in the game or they may have just won the lottery. If your app has an accompanying iPhone app that supports notifications, by default, your Apple Watch will display the notification when appropriate. However, you can do better!

Notification is a massive topic that could fill a book all by itself. This chapter will focus solely on some of the differences you must be aware of when working with watchOS. You can learn everything you need to know about push notifications in our book, Push Notifications by Tutorials, which is available as part of the professional subscription.

Where did it go?

Apple tries to determine the best target device to receive a notification. If you only have an Apple Watch, it’ll go there. However, if you use a watch and another device, the destination depends not only on the type of notification but also on its source.

The diagram below will help you understand how Apple chooses which device should display the notification. As you can see, the notification type, whether it is Local, Remote or a Background notification, will define where it should go. For the first two options, local and background, it will prioritize the Apple Watch. Local Notifications on the other hand will prioritize depending on the source. Check the following image to see the different paths:

Remote Display on phone Start No Watch Kit extension Source Sent directly to Apple Watch Sent directly to Apple Watch No iPhone unlocked and screen on Notification Type Yes iOS app Local Background Yes Yes Yes Apple Watch on wrist and unlocked Display on Apple Watch Display on phone No No

You’ll notice two locations in the diagram where it asks if Apple sent the notification directly to the watch. In watchOS 6 and later, the Apple Watch is a valid target for remote and background notifications. The Apple Watch extension receives a unique device token when registering for remote notifications, just like in iOS.

Short looks

When the Apple Watch receives a notification, it notifies the user via a subtle vibration. If the user views the notification by raising their wrist, the Apple Watch shows an abbreviated version called a short look. If the user views the notification for more than a split second, the Apple Watch will offer a more detailed version, or long look.

Long looks

The long look is a scrolling interface you can customize, with a default static interface or an optional dynamically-created interface. Unlike the short look interface, the long look offers significant customization.

Local notifications

Pawsome is for all cat lovers who procrastinate during the day by looking at cute cat pictures. The Pawsome app will make this easier by interrupting you throughout the day with cute cat pictures that are certain to trigger a smile… unless you’re a dog person!

Getting started

Open the Pawsome starter project in Xcode. Then build and run the Pawsome WatchKit App scheme. You’ll see a collection of cute kitty cats that you can easily browse:

Testing notifications with the simulator

Switch to the Pawsome WatchKit App (Notification) scheme and rerun the project. This time, instead of seeing those adorable cats, you’ll see a pretty boring notification:

WKNotificationScene(
  controller: NotificationController.self,
  category: "myCategory"
)
WKNotificationScene(
  controller: NotificationController.self,
  category: LocalNotifications.categoryIdentifier
)

Custom long look notification

Edit NotificationController.swift, and you’ll see body returns an instance of NotificationView. The controller is where you receive and parse the notification. The view is then where you use the data gathered by the controller.

import SwiftUI

struct NotificationView: View {
  // 1
  let message: String
  let image: Image

  // 2
  var body: some View {
    ScrollView {
      Text(message)
        .font(.headline)

      image
        .resizable()
        .scaledToFit()
    }
  }
}

struct NotificationView_Previews: PreviewProvider {
  static var previews: some View {
    // 3
    NotificationView(
      message: "Awww",
      image: Image("cat\(Int.random(in: 1...20))")
    )
  }
}
// 1
var image: Image!
var message: String!

// 2
override var body: NotificationView {
  return NotificationView(message: message, image: image)
}

// 3
override func didReceive(_ notification: UNNotification) {
  let content = notification.request.content
  message = content.body

  let num = Int.random(in: 1...20)
  image = Image("cat\(num)")
}

let validRange = 1...20

if
  let imageNumber = content.userInfo["imageNumber"] as? Int,
  validRange ~= imageNumber {
  image = Image("cat\(imageNumber)")
} else {
  let num = Int.random(in: validRange)
  image = Image("cat\(num)")
}
, "imageNumber": 5

Remote push notifications

Most apps use push notifications, not local notifications. You’re probably wondering why I spent all that time on something that’s used less frequently. Well, the answer is that Apple made push notifications much easier on watchOS than they are on iOS.

Token creation

This chapter assumes you already created your push notification token, a file you download from the Apple developer portal that ends with a p8 extension. If you need help generating your token, please see our Push Notifications by Tutorials book.

Create a WKExtensionDelegate

In iOS, you register for push notifications using AppDelegate. That class doesn’t exist on watchOS. Instead, you use WKExtensionDelegate. Create a new file called ExtensionDelegate.swift and paste:

import WatchKit
import UserNotifications

// 1
final class ExtensionDelegate: NSObject, WKExtensionDelegate {
  // 2
  func didRegisterForRemoteNotifications(withDeviceToken deviceToken: Data) {
    print(deviceToken.reduce("") { $0 + String(format: "%02x", $1) })
  }

  // 3
  func applicationDidFinishLaunching() {
    async {
      do {
        let success = try await UNUserNotificationCenter
          .current()
          .requestAuthorization(options: [.badge, .sound, .alert])

        guard success else { return }

        // 4
        await MainActor.run {
          WKExtension.shared().registerForRemoteNotifications()
        }
      } catch {
        print(error.localizedDescription)
      }
    }
  }
}
@WKExtensionDelegateAdaptor(ExtensionDelegate.self)
private var extensionDelegate

The MVC of push notifications

Instead of making you copy and paste a ton of code, I’ve provided a Remote Notifications group in the starter project, which contains the relevant files for a push notification.

Add the capability

Xcode will perform magic if you add the Push Notifications capability. In the Project navigator, Command‑1, select the project name, Pawsome. Then on the right, in the project editor, select the extension target. Make sure you choose the extension, not the app. Generally, that’ll be the last target listed.

Add a scheme

Edit the current scheme via your preferred method. I like to press the Command+< keyboard shortcut.

{
  "aps": {
    "alert": {
      "body": "Lorem ipsum dolor sit amet, consectetur...",
      "title": "Lorem Ipsum",
    },
    "category": "lorem"
  },

  "date": "2021-04-01T12:00:00Z"
}

WKNotificationScene(
  controller: RemoteNotificationController.self,
  category: RemoteNotificationController.categoryIdentifier
)

Interactive notifications

Tap Show details. Did something unexpected happen? An average user would expect to see details. Instead, you were taken into the app and shown a cat picture. Surprisingly, that’s by design.

override class var isInteractive: Bool { true }

Styling

If the default colors don’t work well with your app’s theme, you can override a few properties to perform minimal customization.

override class var sashColor: Color? {
  Color(red: 0, green: 156 / 255, blue: 83 / 255)
}
override class var titleColor: Color? { Color.purple }
override class var subtitleColor: Color? { Color.orange }
override class var wantsSashBlur: Bool { true }

APNs request

A key difference between iOS notifications and watchOS notifications is that you must include the apns‑push‑type HTTP header for the Apple Watch to receive a notification.

Key points

  • How Local & Remote Notifications work with Apple Watch.
  • Short & Long looks and how to customize them.
  • Testing Push Notifications on Apple Watch
  • Ensure your server includes the apns‑push‑type HTTP header.

Where to go from here?

In this chapter, you tested Watch notifications, learned about short look and long look interfaces and how they differ. Most impressively, you built a custom, dynamically updating, long look local notification for the Apple Watch.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

© 2022 Razeware LLC

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.