9. Displaying Glances

Every good product I’ve ever seen is because a group of people cared deeply about making something wonderful that they and their friends wanted. They wanted to use it themselves.

Steve Jobs

Most people interact with their watches by glancing at the watch to check the time. For the Apple Watch, Apple has extended this traditional form of interaction by providing glances for your watch applications. Instead of just glancing for the time, users can have a quick glance at the state of the various applications. Glances are shown when users swipe up on the watch. The Apple Watch then shows a scrollable list of glances from apps that support them (up to a maximum of 20 glances). Think of glances as snapshots of the various apps on the watch: Instagram may show the most recently shared photo, and Twitter may show the latest trending tweets. Glances provide users with a quick summary of information that may be important to them. If a user wants more details, tapping a glance launches the corresponding watch app.

If your app supports glances, you need to add a Glance scene to your storyboard file. Each user needs to manually turn on the glance of your application through the Apple Watch application on the watch.

In this chapter, you learn how to implement glances for your watch apps.

What Is a Glance?

From a developer’s perspective, a glance is an additional avenue for your app to display a quick summary of information to the user. Imagine an iPhone app that fetches stock prices at regular time intervals. A user who wants to have a quick look at the price of a particular stock can simply swipe up on the watch face and view the most recently fetched price of the stock. This can be done without launching your app. Of course, if the user wants to view more detailed information, he or she can tap the glance to launch the app. One noteworthy feature of glances is that users have no way of interacting with them—there are no controls for users to interact with. They are solely for the purpose of showing information quickly to the users, hence the name.

Implementing Glances

Let’s look at how to implement a glance for an application:

1. Using Xcode, create a new iOS App with WatchKit App project and name it DisplayingGlances. For this project, uncheck the Include Notification Scene option and check the Include Glance Scene option (see Figure 9.1).

Image

Figure 9.1 Adding the WatchKit App target with the Glance Scene

2. In the WatchKit app, select the Interface.storyboard file. You should now see the Glance Interface Controller together with the Interface Controller (see Figure 9.2).

Image

Figure 9.2 The Glance Interface Controller together with the Interface Controller


Note

Each watch app can contain at most one Glance Interface Controller.


3. In the Extension project, you see the GlanceController.swift file (see Figure 9.3).

Image

Figure 9.3 The GlanceController.swift file in the Extension project

4. The GlanceController.swift file contains the GlanceController class, which represents the Glance Interface Controller in the Interface.storyboard file:

import WatchKit
import Foundation

class GlanceController: WKInterfaceController {

    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()
    }

}

Observe that the GlanceController class extends the WKInterfaceController class, the same as the Interface Controller. The same Interface Controller lifecycle applies to the Glance Controller as well. The only exception is that for a Glance Controller, the initialization takes place very early so that the information can be quickly displayed. Hence, you should try to update your glances in the willActivate method.

Customizing the Glance

Glances can be customized to display different types of information. However, as with notifications, users are not allowed to have interactions with the glances. This means that controls, like Button and Slider controls, are not allowed.


Note

Remember that glances are supposed to show users information quickly. Hence, you should design your glances to convey as much relevant information as quickly as possible.


1. Select the Glance Interface Controller in the Interface.storyboard file and view its Attributes Inspector window (see Figure 9.4). Observe that it is divided into two sections: Upper and Lower. Both contain the Group control, which allows you to add controls like labels and images.

Image

Figure 9.4 The Glance Interface Controller is divided into two sections

2. Click the Upper group in the Attributes Inspector window, and you see a popover list (see Figure 9.5). This is a list of predefined templates for some of the common designs for the Upper section. Select the bottom-left item.

Image

Figure 9.5 Selecting the template for the Upper section


Note

If you do not like the predefined templates, you can always add and lay out your own controls in the Glance Interface Controller.


3. Likewise, click the Lower group, and you see another popover list (see Figure 9.6). Select the top-left item.

Image

Figure 9.6 Selecting the template for the Lower section

4. Add a Label control to the Lower group (see Figure 9.7) and set its attributes as follows:

Font: System Italic

Size: 30

Text Color: Yellow

Style: Bold

Horizontal: Center

Vertical: Center

Image

Figure 9.7 Adding a Label control to the Lower section


Note

You can only use the system font (San Francisco) for glances and notifications on the Apple Watch; custom fonts are not supported.


5. The Glance Interface Controller now looks like Figure 9.8.

Image

Figure 9.8 The final design for the Glance Interface Controller

Testing the Glance

To test the glance, you need to use the Glance scheme that was created in Xcode when you added the WatchKit App target:

1. In Xcode, select the Glance – DisplayingGlances WatchKit App scheme (see Figure 9.9) and then select iPhone 6 + Apple Watch – 38mm.

Image

Figure 9.9 Selecting the Glance scheme

2. Run the application on the Apple Watch Simulator. You now see the glance on the Apple Watch Simulator (see Figure 9.10).

Image

Figure 9.10 Displaying the glance on the Apple Watch Simulator

At this moment, the glance is not doing anything useful. In the next section, you modify the application to display some useful information.

Making the Glance Useful

To make the glance display useful information, you must modify the containing iOS application to perform a background fetch. Your iOS app can then perform a network operation even if it is switched to the background.

In the background fetch, the iOS app connects to a Yahoo web service to fetch the prices of two stocks: AAPL (Apple) and MSFT (Microsoft). Once the prices of the two stocks are fetched, the price of AAPL is sent to the Apple Watch using the ApplicationContext method in the Watch Connectivity Framework (covered in Chapter 7, “Interfacing with iOS Apps”). (For this example, you send the price of only one stock even though you also downloaded the price of MSFT.)

Image In the watch app, the values received from the iPhone are saved using the NSUserDefaults settings.

Image Whenever the glance is shown, the values of the stocks are retrieved from the NSUserDefaults settings and then displayed.

Implementing Background Fetch

The containing iOS app connects to the Yahoo web service and downloads the stock prices of Apple and Microsoft when the application is in the background.

1. To implement background fetch on the containing iOS app, select the DisplayingGlances target in Xcode and, in the Capabilities tab, check the Background fetch option under the Background Modes section (see Figure 9.11).

Image

Figure 9.11 Specifying the background fetch capability for the iOS app

2. In the Info.plist file in the iOS project, add the two keys NSAppTransportSecurity and NSAllowsArbitraryLoads, as shown in Figure 9.12. These two keys are needed so that you can connect to a web server using http:// instead of https:// (which is the default).

Image

Figure 9.12 Adding the keys in Info.plist to enable http://

3. Add the following statements in bold to the AppDelegate.swift file:

import UIKit

import WatchConnectivity

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, WCSessionDelegate {
    var window: UIWindow?

    //---convert from NSDate format to String---
    func currentDateToString() -> String {
        let formatter: NSDateFormatter = NSDateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss zzz"
        return formatter.stringFromDate(NSDate())
    }

    //---extract the required data from the JSON string---
    func parseJSONData(data: NSData) {
        do {
            let parsedJSONData =
            try NSJSONSerialization.JSONObjectWithData(data,
                options: NSJSONReadingOptions()) as!
                [String:AnyObject]

            let query = parsedJSONData["query"] as!
                [String:AnyObject]
            if let results = query["results"] as?
                [String:AnyObject] {
                if let quotes = results["quote"] as?
                    [[String:AnyObject]] {
                    for stock in quotes {
                        let symbol = stock["symbol"] as! String
                        let ask = stock["Ask"] as! String
                        let lastupdate = currentDateToString()

                        //---debugging---
                        print(symbol)
                        print(ask)
                        print(lastupdate)

                        if symbol == "AAPL" {
                            do {
                                let applicationContext = [
                                    "symbol" : symbol,
                                    "lastupdate" : lastupdate,
                                    "ask": ask]
                                try
                                    WCSession.defaultSession().
                                        updateApplicationContext(
                                            applicationContext)
                            } catch {
                                print("(error)")
                            }
                        }
                    }
                }
            }
        } catch {
            print(error)
        }
    }

    //---performing a background fetch---
    func application(application: UIApplication,
        performFetchWithCompletionHandler completionHandler:
        (UIBackgroundFetchResult) -> Void) {

        /*
            For http://, you need to add the following keys in
            info.plist:
            NSAppTransportSecurity
            NSAllowsArbitraryLoads - YES
            Application Transport Security has blocked a cleartext
                HTTP (http://) resource load since it is insecure.
                Temporary exceptions can be configured via your app's
                Info.plist file.
        */

        let urlString =
            "http://query.yahooapis.com/v1/public/yql?q=" +
            "select%20*%20from%20yahoo.finance.quotes%20" +
            "where%20symbol%20in%20(%22AAPL%22%2C%22MSFT%22)" +
            "%0A%09%09&env=http%3A%2F%2Fdatatables.org%2" +
            "Falltables.env&format=json"

        let session = NSURLSession.sharedSession()
        session.dataTaskWithURL(NSURL(string:urlString)!,
        completionHandler: {
            (data, response, error) -> Void in
            let httpResp = response as! NSHTTPURLResponse
            if error == nil && httpResp.statusCode == 200 {
                //---parse the JSON result---
                self.parseJSONData(data!)
                completionHandler(UIBackgroundFetchResult.NewData)
            } else {
                completionHandler(UIBackgroundFetchResult.Failed)
            }
        }).resume()
    }

    func application(application: UIApplication,
        didFinishLaunchingWithOptions launchOptions:
        [NSObject: AnyObject]?) -> Bool {

        // Override point for customization after application launch.

        if (WCSession.isSupported()) {
            let session = WCSession.defaultSession()
            session.delegate = self
            session.activateSession()
        }

        return true
    }


Note

Remember to change the shared group name to the one that you have used.


You just enabled the following to happen:

Image You connect to the Yahoo web service to fetch the price of Apple and Microsoft.

Image The web service returns the result as a JSON string.

Image You pass the JSON string to the parseJSONData: method to extract the relevant data: stock symbol and asking price.

Image The currentDateToString method returns the current date and time as a String object.

Image The stock symbols, asking prices, and the date and time the prices were fetched are sent to the Apple Watch using the ApplicationContext method.

4. Select the DisplayingGlances scheme (see Figure 9.13) in Xcode and run the application on the iPhone Simulator.

Image

Figure 9.13 Selecting the DisplayingGlances scheme

5. To simulate a background fetch on the application, select Debug | Simulate Background Fetch in Xcode. If the stock prices are downloaded correctly, you should now see the prices in the Output window (see Figure 9.14).

Image

Figure 9.14 The stock prices downloaded from the web service

Updating the Glance

Now that the stock prices are downloaded and saved, you can display them on the Glance Interface Controller:

1. In the GlanceController.swift file, create three outlets for the three Label controls in the Glance Interface Controller:

import WatchKit
import Foundation

class GlanceController: WKInterfaceController {

    @IBOutlet var lblSymbol: WKInterfaceLabel!

    @IBOutlet var lblLastUpdate: WKInterfaceLabel!

    @IBOutlet var lblAsk: WKInterfaceLabel!

    override func awakeWithContext(context: AnyObject?) {
        super.awakeWithContext(context)

        // Configure interface objects here.
    }

2. Add the following statements in bold to the GlanceController.swift file:

import WatchKit
import Foundation

import WatchConnectivity

class GlanceController: WKInterfaceController, WCSessionDelegate {
    @IBOutlet var lblSymbol: WKInterfaceLabel!

    @IBOutlet var lblLastUpdate: WKInterfaceLabel!

    @IBOutlet var lblAsk: WKInterfaceLabel!

    override func awakeWithContext(context: AnyObject?) {
        super.awakeWithContext(context)

        // Configure interface objects here.
        if (WCSession.isSupported()) {
            let session = WCSession.defaultSession()
            session.delegate = self
            session.activateSession()
        }
    }

    func session(session: WCSession, didReceiveApplicationContext
        applicationContext: [String : AnyObject]) {
        let defaults = NSUserDefaults.standardUserDefaults()
        defaults.setValue(applicationContext["symbol"]! as! String,
            forKey: "symbol")
        defaults.setValue(applicationContext["lastupdate"]! as! String,
            forKey: "lastupdate")
        defaults.setValue(applicationContext["ask"]! as! String,
            forKey: "ask")
        defaults.synchronize()
    }

    //---convert from string to NSDate---
    func dateStringToDate(date:String) -> NSDate {
        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss zzz"
        return dateFormatter.dateFromString(date)!
    }

    override func willActivate() {
        // This method is called when watch view controller is about to
        // be visible to user.
        super.willActivate()

        let defaults = NSUserDefaults.standardUserDefaults()

        if defaults.valueForKey("lastupdate") != nil {
            //---retrieve the price and date fetched from the settings---
            let lastupdate = dateStringToDate(
                (defaults.valueForKey("lastupdate") as? String)!)
            let price = defaults.valueForKey("ask") as? String
            let symbol = defaults.valueForKey("symbol") as? String

            //---the difference between the current time and the time the
            // price was fetched---
            let elapsedTime = NSDate().timeIntervalSinceDate(lastupdate)

            //---convert to seconds---
            let elapsedTimeSeconds = Int(elapsedTime)

            //---convert the time to mins and secs---
            let elapsedMin = elapsedTimeSeconds / 60
            let elapsedSec = elapsedTimeSeconds % 60

            if elapsedMin > 0 {
                lblLastUpdate.setText(
                    "(elapsedMin) mins (Int(elapsedSec)) secs")
            } else {
                lblLastUpdate.setText("(Int(elapsedTime)) secs")
            }
            //---show the info on the glance---
            self.lblSymbol.setText(symbol)
            self.lblAsk.setText("$" + price!)
        }
    }

You just enabled the following to happen:

Image When the Glance Controller is awakened, you want to activate the session for the Watch Connectivity Framework so that you can receive the incoming stock information sent from the watch.

Image You load the values saved in the NSUserDefaults settings. For simplicity, you are retrieving only one stock price: AAPL. The value of the AAPL key is a dictionary containing the price as well as the date and time it was fetched.

Image The dateStringToDate: method accepts the date and time as a String and then returns an NSDate object.

Image You calculate the elapsed time since the price was fetched using the timeIntervalSinceDate: method of the NSDate object.

Image You then display the stock symbol, the elapsed time since the price was fetched, and the price of the stock in the Glance Interface Controller.

3. In Xcode, switch to the Glance – DisplayingGlances WatchKit App scheme and test the application on the iPhone Simulator. The Glance Interface Controller should now look like Figure 9.15.

Image

Figure 9.15 The glance showing the latest fetched price

Summary

In this chapter, you learned how to implement glances in your Apple Watch application. You also learned how to perform background fetch in your containing iOS application and then display the information downloaded in your Glance Interface Controller.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.34.31