iOS Life

  • Home
  • ABOUT
  • SERVICES
  • PORTFOLIO
  • BLOG
  • Contact

Tutorial: Building an Apple Watch Notification

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.

notification_static_dynamic_2x

notification_process_2x

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

  1. Begin by adding all of the labels needed for the Interface within the Storyboard under the Dynamic Notification Interface.
  2. Drag in a Group object from the Object Library for each one of the timezones.
    Screen Shot 2015-04-20 at 3.55.05 PM

  3. 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.
  4. 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.
  5. 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()
    
  6. 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);
            }
        }
    
  7. 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

    1. 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.
    2. Build and Run your app against the Notification target.
      Screen Shot 2015-04-20 at 4.00.35 PM

    3. 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).
      Screen Shot 2015-04-17 at 1.44.26 PM

    4. 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.
      Screen Shot 2015-04-20 at 4.02.20 PM

    You can grab the full source code for this tutorial. Note: Built for Xcode 6.3 (Swift 1.2).

    Apr 20, 2015 Brian Coleman
Tutorial: Upgrading to Swift 1.2Tutorial: Building an Apple Watch Glance
You Might Also Like
 
Tutorial: Get Facebook Friends using the Facebook SDK in Swift
 
Tutorial: Receipt Validation in Swift
7 years ago Swift, Tutorials, Watchios8, swift, tutorial, watch11,884
Follow Me
    
Categories
  • About Me
  • Frameworks
  • My Apps
  • News
  • Strategy
  • Swift
  • Tools
  • Tutorials
  • tvOS
  • Uncategorized
  • Videos
  • Watch
Archives
  • May 2016
  • January 2016
  • October 2015
  • July 2015
  • May 2015
  • April 2015
  • March 2015
  • November 2014
  • October 2014
  • September 2014
  • July 2014
  • June 2014
  • September 2013
  • August 2013
  • July 2013
  • June 2013
  • May 2013
  • April 2013
  • March 2013
  • February 2013
brianjcoleman on Twitter
  • Classix is still holding in the top charts on Apple TV in Canada. #55 Free, #23 Top Grossing. #tvos #appletv #app https://t.co/xuEJiT4rro, Jul 14
  • New Blog Post: "Classix for iPhone, iPad & Apple TV” #iOSDev #ios #swift #swiftlang #SwiftDevs #AppleTV Read here: https://t.co/uF6w3gYOot, May 20
  • New Blog Post: "How to test your app for IPv6 compatibility” #iOSDev #ios #swift #swiftlang #SwiftDevs Read here: https://t.co/SveichSUep, May 6

Recent Posts
  • Classix for iPhone, iPad & Apple TV
  • Tutorial: How to test your app for IPv6 compatibility
  • Tutorial: Testing SSL using Charles Proxy on an iOS Device
  • Tutorial: 3D Touch – Quick Actions in Swift
  • tvOS Tutorial: Top Shelf in Swift
Featured Apps
Classix
Sportsnet
TAGS
tutorialswiftios8iosobjective-cvideostrategygamesframeworknewsappsmonitizeios7applefacebookwatchtoolstvosios9bookdesignsocialapiprovisiontutorialsbooksiapiTunes ConnectIPv6
Search
ABOUT
Brian is a Lead iOS/tvOS Developer from Toronto with over 18 years of multifaceted experience including development, design, business analysis and project management.
MOST VIEWED
Tutorial: How To Use Login in Facebook SDK 4.1.x for Swift
163,489 views
Tutorial: How to test your app for IPv6 compatibility
102,074 views
Tutorial: How to use Auto Layout in Xcode 6
89,234 views
FOLLOW ME
    
Email Subscription
Sign up for my newsletter to receive the latest news and tutorials posted.

Enter your email address:

2013-2017 © Brian Coleman