iOS Development

Apple Watch Notifications

Remote and local notifications are not at all a new thing. From the very day – the 17th of June, 2009, they were introduced, they have always been, arguably, the most favourite mechanism to deliver messages to the user. The messages has ranged from proximity alert to a store, new update to an app, directional instructions to illegal promotions.

 But in Apple watch, notifications take a new dimension. From the way the notifications are designed for  Watch, it appears quite evident that Apple has spent considerable amount of energy to make them better and more meaningful. Following is the flow showing how a notification is presented to an  Watch user.

  1. According to the  Watch Human Interface Guidelines and WatchKit Programming Guide, when an app running in the iPhone receives a notification, iOS decides where to show the notification. Though this is kind of a vague statement, but as of now there seems to be no control over this even if we specifically need the notification to be shown in  the  Watch. Also, I could not find any information on how iOS decides to show the notification where it decides to show. Guess we’ll have to wait a bit for to know that.
  2. If the notification is sent to the  Watch, the user feels a subtle vibration on his wrist or a mild audio cue based on the notification’s payload.
  3. Alarmed, as the user raises his hand, a very minimalistic screen is presented which is called Short Look interface. This is an immutable, non-scrollable screen that conveys the user the most important information about the notification. iOS designs them based on a predetermined template and present on the screen. This is  Watch’s interpretation of your everyday notification, with just a title provided by you in the notification payload.
  4. All work and no play makes Jack a dull boy. Who understands this better than Apple? So, here is the shining playful part. The customisable, scrollable, actionable notification screen. After the Short Look notification is displayed, if the user continues to keep his hand raised (in the hope that something else will happen…soon…well..anytime now…), or taps on the Short Look interface the Long Look interface is displayed.

Apple has given you freedom within the aesthetic boundary to design the Long Look interface. You can add buttons and graphics and customise the behaviour when the user taps on it.

But what happens if you don’t provide the Long Look interface? Well, Apple has a backup plan. iOS displays a default interface with the app icon, title string and alert message. Tapping on it launches the app.

OK, so let’s not allow Apple to have all the fun and design our own Long Look interface!

A Long Look interface has three major parts —

  • The Sash at the top of the screen — this includes the title string and the icon of the app
  • The content — this is your playground. Add any graphics and buttons
  • The dismiss button — this is always present, added by the iOS, and dismisses the notification once tapped

 In the Sash section, as a third party developer, you have basic freedom. You can change the tint color and title of the notification.

In the content, you have much more liberty. You can modify and design the label to show your notification message. You can add buttons, but not directly. All the buttons should come in a specific format through a JSON payload that will invoke the notification. The SDK already generates one  such JSON payload file while creating  notification scene for testing purpose.

Screen Shot 2014-12-04 at 02.29.48Changing the alert, title and category controls what the notification screen will display.

As you see above, in the “WatchKit Simulator Actions” array is holding a collection of buttons in the form of dictionary which can be used to add/remove/modify buttons in the notification.

To create a notification, create a new project and add a new Watch Target as discussed in my previous post. This time keep the “Include Notification Scene” checkbox selected to include our notification interfaces.

Screen Shot 2014-12-04 at 02.35.44

Include all the necessary app icon images. Apple could not think of any more, so they want only the following few dimensions :

  • 29 X 29
  • 36 X 36
  • 58 X 58
  • 80 X 80
  • 88 X 88
  • 196 X 196
  • 172 X 172

Xcode will generate two extra interfaces for you in the interface.storyboard (other than the usual screen for your watch app) inside your project. They are —

  • Static Interface — A notification interface that can be configured design time. It is mandatory to keep this interface in your app bundle.
  • Dynamic Interface — A notification interface that can be decorated with dynamic data in runtime. This is not mandatory.

When running the app, iOS first looks for Dynamic Interface. If not found it falls back to Static Interface. If the static interface suffices your n0tification requirement, it is safe to delete the dynamic interface. Also, it can be explicitly instructed not to show the dynamic interface.

For the time being, lets change the Static Interface. What we are trying to do here is —

  •  show a notification stating that Japan is already enjoying the new year with an action button.
  • Tapping on the “View” button the app will launch and
  • display the current time in Tokyo

Now create a new scheme for notification and map the notification executable to that scheme to view the  Watch notification in iOS simulator.

Screen Shot 2014-12-04 at 03.07.07

Screen Shot 2014-12-04 at 03.07.28

Screen Shot 2014-12-04 at 03.15.01

If you build and run your app now, the default static notification screen will show. Take into notice that the message text, button text everything is being pulled from the JSON file that is included in the bundle.  you can try changing them to see if the change takes place in the notification.

Before we do some action, lets modify the JSON file to suit our needs by changing title, message and category. (Screenshot above) Lets name them as follows:

"aps": {
        "alert": "Japan is already celebrating new year!",
        "title": "Happy New Year!",
        "category": "NewYear"
    }

Also, in the Interface.storyboard file, select the “Category” under Static Notification Interface Controller and in the attributes inspector, change it to “NewYear”. Make sure that the category names are matching in the JSON as well as in the storyboard. Otherwise, the app will not build at all.

Now we want the user to tap on the button and make something happen. Let’s add a date label to the interface of the  Watch app which will display the date based on the timezone set. Hook it to the InterfaceController as dateLabel.

Inside the Interface Controller, we can handle the notification like so:

@IBOutlet weak var dateLabel: WKInterfaceDate!
override func handleActionWithIdentifier(identifier: String?, forRemoteNotification remoteNotification: [NSObject : AnyObject]) {

        if let id = identifier {
            if id == "firstButtonAction" {

                var plistKeys: NSDictionary?
                var timeZones: NSDictionary?

                if let path = NSBundle.mainBundle().pathForResource("Timezones", ofType: "plist") {
                    plistKeys = NSDictionary(contentsOfFile: path)!
                    timeZones = plistKeys!["TimeZones"] as NSDictionary?
                }

                if let dict = timeZones {
                    NSLog("%@", dict.valueForKey("Tokyo") as String)
                    dateLabel.setTimeZone(NSTimeZone(name: dict.valueForKey("Tokyo") as String))
                }

            }
        }

    }

Now build and run the app. At first it will show your designed screen. Tapping on the dismiss button dismisses the notification screen. Tapping on “View” button shows Tokyo’s current time.

Right now, the app is showing the static notification screen. If you want to show a custom dynamic notification with data that can only dynamically be passed into the interface, we need to modify the “Dynamic Interface”. The Dynamic Interface is controlled by the NotificationController.swift. If you navigate there, you will find two functions commented out

override func didReceiveRemoteNotification(remoteNotification: [NSObject : AnyObject], withCompletion completionHandler: ((WKUserNotificationInterfaceType) -> Void))

override func didReceiveLocalNotification(localNotification: UILocalNotification, withCompletion completionHandler: (WKUserNotificationInterfaceType) -> Void)

Uncomment the

didReceiveRemoteNotification(remoteNotification: [NSObject : AnyObject], withCompletion completionHandler: ((WKUserNotificationInterfaceType) -> Void))

and make sure that the completionHandler is set to be .Custom

override func didReceiveRemoteNotification(remoteNotification: [NSObject : AnyObject], withCompletion completionHandler: ((WKUserNotificationInterfaceType) -> Void)) {
        
        // Tell WatchKit to display the custom interface.
        completionHandler(.Custom);
    }

Now if we make any modification to the dynamic interface, you will see that the dynamic interface with its changes is being shown as the notification screen. This is because, as I mentioned earlier, iOS searches for the custom dynamic interface first. If it can not find one, then only it loads the static one.

Try changing the .Custom to .Default to see your static interface.

You can download the whole project from Github — https://github.com/an-indya/WatchNotifications

Hope you will enjoy building for  Watch as much as I did. I will try putting in something more as I learn. Please do leave a reply and feel free to share if you like!

Hope this helps!

Advertisements
iOS Development

Watch and Learn!

“Tell me and I forget. Teach me and I remember. Involve me and I learn”
— Benjamin Franklin


It’s a very interesting time for iOS developers, not quite like ever before. We have a new faster, cleaner and better language and a new device to develop for. So, I decided to learn them together. You have guessed it quite right, this post is an effort to  assimilate my knowledge on Swift and WatchKit. I really got inspired by
Natasha the Robot, who has recently posted a series of excellent posts describing in depth about making  Watch apps. Her blog is worth a visit. Also she has a guest post in NSHipster as well! So, coming back to the point, I intend to follow her loosely as I embark on the journey to learn WatchKit and perform the same exercise in Swift. There is no better way to learn other than doing it ourselves, is there?WatchKit was released a few days back, and its probably the beginning of a new era. It is almost like the time when the first iPhone Development Kit was released for registered Apple developers. Though this time it’s a little disheartening to understand the limitations and constraints imposed on the  Watch apps, but knowing Apple, I am sure it won’t last long. Its a really clever move from Apple to make the Watch extremely lightweight and thus very much battery efficient. Still I believe – it’s just a matter of time before Apple finds a way to improve the hardware and battery life of the watches to make them self reliant and sufficient to host their own native standalone apps.

Before we start, here is something on NDA on WatchKit in case you are wondering:

A Note on NDAs – while an Apple developer account with NDA is required to download Xcode betas and actually use WatchKit, all of the reference documentation is publicly available at this link.  The programming guide is similarly available publicly here. Since all of this information is public, we don’t have to worry about NDA trouble.

http://www.willowtreeapps.com/blog/developer-perspective-on-apples-watchkit/


 

Architecture

Before we delve into coding, let me explain the architecture of  Watch apps. This will help us understand the apps better, let us take informed design decisions and help cultivate useful and innovative ideas leveraging the  Watch and its capabilities.

The  Watch apps are nothing but extensions of app extensions and are designed the same way as the recently introduced extensions are designed. So, the WatchKit apps will have 2 parts —

  1. The application extension which will be running on a paired iPhone
  2. The app installed in the  Watch

As a basic principle of App extensions in Apple platform, a container app and an app extension can not interact directly, however, they may do so indirectly through intermediator. The intermediator would be WatchKit.

All the heavy liftings, like – implementation of business logic and data manipulation etc. would be done in the app running on the iPhone, whereas the Watch app will contain the main story board and other UI resources. These resources will form the outward interface of the Watch app which will be powered by the iPhone app. In a simpler sense, the Watch app would be the face and the iPhone app will be the brain behind it.

WatchKit Architecture
Watch App Architecture

In the above diagram, the left hand side box represents the iPhone and the right side box  Watch. As the smaller boxes depict, the app extension (WatchKit Extension) runs on the iPhone and interacts with the WatchKit. The Watch App contains all the UI elements required for displaying the app and these resources can not be changed in run time. So, if you need to show a custom view at some point of time in the life cycle of the application, you would need to plan ahead and keep a hidden view already there in the Watch app which you can unhide and display.

So, when the user of the  Watch touches a notification or views a “glance” in the Watch App, the Watch invokes the app installed in it and opens up the appropriate storyboard scene and UI resources. The app then requests the WatchKit extension running on the iPhone through the WatchKit framework to respond to the events and updates the UI based on the response received from it. This communication takes place for all the user events like touch and other gestures registered by the Watch app. The code executed in the iPhone app is responsible for updating the UI of the Watch app and perform any necessary operations including generating dynamic content and sending over to the Watch app for display.

For the following codes we assume that Xcode 6.2 (beta at the time of writing this) or above is installed.

Hello, World!

I will stick to the tradition and start by creating an  Watch application which says to the wearer — “Hello, World!” Then we shall move on to create a little more complex and interesting things.

  • Create a new project in Xcode with “Single View Application”. Lets name it as “AppleWatchDemo”. Make sure you select the language as “Swift”, we need to learn Swift too — isn’t it?

 Screen Shot 2014-11-26 at 22.07.12

  • Add a new target by selecting “Edit –> Add Target” or by selecting the project file and then in the properties window, expand the target dropdown and select “Add Target”.
Screen Shot 2014-11-26 at 22.10.49 Screen Shot 2014-11-26 at 22.11.25
  •  In the template selection window, go to “Apple Watch” section and well, you know what template to choose here. 🙂

Screen Shot 2014-11-26 at 22.20.28

  • Click on next and if you want, uncheck the “Include Notification Scene” and “Glance Scene” checkboxes.

Screen Shot 2014-11-26 at 22.24.59

  • Click on “Finish”. As alway Apple promises, you already have a Watch App ready to be deployed. Only it’s blank. So let’s put something in it to show. We are going to show “Hello, World!” in a UILabel.
  • Now, go to the Interface.storyboard file of the Watch App. You will see an interface of the Watch is present there. At the top right there will be a time label, one of those fancy labels Apple has created which make you life easier by showing the time, or displaying count down timer. It’s a watch after all, why shall we be surprised? 🙂 As you can clearly guess, if you run the app now, it will show a small screen with current time at the top right corner.

Screen Shot 2014-11-26 at 22.56.21

  • Now lets add a label. Drag and drop a UILabel into the window. To centre align — select the label and go to the Attributes Inspector. Under “Position” section change both the drop downs to “Centre”. This is how the interface design works in  Watch. There is no fixed frame, no autolayout. Everything you layout on the screen will be laid out horizontally side by side. Now change the text to read “Hello, World!”.

Screen Shot 2014-11-26 at 23.08.51

  • And…you are done. Select the target to be Watch App and then run the project. You will see the iPhone simulator and the Watch Simulator launch together and the Watch display “Hello, World!”
  • Congrats! You have made your first  Watch App!

Delving Deeper in to the world of the clocks and watches

But we are serious developers, why should we be happy with just a “Hello World” app? We need more, isn’t it? Let’s build some tableview goodness.

Along with many interesting features, Apple has also provided the Watch with some extra UI elements which are enhancements of previous primitive ones. Timer and Date Labels are two of them. When a date label is displayed, it shows the current time at your convenient format, without having you to write a single line of code for it. Leveraging them we are going to build a world clock (watch!) application which will help us explore the tableview as well as the Date Labels. The final product will look something like this —

Final

As you can see, I am sitting in London and the current time is being displayed at the top right corner of the watch which is 9:43 PM.  The other cities are also displaying their respective times.

  • So, lets remove the Label displaying “Hello, World!” and add a Table to the Interface.storyboard, the main story board file of your watch app. You will see a Table Row Controller getting added to the Table automatically.
  • So, these will be the template for our rows. Lets add a Label to show the city name and a Date Label for the times. Vertically centre align the labels and set appropriate width the same way we did for Hello World label.
  • Set the Format of the date to be custom — hh:mm a which will display similar to 09.00 pm. Set the font to be “System Bold” and of size 13. Also, for the City label, let’s make the font to be System and size to be 13 as well.
  • The attribute inspector also provide lots of attributes to play with, tinker to your heart’s content!

 

Screen Shot 2014-11-27 at 21.49.18Screen Shot 2014-11-27 at 21.49.01
  • Finally, some code. Create a new file in the WorldWatch WatchKit Extension and name it LocalTimeRowController.swift. Make it inherit from NSObject and import WatchKit into it.
    import WatchKit
    class LocalTimeRowController: NSObject {
    @IBOutlet weak var countryLabel: WKInterfaceLabel!
    @IBOutlet weak var localTimeLabel: WKInterfaceDate!
    }
  • Now lets add a yellow background colour to the row and set its height to be “Fixed Height” and make it 30 Screen Shot 2014-11-27 at 22.14.06
  •  Move over to your Interface.storyboard in the WorldWatch Watch App and select the Table Row Controller in the left hand pane. Change the class name for the controller to be LocalTimeRowController.Screen Shot 2014-11-27 at 22.41.31
  • Also, change the Row controller identifier in the Attributes Inspector to be “LocalTimeRowController”.Screen Shot 2014-11-27 at 22.41.42
  • Create Outlets of the labels we created in the LocalTimeRowController. They will help us set the text and attributes.

Untitled

  • Since we will be showing times for multiple cities, we will need to know the timezone names for all the cities and how to refer them in the code. Fortunately this useful Gist provides what we are looking for in a nice plist.
    I have extracted out the relevant part and you can download it from here. Download the file and include the plist file into extension project.
  • Now head over to InterfaceController.swift and lets put the real logic there for populating the table. As you see, there is absolutely no code we are putting in the Watch app project. All the controlling logic code goes to the extension which is about to run in the iPhone. Implement the following method which populates the table —
      private func populateTable () {
            
            var plistKeys: NSDictionary?
            var timeZones: NSDictionary?
            
            if let path = NSBundle.mainBundle().pathForResource("Timezones", ofType: "plist") {
                plistKeys = NSDictionary(contentsOfFile: path)!
                timeZones = plistKeys!["TimeZones"] as NSDictionary?
            }
            
            if let dict = timeZones {
                table.setNumberOfRows(dict.count, withRowType: "LocalTimeRowController")
                var keyArray = dict.allKeys as [String]
                func alphabaticalSort(s1: String, s2: String) -> Bool {
                    return s1 < s2
                }
                
                var sortedCityNamesArray = sorted(keyArray, alphabaticalSort)
                for (index, key) in enumerate(sortedCityNamesArray) {
                    let row = table.rowControllerAtIndex(index) as LocalTimeRowController
                    row.countryLabel.setText((key as String))
                    var value: AnyObject? = dict[key as String]
                    row.localTimeLabel.setTimeZone(NSTimeZone(name: value as String))
                }
            }
        }
  •  Essentially, we are just taking all the City names (which are the keys from the plist file we included in the bundle) and displaying them in the City labels. On the other hand the values, which are name of time zones, are being used to set the time zone for the Date labels. So, each of them shows respective time for the timezones assigned to them.
  • Call the populateTable method from the init of the class.
        override init(context: AnyObject?) {
            super.init(context: context)
            populateTable()
        }
  •  Now select the executable target to be WorldWatch Watch App and run the project. Voila! You can now see through the dates of each of the cities which are being updated realtime.

You can download the whole project from Github —  https://github.com/an-indya/WorldWatch

Hope you will enjoy building for  Watch as much as I did. I will try putting in something more as I learn. Do leave a reply and feel free to share if you like!

Reference:

https://developer.apple.com/watchkit/