Watch and Learn!

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

AplWatch42-Sstl-RbrWht-PF_iPhone6-Svr-PF-PRINT
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.

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?


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.

Po2Enough theory, let’s code!

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/

Kung Fu Panda Image © Copyright respective owner

Advertisements