A watchOS 2 Complication Tutorial

PreviousTable of ContentsNext
An Overview of ClockKit and Apple Watch ComplicationsSupporting Different Apple Watch Display Sizes in watchOS 2


Purchase the full edition of this watchOS 2 App Development Essentials book in eBook ($12.99) or Print ($27.99) format
watchOS 2 App Development Essentials Print and eBook (ePub/PDF/Kindle) editions contain 35 chapters.

Buy Print


The previous chapter explored the theory behind the implementation of complications in watchOS 2. This chapter will work through the creation of an example project designed to show the basics steps involved in implementing a complication for a WatchKit app.

About the Complication Project

The complication created in this project is intended to accompany a WatchKit app developed for a beer festival. At hourly intervals during this festival, different breweries will be hosting special beer tasting sessions. The purpose of the complication is to notify the user of the time of the next tasting, the type of beer being served and the booth number at which that particular brewery is located.

The timeline will contain four events scheduled at hourly intervals starting from the initial invocation of the complication with the information presented using the Modular Large complication family.

Creating the Complication Project

Start Xcode and create a new iOS project. On the template screen choose the Application option located under watchOS in the left hand panel and select iOS App with WatchKit App. Click Next, set the product name to ClockKitApp, enter your organization identifier and make sure that the Devices menu is set to Universal.

Before clicking Next, change the Language menu to Swift, switch the Include Complication option on and all other include options off.

On the final screen, choose a location in which to store the project files and click on Create to proceed to the main Xcode project window.


Watchos add complication.png

Figure 29-1


Within the Project Navigator panel (Figure 29-2), note that Xcode has added an additional Swift class file named ComplicationController.swift within the WatchKit extension folder to act as the complication data source class:


Watchos complication controller.png

Figure 29-2


Select this file and review the template delegate methods that have been generated by Xcode ready to be completed.


Configuring the Supported Complication Families

The next step in the configuration process is to select the complication families which are to be supported by the WatchKit app extension. To configure these settings, select the ClockKitApp target at the top of the Project Navigator panel and select the General tab in the main panel. Using the menu in the upper left hand corner of the settings panel, select the ClockKit WatchKit Extension entry as shown in Figure 29-3:


Watchos complication extension.png

Figure 29-3


Within the general settings for the extension target, locate the Complications Configuration section and uncheck all of the supported family options with the exception of the Modular Large entry. Also verify that the Data Source Class field is set to the correct class (it should read $(PRODUCT_MODULE_NAME).ComplicationController):


Watchos select complication families.png

Figure 29-4


The settings configured in this panel are stored in the Info.plist file of the extension target and can be reviewed by selecting this file within the Project Navigator panel. Once selected, the relevant properties will appear in the property editor as shown in Figure 29-5:

Watchos complication plist.png

Figure 29-5


Adding the Data and Image to the Data Source

The data source for the complication has been designated by Xcode to be the ComplicationController class contained within the ComplicationController.swift file. The next step in the project is to add some data to be displayed within the complication timeline. This will take the form of an array object initialized with the text to be displayed within each timeline entry. Locate, select and edit the ComplicationController.swift file and modify it to declare this array as follows:

import ClockKit

class ComplicationController: NSObject, CLKComplicationDataSource {

    let timeLineText = ["Oatmeal Stout at Booth 212", "Porter at Booth 432", "Pale Ale at Booth 232", "English Bitter at Booth 327"]
.
.
.
}

The complication is also going to display an image of a pint glass in the header of the message. This icon image file is named beer_glass.png and can be found in the image_files folder of the sample code download available from the following link:

http://www.ebookfrenzy.com/code/watchOS2BookSamples.zip

Locate this image file in a Finder window and drag and drop it onto the Project Navigator panel beneath the ClockKit WatchKit Extension entry as shown in Figure 29 6:


Watchos image added to complication.png

Figure 29-6


Implementing the Placeholder Delegate Method

When the WatchKit app is first installed on an Apple Watch, the getPlaceholderTemplateForComplication method in the ComplicationController.swift file will be called and required to return the placeholder template for the complication. This is the complication that will be displayed when the user customizes the clock face to include the complication. Locate the template method within the ComplicationController.swift file and implement the code to create and return the placeholder template:

func getPlaceholderTemplateForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTemplate?) -> Void) {

    let template = CLKComplicationTemplateModularLargeStandardBody()
    let beerGlass = UIImage(named: "beer_glass")

    template.headerImageProvider = 
		CLKImageProvider(onePieceImage: beerGlass!)

    template.headerTextProvider = 
	 CLKSimpleTextProvider(text: "Beer Festival")
    template.body1TextProvider = 
        CLKSimpleTextProvider(text: "Beer Tasting Schedule")

    handler(template)
}

The code added to this method creates a new complication template object based on the standard large modular family and then uses image and text providers to set the image and text content of the placeholder. The reply handler is then called and used to return the template object to the ClockKit framework.

Configuring Travel Directions

The ClockKit framework time travel feature needs to know the directions in which it will be able to traverse the timeline. For the purposes of the example, the timeline will only contain future events. As such the getSupportedTimeTravelDirectionsForComplication method needs to be modified to support only forward direction time travel:

func getSupportedTimeTravelDirectionsForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTimeTravelDirections) -> Void) {

    handler([.Forward])
} 

With this property configured for forward travel only, the complication in the clock face will be dimmed out in the event the user attempts to travel into the past.

Adding a Timeline Entry Creation Method

A number of the methods in the data source class will need to create complication timeline entries. Rather than duplicate code in multiple methods it is more efficient to write a single utility method and call that whenever a timeline entry is needed. This method will be named createTimeLineEntry and will take as parameters the header and body text to be included in the complication and the date of the timeline entry in the form of an NSDate object. The code for the method will also incorporate the beer glass icon into the entry before returning the result in the form of a completed CLKComplicationTimelineEntry object. Within the ComplicationController.swift file, implement this method as follows:

Purchase the full edition of this watchOS 2 App Development Essentials book in eBook ($12.99) or Print ($27.99) format
watchOS 2 App Development Essentials Print and eBook (ePub/PDF/Kindle) editions contain 35 chapters.

Buy Print

func createTimeLineEntry(headerText: String, bodyText: String, date: NSDate) -> CLKComplicationTimelineEntry {

    let template = CLKComplicationTemplateModularLargeStandardBody()
    let beerGlass = UIImage(named: "beer_glass")

    template.headerImageProvider = 
	CLKImageProvider(onePieceImage: beerGlass!)
    template.headerTextProvider = CLKSimpleTextProvider(text: headerText)
    template.body1TextProvider = CLKSimpleTextProvider(text: bodyText)

    let entry = CLKComplicationTimelineEntry(date: date, 
		complicationTemplate: template)

    return(entry)
} 

The method takes the same steps as those outlined for the getPlaceholderTemplateForComplication method to create a complication template object and combines that object with the designated date to create a timeline entry object.

Specifying the Timeline Start and End Dates

Among the information required by the ClockKit framework for a complication are the start and end dates of the timeline. In this example, the start date is the first timeline entry which will be displayed for the current time. The getTimelineStartDateForComplication method, therefore, simply needs to return the current date and time:

func getTimelineStartDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {

    let currentDate = NSDate()
    handler(currentDate)
}

Since the timeline contains four entries spaced at hourly intervals, the getTimelineEndDateForComplication method needs to calculate and return a time four hours into the future from the current time:

func getTimelineEndDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
        
    let currentDate = NSDate()
    let endDate = 
         currentDate.dateByAddingTimeInterval(NSTimeInterval(4 * 60 * 60))

    handler(endDate)
} 

Providing the Current Timeline Entry

Once the user has added the complication into a clock face the ClockKit framework will request the timeline entry to be displayed for the current time. It does this via a call to the getCurrentTimelineEntryForComplication method of the complication data source. The code for this method now needs to be added to the template method in the ComplicationController.swift file as follows:

func getCurrentTimelineEntryForComplication(complication: CLKComplication, withHandler handler: ((CLKComplicationTimelineEntry?) -> Void)) {

    if complication.family == .ModularLarge {
        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "hh:mm"

        let timeString = dateFormatter.stringFromDate(NSDate())

        let entry = createTimeLineEntry(timeString, bodyText: timeLineText[0], date: NSDate())

        handler(entry)
    } else {
        handler(nil)
    }
}

The method begins by checking that the requested complication family matches the modular large family. A date formatter object is then created and used to create a string containing the current time in hh:mm format. This string, together with the first entry from the timeLineText array and an NSDate object set to the current time is then passed to the createTimeLineEntry utility method. The returned timeline entry object is then passed back to the ClockKit framework via the reply handler. In the event that a complication family other than modular large is selected, a nil value is returned to indicate that no entry is available.

Providing the Remaining Timeline Entries

Because the complication in this tutorial only supports forward time travel, it is only necessary to implement the getTimelineEntriesForComplication:afterDate method in the ComplicationController.swift file, leaving the beforeDate variant of the method returning a nil value:

func getTimelineEntriesForComplication(complication: CLKComplication, afterDate date: NSDate, limit: Int, withHandler handler: (([CLKComplicationTimelineEntry]?) -> Void)) {

    var timeLineEntryArray = [CLKComplicationTimelineEntry]()
    var nextDate = NSDate(timeIntervalSinceNow: 1 * 60 * 60)

    for index in 1...3 {

        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "hh:mm"

        let timeString = dateFormatter.stringFromDate(nextDate)

        let entry = createTimeLineEntry(timeString, bodyText: timeLineText[index], date: nextDate)

        timeLineEntryArray.append(entry)

        nextDate = nextDate.dateByAddingTimeInterval(1 * 60 * 60)
    }
    handler(timeLineEntryArray)
}

The code begins by creating and initializing an array of CLKComplicationTimelineEntry objects and calculating the date of the next entry (in this case an hour into the future from the current time). A for loop is then used to iterate through the remaining three entries in the timeLineText array, formatting the time for each entry and calling the createTimeLineEntry method with the text and date values. Once a timeline entry has been created it is added to the timeLineEntryArray array and the date incremented by an additional hour. Once all of the entries have been placed into the array it is returned to the ClockKit framework via the reply handler.

The code for the complication is now complete and ready for testing.

Adding the Complication to a Clock Face

Before the complication will be visible to the user it must be added to a clock face on the Apple Watch device. From the Xcode toolbar, select the Complication – ClockKitApp WatchKit App target and run it either on a physical Apple Watch device or Watch Simulator session.

On the watch or simulator display the time and perform a deep press on the screen (to make a deep press on the simulator, select the Hardware -> Force Touch Pressure -> Deep Press menu option, click on the screen then switch back to Shallow Press mode).

When the clock face customization screen appears make swiping motions until the Modular clock face shown in Figure 29-7 appears:


Watchos clock face selection.png

Figure 29-7


Tap on the Customize button to display the color configuration screen and then swipe left to display the complications configuration screen (Figure 29-8):


Watchos compliction config.png

Figure 29-8


Select the center modular large complication and use the Digital Crown (or mouse scroll wheel if using the simulator) to scroll through the available complications until the ClockKitApp WatchKit App complication appears showing the placeholder content:


Watchos complication in config.png

Figure 29-9


Press the Digital Crown to save the selection then tap on the clock face selection screen to return to the live clock face.

Testing the Complication

With the complication included in the clock face, the current timeline entry should be visible as illustrated in Figure 29 10:


Watchos complication current entry.png

Figure 29-10


Test the time travel feature using either the Digital Crown on the Apple Watch, or by clicking the time display in the simulator and using the mouse scroll wheel. Traveling back in time should cause the complication to dim. Forward time travel, on the other hand, should move through the future timeline entries:


Watchos complication time travel.png

Figure 29-11


Travelling beyond the last timeline entry will once again cause the complication to be dimmed to indicate that there are no further events to display.

Summary

This chapter has worked through the creation of a project that demonstrates the practical steps involved in implementing a ClockKit complication for a WatchKit app. The steps covered included the creation of a project containing a complication, the selection of the supported complication families and the implementation of the delegate methods in the complication data source class including providing a placeholder template and both current and future timeline entries. The complication was then added to a watchOS 2 clock face and tested in terms of displaying the current timeline entry and future entries using the time travel feature.


Purchase the full edition of this watchOS 2 App Development Essentials book in eBook ($12.99) or Print ($27.99) format
watchOS 2 App Development Essentials Print and eBook (ePub/PDF/Kindle) editions contain 35 chapters.

Buy Print



PreviousTable of ContentsNext
An Overview of ClockKit and Apple Watch ComplicationsSupporting Different Apple Watch Display Sizes in watchOS 2