An Overview of the watchOS 2 App Architecture

PreviousTable of ContentsNext
Building an Example watchOS 2 WatchKit AppAn Example Interactive watchOS 2 App


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 chapters have explained that a WatchKit app consists of two main components, the WatchKit app itself and the WatchKit extension.

It has also been established that the WatchKit app is primarily responsible for displaying the user interface while the programming logic of the app resides within the WatchKit extension.

It is less clear at this point, however, how the user interface elements in the app are connected to the code in the extension. In other words, how the project is structured such that tapping on a button in a scene causes a specific method in the extension to be called. Similarly, we need to understand how the code within the extension can manipulate the properties of a visual element in a storyboard scene, for example changing the text displayed on a Label interface object. These topics will be covered in this chapter and then put into practice in the next chapter entitled An Example Interactive WatchKit App.

This chapter will also introduce the extension delegate class and provide an overview of the lifecycle of a WatchKit app and outline the ways in which this can be used to perform certain initialization tasks when a WatchKit app is launched.

Basic WatchKit App Architecture

As discussed in previous chapters, the WatchKit app itself consists only of the storyboard file and a set of resource files. The storyboard contains one or more scenes, each of which represents a different screen within the app and may, optionally, provide mechanisms for the user to transition from one scene to another. Clearly this does not provide any functionality beyond presenting user interfaces to the user. The responsibility of providing the behavior behind a user interface scene so that the app actually does something useful belongs to the interface controller.

WatchKit Interface Controllers

Each scene within a storyboard must have associated with it an interface controller instance. Interface controllers are subclassed from the WatchKit framework WKInterfaceController class and contain the code that allows the WatchKit app to perform tasks beyond simply presenting a user interface to the user. This provides a separation between the user interfaces (the storyboard) and the logic code (the interface controllers). In fact, interface controllers are similar to view controllers in iOS applications.

The interface controllers for a WatchKit app reside within the WatchKit extension associated with the app and are installed and executed on the Apple Watch. It is the responsibility of the interface controller to respond to user interactions in the corresponding user interface scene and to make changes to the visual elements that make up the user interface. When a user taps a button in a WatchKit scene, for example, a method in the interface controller will be called by the WatchKit framework in order to perform some form of action. In the event that a change needs to be made to a user interface element, for example to change the text displayed on a label, the interface controller will make the necessary changes and the WatchKit framework will transmit those changes to the WatchKit app where the update will be performed.

This sounds good in theory but does not explain how the connections between the elements in the user interface and the interface controller are established. This requires an understanding of the concepts of outlets and action methods.


WatchKit Action Methods

Creation of a WatchKit app typically involves designing the user interface scenes using the Interface Builder tool and writing the code that provides the logic for the app in the source code files of the interface controller classes. In this section we will begin to look at how the user interface scene elements and the interface controller code interact with each other.

When a user interacts with objects in a scene of a WatchKit app, for example touching a button control, an event is triggered. In order for that event to achieve anything, it needs to trigger a method call on the interface controller class. Use of a technique known as target-action provides a way to specify what happens when such events are triggered. In other words, this is how you connect the objects in the user interface you have designed in the Interface Builder tool to the back end Swift code you have implemented in the corresponding interface controller class. Specifically, this allows you to define which method of the interface controller gets called when a user interacts in a certain way with a user interface object.

The process of wiring up a user interface object to call a specific method on an interface controller is achieved using something called an Action. Similarly, the target method is referred to as an action method. Action methods are declared within the interface controller class using the IBAction keyword, for example:

@IBAction func buttonPress() {
    print("Button Pressed")
    // Perform tasks in response to a button press
}

WatchKit Outlets

The opposite of an Action is an Outlet. As previously described, an Action allows a method in an interface controller instance to be called in response to a user interaction with a user interface element. An Outlet, on the other hand, allows an interface controller to make changes to the properties of a user interface element. An interface controller might, for example, need to set the text on a Label object. In order to do so, an Outlet must first have been defined using the IBOutlet keyword. In programming terms, an IBOutlet is simply an instance variable that references the user interface object to which access is required. The following line demonstrates an outlet declaration for a label:

@IBOutlet weak var myLabel: WKInterfaceLabel!

Once outlets and actions have been implemented and connected, all of the communication required to make these connections work is handled transparently by WatchKit. Figure 4-1 provides a visual representation of actions and outlets:


watchOS 2 actions and outlets diagram

Figure 4-1


Outlets and actions can be created visually with just a few mouse clicks from within Xcode using the Interface Builder tool in conjunction with the Assistant Editor panel, a topic which will be covered in detail in the chapter entitled An Example Interactive WatchKit App.

WatchKit App State Transitions

At any given time, a WatchKit app will be in one of the following three possible states:

  • Not running – Either the WatchKit app has yet to be launched or it has been terminated by watchOS.
  • Inactive – The WatchKit app is running but is not receiving any events from WatchKit. An app usually enters this state briefly during the launch process, or when the app is transitioning out of active state because the user is no longer interacting with the app.
  • Active – The WatchKit app is running, is most likely the currently visible app on the Apple Watch and is receiving events.

As a WatchKit app transitions from one state to another, the app is notified of the state change via calls to the methods in the extension delegate object.

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 WatchKit Extension Delegate

In addition to an interface controller instance for each scene in the WatchKit app, the WatchKit extension also includes a single extension delegate object. This class is added to the WatchKit extension automatically by Xcode when a WatchKit app target is created and conforms to the WKExtensionDelegate protocol which defines which methods this class must implement. These methods are then called by the WatchKit framework to notify the WatchKit app about lifecycle state transition events and notifications. By adding code to these methods, the WatchKit app can respond to the notifications where necessary. At a minimum the extension delegate class will implement the following lifecycle methods:

  • applicationDidFinishLaunching() – Called by WatchKit when the WatchKit app launch process is close to completion and the WatchKit extension is about to run. This is a useful location in which to perform initialization tasks that relate to the entire WatchKit app as opposed to an individual interface controller.
  • applicationDidBecomeActive() – Called to notify the WatchKit app that it is now active and visible to the user. This method may be used to resume any tasks that were paused when the app previously transitioned to the inactive state.
  • applicationDidBecomeInactive() – Called when the WatchKit app transitions from active to inactive state. This opportunity should be taken to pause any non-essential activities. Once in the inactive state, the app will either transition to the active or not running state depending on the actions of the user.

The Lifecycle of an Interface Controller

The lifecycle of a WatchKit app and the corresponding WatchKit extension is actually very simple. When a WatchKit app is launched on the device, a scene will be loaded from within the storyboard file. When the scene has loaded, the WatchKit framework will request that the extension corresponding to the app be launched. The extension is then instructed to create the interface controller associated with the scene that was loaded.

As long as the user is interacting with the WatchKit app the extension will continue to run. When the system detects that the user is no longer interacting with the watch, or the user exits the app, the interface controller is deactivated and the extension suspended.

At various points during this interface controller initialization and de-initialization cycle, calls will be made to specific lifecycle methods declared within the interface controller class where code can be added to perform initialization and clean up tasks. These methods are as follows:

  • init() - The first method called on the interface controller when the scene is to be displayed. This method can be used to perform initialization tasks in preparation for the scene being displayed to the user. It is also possible to make changes to user interface objects via outlets from within this method.
  • awakeWithContext() - This method is called after the call to the init() method and may optionally be passed additional context data. This is typically used when navigating from one scene to another in a multi-scene app and allows data to be passed from the interface controller of the currently displayed scene to the interface controller of the destination scene. Access to user interface objects is available from within this method.
  • willActivate() – Called immediately before the scene is presented to the user on the Apple Watch device. This is the recommended method for making final minor changes to the elements in the user interface. Access to user interface objects is available from within this method.
  • didAppear() – This method is called immediately after the scene associated with the interface controller becomes visible to the user on the Apple Watch screen. This is the recommended lifecycle method in which to initiate any animation sequences.
  • willDisappear() – Called immediately before the scene associated with the interface controller is removed from view on the Apple Watch screen. This method should be used to stop any animation that is currently running.
  • didDeactivate() – The last method called when the user exits the current scene or the system detects that, although the app is still running, the user is no longer interacting with the Apple Watch device. This method should be used to perform any cleanup tasks necessary to ensure a smooth return to the scene at a later time. Access to user interface objects is not available from within this method. Calls to this method may be triggered for testing purposes from within the WatchKit simulator environment by locking the simulator via the Hardware -> Lock menu option.

The diagram in Figure 4-2 illustrates the WatchKit app lifecycle as it corresponds to the WatchKit extension and interface controller:


The watchOS 2 app lifecycle methods

Figure 4-2


It should be noted that, with the exception of the app launch and extension suspension phases in the above diagram, the same lifecycle sequence is performed each time a scene is loaded within a running WatchKit app.

WatchKit Extension Guidelines

A key point to note from the lifecycle description is that the WatchKit extension is suspended when the user either exits or stops interacting with the WatchKit app. It is important, therefore, to avoid performing any long term tasks within the extension. Any tasks that need to continue executing after the extension has been suspended should be passed to the containing iOS app to be handled. Details of how this can be achieved are outlined in the An Introduction to Watch Connectivity in watchOS 2 chapter of this book.

Summary

Each scene within a WatchKit app has associated with it an interface controller within the WatchKit extension. The interface controller contains the code that provides the underlying logic and behavior of the WatchKit app.

The interactions between the current scene of a WatchKit app running on an Apple Watch and the corresponding interface controller within the WatchKit extension are implemented using actions and outlets. Actions define the methods that get called in response to specific actions performed by the user within the scene. Outlets, on the other hand, provide a mechanism for the interface controller code to access and modify the properties of user interface objects.

A WatchKit app will transition through different lifecycle states consisting of not running, inactive and active. The app is notified of each transition change via a call to methods within the extension delegate.

When a WatchKit app is launched, and each time a new scene is loaded, each interface controller has a lifecycle consisting of initialization, user interaction and termination. At multiple points within this lifecycle, calls are made to specific methods within the interface controller of the current scene thereby providing points within the code where initialization and de-initialization tasks can be performed.


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
Building an Example watchOS 2 WatchKit AppAn Example Interactive watchOS 2 App