• BUILDING AN APPLE WATCH APP

    26 June 2015

    Screen Shot 2015-04-20 at 9.03.20 AM

     This app will allow the user to swipe between the timezones so they can easily know what time it is anywhere in Canada and the US.

    This app is built on top of the Tutorial: Today Widget in Swift tutorial, since in that app we also made a timezone view. Since the Today Widget and the Apple Watch apps are very similar because they are both built using extensions to an existing app it should make it a little easier to build.

    WHAT IS A WATCH APP?

    If you don’t know much about the framework of what makes up a watch app, you should first read Get Started with Apple Watch and WatchKit. This will give you a great introduction to what a Watch App is, and how glances and notifications work with the watch.

    SETUP THE APPLICATION EXTENSION

    1. Select File > New > Project. Select the Single view application template, and call it WatchApp.
      Screen Shot 2015-04-17 at 8.49.00 AM
      Screen Shot 2015-04-17 at 8.49.16 AM
    2. Add the Application Extension Target. Select File > New > Target…. Then in the left pane, select Apple Watch and under that choose WatchKit App.
      Screen Shot 2015-04-17 at 8.56.49 AM
      Set the language to Swift if is isn’t already and check both Include Notification Scene and Include Glance Scene are not checked. Click Finish and Xcode will set up the target and the files needed for the Watch interface.
      Screen Shot 2015-04-17 at 9.00.58 AM
    3. A message will popup asking if you want to activate the “WatchApp WatchKit App” scheme. Click Activate.
      Screen Shot 2015-04-17 at 9.19.04 AM
    4. Adding a WatchKit App target to your Xcode project creates two new executables and updates your project’s build dependencies. Building your iOS app builds all three executables (the iOS app, WatchKit extension, and WatchKit app) and packages them together.
      Screen Shot 2015-04-17 at 9.27.10 AM
      The WatchKit app is packaged inside your WatchKit extension, which is in turn packaged inside your iOS app. When the user installs your iOS app on an iPhone, the system prompts the user to install your WatchKit app if there is a paired Apple Watch available.
      target_structure_2x

    CREATE THE WATCH APP VIEW

    The terminology for Watch Apps are a little different than iOS apps, instead of calling them “views”, Apple uses the term “Interface”. WatchKit apps do not use the same layout model as iOS apps. When assembling the scenes of your WatchKit app interface, you do not create view hierarchies by placing elements arbitrarily in the available space. Instead, as you add elements to your scene, Xcode arranges the items for you, stacking them vertically on different lines. At runtime, Apple Watch takes those elements and lays them out for you based on the available space.

    storyboard_layout_2x

    When creating your interfaces in Xcode, let objects resize themselves to fit the available space whenever possible. App interfaces should be able to run both display sizes of Apple Watch. Letting the system resize objects to fit the available space minimizes the amount of custom code you have to write for each device

    1. We are going to start by adding a label into the Watch App Interface, in Interfaces the label type is WKInterfaceLabel. This one is the name of the time zone for the time we are going to display. Drag the label out from the Objects inspector just like you would in an iOS app. Also be sure to align the label text to center so it looks nice, also set the Position – Horizontal to “Center” as well.

      Screen Shot 2015-04-17 at 9.43.41 AM

    2. Next we need to add another label for the time. We’ll use a standard label so we can easily change the date per time zone> Watch Kit comes with its own Date label called WKInterfaceDate (but that only displays the current date and time). This can be customized, for our purposes we only want to see the time, not the date, so change the Date format to “None” and customize the font to “System” with a size of 30, and make it “Bold” so the time stands out, also set the Position – Horizontal to “Center” and Vertical to “Center”.

      Screen Shot 2015-04-17 at 9.54.57 AM

      Xcode supports customizing your interface for the different sizes of Apple Watch. The changes you make in the storyboard editor by default apply to all sizes of Apple Watch, but you can customize your storyboard scenes as needed for different devices. For example, you might make minor adjustments to the spacing and layout of items or specify different images for different device sizes.

    3. Now that we have our UI defined, let’s connect the IBOutlets with the code in the “InterfaceController.swift” file. You can do this many ways, just like an iOS app. Either Control-drag the UI object from the storyboard into the InterfaceController.swift or define the code and link up using the Connections Inspector.
      Here is the code for the two objects we are linking:
       //
      //  InterfaceController.swift
      //  WatchApp WatchKit Extension
      //
      //  Created by Brian Coleman on 2015-04-17.
      //  Copyright (c) 2015 Brian Coleman. All rights reserved.
      //
      
      import WatchKit
      import Foundation
      
      
      class InterfaceController: WKInterfaceController {
      
          @IBOutlet var timeInterfaceDate : WKInterfaceDate? = WKInterfaceDate()
          
          override func awakeWithContext(context: AnyObject?) {
              super.awakeWithContext(context)
              
              // Configure interface objects here.
          }
      
          override func willActivate() {
              // This method is called when watch view controller is about to be visible to user
              super.willActivate()
          }
      
          override func didDeactivate() {
              // This method is called when watch view controller is no longer visible
              super.didDeactivate()
          }
      
      }
      
      

    WATCH APP NAVIGATION

    For WatchKit apps with more than one screen of content, you must choose a technique for navigating between those screens. WatchKit apps support two navigation styles, which are mutually exclusive:

    • Page based. This style is suited for apps with simple data models where the data on each page is not closely related to the data on any other page. A page-based interface contains two or more independent interface controllers, only one of which is displayed at any given time. At runtime, the user navigates between interface controllers by swiping left or right on the screen. A dot indicator control at the bottom of the screen indicates the user’s current position among the pages.
    • Hierarchical. This style is suited for apps with more complex data models or apps whose data is more hierarchical. A hierarchical interface always starts with a single root interface controller. In that interface controller, you provide controls that, when tapped, push new interface controllers onto the screen.

    Although you cannot mix page-based and hierarchical navigation styles in your app, you can supplement these base navigation styles with modal presentations. Modal presentations are a way to interrupt the current user workflow to request input or display information. You can present interface controllers modally from both page-based and hierarchical apps. The modal presentation itself can consist of a single screen or multiple screens arranged in a page-based layout.

    For our project we are going to use a page based navigation so the user can swipe between the four time zones.

    You configure a page-based interface in your app’s storyboard by creating a next-page segue from one interface controller to the next.

    To create a next-page segue between interface controllers:

    1. In your storyboard, add interface controllers for each of the pages in your interface. Let’s add one for each of our time zones.
      Screen Shot 2015-04-17 at 10.58.52 AM
    2. Control-click your app’s main interface controller, and drag the segue line to another interface controller scene.
    3. The second interface controller should highlight, indicating that a segue is possible.
    4. Release the mouse button.
      Screen Shot 2015-04-17 at 11.01.16 AM
    5. Select “next page” from the relationship segue panel.
    6. Using the same technique, create segues from each interface controller to the next.
      Screen Shot 2015-04-17 at 11.02.27 AM
    7. The order in which you create your segues defines the order of the pages in your interface.

    ADD IN LIVE DYNAMIC DATA

    We are almost done making our watch app, let’s add in the clocks to finish it up. The updateClocks() method is called every second to keep the time up to date.

    //
    //  InterfaceController.swift
    //  WatchApp WatchKit Extension
    //
    //  Created by Brian Coleman on 2015-04-17.
    //  Copyright (c) 2015 Brian Coleman. All rights reserved.
    //
    
    import WatchKit
    import Foundation
    
    
    class InterfaceController: WKInterfaceController {
    
        @IBOutlet var easternLabel : WKInterfaceLabel? = WKInterfaceLabel()
        @IBOutlet var centralLabel : WKInterfaceLabel? = WKInterfaceLabel()
        @IBOutlet var mountainLabel : WKInterfaceLabel? = WKInterfaceLabel()
        @IBOutlet var pacificLabel : WKInterfaceLabel? = WKInterfaceLabel()
        
        override func awakeWithContext(context: AnyObject?) {
            super.awakeWithContext(context)
            
            // Create a timer to refresh the time every second
            var timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("updateClocks"), userInfo: nil, repeats: true)
            timer.fire()
        }
    
        override func willActivate() {
            // This method is called when watch view controller is about to be visible to user
            super.willActivate()
        }
    
        override func didDeactivate() {
            // This method is called when watch view controller is no longer visible
            super.didDeactivate()
        }
        
        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 WATCH APP

    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 Watch App target.
      Screen Shot 2015-04-17 at 1.44.56 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 Apple Watch simulator. Swipe left to see the other time zones.
      Screen Shot 2015-04-17 at 1.49.09 PM

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


Comments

Comments closed on this post.