
Notifications on Apple Watch facilitate quick, lightweight interactions for local and remote notifications. These interactions occur in two stages, which are managed by the short-look and long-look interfaces. The short-look interface appears when a local or remote notification first arrives. A short look presents a discreet, minimal amount of information to the user—preserving a degree of privacy. If the wearer’s wrist is lowered, the short-look interface disappears. The long-look interface appears when the wearer’s wrist remains raised or when the wearer taps the short-look interface. It provides more detailed information and more functionality—and it must be actively dismissed by the wearer.
Static vs Dynamic Interfaces
The custom long-look notification interface consists of two separate interfaces: one static and one dynamic. The static interface is required and is a simple way to display the notification’s alert message and any static images and text that you configure at design time. The dynamic interface is optional and gives you a way to customize the display of your notification’s content.
Use the static notification interface to define a simple version of your custom notification interface. The purpose of a static interface is to provide a fallback interface in the event that your WatchKit extension is unable to configure the dynamic interface in a timely manner.
A dynamic notification interface lets you provide a more enriched notification experience for the user. With a dynamic interface, you can display more than just the alert message. You can incorporate additional information, configure more than one label, display dynamically generated content, and so on.
To implement a dynamic notification interface, you must create a custom WKUserNotificationInterfaceController subclass. How you implement that subclass determines what information is displayed in the notification interface.
We’ll cover how to customize a dynamic notification, since for the static ones, “it just works”.
To build a Dynamic Notification you need to already have a Watch App, if you’d like to learn how to do that, read
Tutorial: Building an Apple Watch App In that tutorial we walk through building an app that shows the time across all four Canadian and U.S. timezones.
Customize a Dynamic Notification
- Begin by adding all of the labels needed for the Interface within the Storyboard under the Dynamic Notification Interface.
- Drag in a Group object from the Object Library for each one of the timezones.
- Now that we have our UI defined, let’s connect the IBOutlets with the code in the “NotificationController.swift” file. You can do this many ways, just like an iOS app. Either Control-drag the UI object from the storyboard into the NotificationController.swift or define the code and link up using the Connections Inspector.
- After the UI is all hooked up, let’s add some code for the NotificationController.swift so when we receive a notification we’ll show the current timezone times to the user.
- The following is to define our labels that will be in the notification. Don’t forget to add the alertLabel and bodyLabel or else the user will not see the pay load for the remote notification sent to them.
@IBOutlet var easternLabel : WKInterfaceLabel? = WKInterfaceLabel() @IBOutlet var centralLabel : WKInterfaceLabel? = WKInterfaceLabel() @IBOutlet var mountainLabel : WKInterfaceLabel? = WKInterfaceLabel() @IBOutlet var pacificLabel : WKInterfaceLabel? = WKInterfaceLabel() @IBOutlet var alertLabel : WKInterfaceLabel? = WKInterfaceLabel() @IBOutlet var bodyLabel : WKInterfaceLabel? = WKInterfaceLabel()
- Next are the methods for handling the local and remote notifications. You need to add code in here for the Dynamic Interface to be enabled, if you leave it blank you will get the Static Interface for the notification with the title displayed.
override func didReceiveLocalNotification(localNotification: UILocalNotification, withCompletion completionHandler: ((WKUserNotificationInterfaceType) -> Void)) { // This method is called when a local notification needs to be presented. // Implement it if you use a dynamic notification interface. // Populate your dynamic notification interface as quickly as possible. self.alertLabel?.setText(localNotification.alertTitle) self.updateClocks() // After populating your dynamic notification interface call the completion block. completionHandler(.Custom) } override func didReceiveRemoteNotification(remoteNotification: [NSObject : AnyObject], withCompletion completionHandler: ((WKUserNotificationInterfaceType) -> Void)) { self.updateClocks() if let remoteaps:NSDictionary = remoteNotification["aps"] as? NSDictionary{ if let remoteAlert:NSDictionary = remoteaps["alert"] as? NSDictionary{ handleNotification( remoteAlert ); } } completionHandler(.Custom) } func handleNotification( alert : AnyObject? ){ if let alert: AnyObject = alert, let remotetitle = alert["title"] as? String{ println( "didReceiveRemoteNotification::remoteNotification.alert \(remotetitle)" ) self.alertLabel!.setText(remotetitle); } if let alert: AnyObject = alert, let remotebody = alert["body"] as? String{ //println( "didReceiveRemoteNotification::remoteNotification.alert \(remotetitle)" ) self.bodyLabel!.setText(remotebody); } }
- Lastly below is the method to return the time per timezone to be displayed in the notification.
func updateClocks() { var time: NSDate = NSDate() let formatter:NSDateFormatter = NSDateFormatter(); var timeZone = NSTimeZone(name: "UTC") formatter.timeZone = timeZone formatter.dateFormat = "h:mm a" var formattedString = formatter.stringFromDate(time) var formatDateString = formattedString.stringByReplacingOccurrencesOfString(" p", withString: "PM", options: nil, range: nil) formattedString = formattedString.stringByReplacingOccurrencesOfString(" a", withString: "AM", options: nil, range: nil) timeZone = NSTimeZone(name: "US/Eastern") formatter.timeZone = timeZone formatter.dateFormat = "h:mm a" formattedString = formatter.stringFromDate(time) formatDateString = formattedString.stringByReplacingOccurrencesOfString(" p", withString: "PM", options: nil, range: nil) formattedString = formattedString.stringByReplacingOccurrencesOfString(" a", withString: "AM", options: nil, range: nil) self.easternLabel?.setText(formatDateString) timeZone = NSTimeZone(name: "US/Pacific") formatter.timeZone = timeZone formatter.dateFormat = "h:mm a" formattedString = formatter.stringFromDate(time) formatDateString = formattedString.stringByReplacingOccurrencesOfString(" p", withString: "PM", options: nil, range: nil) formattedString = formattedString.stringByReplacingOccurrencesOfString(" a", withString: "AM", options: nil, range: nil) self.pacificLabel?.setText(formatDateString) timeZone = NSTimeZone(name: "US/Mountain") formatter.timeZone = timeZone formatter.dateFormat = "h:mm a" formattedString = formatter.stringFromDate(time) formatDateString = formattedString.stringByReplacingOccurrencesOfString(" p", withString: "PM", options: nil, range: nil) formattedString = formattedString.stringByReplacingOccurrencesOfString(" a", withString: "AM", options: nil, range: nil) self.mountainLabel?.setText(formatDateString) timeZone = NSTimeZone(name: "US/Central") formatter.timeZone = timeZone formatter.dateFormat = "h:mm a" formattedString = formatter.stringFromDate(time) formatDateString = formattedString.stringByReplacingOccurrencesOfString(" p", withString: "PM", options: nil, range: nil) formattedString = formattedString.stringByReplacingOccurrencesOfString(" a", withString: "AM", options: nil, range: nil) self.centralLabel?.setText(formatDateString) }
Test Your Notification
- When you run the app you’ll see a blank view since our app doesn’t do anything except create the Watch App and the watch is shown in an External Display.
- Build and Run your app against the Notification target.
- Then, switch over to your simulator and check that you have the External Display set for the watch. iOS Simulator > Hardware > External Displays (try both sizes to see how it looks).
- You should see your app in the Notification in the Watch simulator. Since it’s in the simulator it will provide some sample payload “Optional title” and “Test message” to be displayed.
You can grab the full source code for this tutorial. Note: Built for Xcode 6.3 (Swift 1.2).