What's New in watchOS 2 for Developers?
Barely two months after the Apple Watch was made available for sale, Apple has announced the second edition of the Apple Watch OS, aptly named watchOS 2. Without much surprise, watchOS 2 now supports native apps and comes with a slew of new features. In this article, I will walk you through some of the new features, providing some code snippets to get you started.
Architecture Changes
The change that you will encounter first and foremost when moving from watchOS 1 to watchOS 2 is the change in execution model. In watchOS 1, the WatchKit Extension (the application logic of your watch app) is hosted within the containing iOS app running on the iPhone (see Figure 1). The UI of your watch app is on the Apple Watch itself. This execution model is limited, as all interactions with the watch app must be communicated back to the iPhone, and hence the watch itself has this inherent reliance on the iPhone in order to work. This change has resulted in users reporting that first-generation watch apps were sluggish and took a long time to load.
Figure 1 Architecture of watchOS 1.
In watchOS 2, the WatchKit Extension is now moved to the Apple Watch itself (see Figure 2). So now the UI and application logic of your watch app are executed directly on the watch itself. This model enables developers to build more responsive apps, without having to send data back and forth between the containing iOS app and the watch app. In addition, the watch can connect directly to known WiFi networks, and your watch app can run independently without the help of the paired iPhone.
Figure 2 Architecture of watchOS 2.
There is one important thing that you need to note, though. In watchOS 1, you can create shared app groups to share data (such as files and NSUserDefaults values) between the containing iOS app and the WatchKit Extension. In this new model, sharing like this is no longer possible. Instead, you need to use the new Watch Connectivity Framework to enable a two-way communication conduit between the containing iOS app and the WatchKit Extension.
I will talk more about the Watch Connectivity Framework in a future article.
New Interface Objects
watchOS 1 had a limited number of interface objects (such as Button, Table, and so on). watchOS 2 brings a number of useful additions we'll examine next.
Movie Control
You can now play movies on the Apple Watch, using the presentMediaPlayerControllerWithURL:options: method:
let bundle = NSBundle.mainBundle() let url = bundle.URLForResource("sample_mpeg4", withExtension: "mp4") let options = [WKMediaPlayerControllerOptionsAutoplayKey: true] self.presentMediaPlayerControllerWithURL(url!, options: options) { (didPlayToEnd, endTime, error) -> Void in print("Movied finished playback") }
The code snippet above programmatically plays a video named sample_mpeg4.mp4 located in the WatchKit Extension. Figure 3 shows the movie playing in full screen on the Apple Watch Simulator.
Figure 3 Playing back a video.
In addition to playing a movie programmatically using the presentMediaPlayerControllerWithURL:options: method, watchOS 2 comes with the new Movie control (see Figure 4).
Figure 4 Using the Movie control.
The Movie control is a visual control that displays a poster image (customizable) with a play button on top of it. When the user taps on the Movie control, the movie starts playing. The user can also tap on the movie to pause the movie. The following code snippet shows how to set a movie to be played with the Movie control:
//---outlet for the Movie control--- @IBOutlet var movie: WKInterfaceMovie! ... ... let bundle = NSBundle.mainBundle() let url = bundle.URLForResource("sample_mpeg4", withExtension: "mp4") movie.setMovieURL(url!)
The advantage of using the Movie control instead of playing back the movie programmatically is that you don't have to design your UI manually for video playback. (When using the presentMediaPlayerControllerWithURL:options: method, you need to manually invoke it—in a Button's action, for example.)
Figure 5 shows the Movie control in action.
Figure 5 Video playback using the Movie control.
Picker Control
As well as using the Table control to display a list of items, in watchOS 2 you can use the new Picker control (see Figure 6) to show a list of items; both text and images are supported.
Figure 6 The new Picker control.
The following code snippet shows how to load the Picker control with a list of fruit names with their icons:
@IBOutlet var picker: WKInterfacePicker! ... ... let item1 = WKPickerItem() item1.title = "Apple" item1.accessoryImage = WKImage(imageName: "apple") let item2 = WKPickerItem() item2.title = "Pear" item2.accessoryImage = WKImage(imageName: "pear") let item3 = WKPickerItem() item3.title = "Orange" item3.accessoryImage = WKImage(imageName: "orange") picker.setItems([item1, item2, item3])
In Figure 7, the Picker control shows the list of items, with its Style attribute set to List.
Figure 7 The Picker control in List style.
When you set the Style attribute to Stack, you can set the contentImage property to display another image, like this:
let item1 = WKPickerItem() item1.title = "Apple" item1.accessoryImage = WKImage(imageName: "apple") item1.contentImage = WKImage(imageName: "apple") let item2 = WKPickerItem() item2.title = "Pear" item2.accessoryImage = WKImage(imageName: "pear") item2.contentImage = WKImage(imageName: "pear") let item3 = WKPickerItem() item3.title = "Orange" item3.accessoryImage = WKImage(imageName: "orange") item3.contentImage = WKImage(imageName: "orange") picker.setItems([item1, item2, item3])
The Picker control will now show a list of images, as shown in Figure 8.
Figure 8 The Picker control in Stack style.
If you run this code on the Apple Watch Simulator, you can use the scroll wheel on the mouse to scroll through the images, and the watch will animate the images as it swaps from one to the next.
The Picker control also supports the Sequence style, which is similar to the Stack style, but without the animation.
Alerts and Action Sheets
The new watchOS 2 also allows you to display alerts and action sheets. No more using the Label control to display statuses to the user!
The following code snippet displays an alert (see Figure 9) with two buttons:
let okAction = WKAlertAction(title: "OK", style: WKAlertActionStyle.Default) { () -> Void in print("OK") } let cancelAction = WKAlertAction(title: "Cancel", style: WKAlertActionStyle.Default) { () -> Void in print("Cancel") } presentAlertControllerWithTitle("Title", message: "Message", preferredStyle: WKAlertControllerStyle.Alert, actions: [okAction, cancelAction])
Figure 9 An alert with two buttons.
You can also display an action sheet by using the ActionSheet style:
let okAction = WKAlertAction(title: "OK", style: WKAlertActionStyle.Default) { () -> Void in print("OK") } let cancelAction = WKAlertAction(title: "Cancel", style: WKAlertActionStyle.Default) { () -> Void in print("Cancel") } presentAlertControllerWithTitle("Title", message: "Message", preferredStyle: WKAlertControllerStyle.ActionSheet, actions: [okAction, cancelAction])
Figure 10 shows the action sheet with the Cancel button displayed.
Figure 10 An action sheet.
You can also display the buttons side-by-side, using the SideBySideButtonsAlert style:
let okAction = WKAlertAction(title: "OK", style: WKAlertActionStyle.Default) { () -> Void in print("OK") } let cancelAction = WKAlertAction(title: "Cancel", style: WKAlertActionStyle.Default) { () -> Void in print("Cancel") } presentAlertControllerWithTitle("Title", message: "Message", preferredStyle: WKAlertControllerStyle.SideBySideButtonsAlert, actions: [okAction, cancelAction])
Figure 11 shows the alert with the two buttons displayed side-by-side.
Figure 11 An alert with buttons displayed side-by-side.
Recording Audio
watchOS 2 brings the capability to record audio on your Apple Watch, via the following method:
presentAudioRecordingControllerWithOutputURL:preset:maximumDuration:actionTitle:
The following code snippet records a 60-second audio clip on the Apple Watch:
let paths = NSSearchPathForDirectoriesInDomains( NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) let path = paths.first!.stringByAppendingPathComponent( "myrecording.mp4") let filePath = NSURL(string: path) presentAudioRecordingControllerWithOutputURL(filePath!, preset: WKAudioRecordingPreset.HighQualityAudio, maximumDuration: 60, actionTitle: "Recording") { (didSave, error) -> Void in //---print error--- }
Figure 12 shows the recording in progress. After the recording, you can also play back the audio clip. The recorded audio clip will be saved to the path specified in the code.
Figure 12 Recording an audio clip and playing it back.
Storing Data Securely with Keychain
watchOS 2 supports saving secure data by using the Keychain. The following code snippet stores the username and password of an account securely in the Keychain of the Apple Watch:
let key = "username" let value = "password" let valueData = value.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) let secItem = [ kSecClass as NSString : kSecClassGenericPassword as NSString, kSecAttrAccessible as NSString: kSecAttrAccessibleWhenUnlocked, kSecAttrAccount as NSString : key, kSecValueData as NSString : valueData!, kSecAttrSynchronizable as NSString : kCFBooleanTrue ] as NSDictionary var result: Unmanaged<AnyObject>? = nil let status = Int(SecItemAdd(secItem, &result)) switch status{ case Int(errSecSuccess): print("Password saved.") case Int(errSecDuplicateItem): print("Duplicate password") default: print("Error: \(status)") }
Setting the kSecAttrAccessible attribute makes the item accessible when the Apple Watch is on the user's wrist and unlocked. The item will be locked when the user removes the Apple Watch.
The following code snippet retrieves the previously saved item from the Keychain and prints out its detail:
let key = "username" //---first, search for the attributes of a particular key--- var query = [ kSecClass as NSString : kSecClassGenericPassword as NSString, kSecAttrAccount as NSString : key, kSecReturnAttributes as NSString : kCFBooleanTrue, kSecAttrSynchronizable as NSString : kCFBooleanTrue ] as NSDictionary var valueAttributes: Unmanaged<AnyObject>? = nil var results = Int(SecItemCopyMatching(query, &valueAttributes)) if results == Int(errSecSuccess){ let attributes = valueAttributes!.takeRetainedValue() as! NSDictionary let key = attributes[kSecAttrAccount as NSString] as! String let creationDate = attributes[kSecAttrCreationDate as NSString] as! NSDate let modifiedDate = attributes[kSecAttrModificationDate as NSString] as! NSDate print("Key - \(key)") print("Creation Date - \(creationDate)") print("Modification Date - \(modifiedDate)") //---next, search for the data of a particular key--- query = [ kSecClass as NSString : kSecClassGenericPassword as NSString, kSecAttrAccount as NSString : key, kSecReturnData as NSString : kCFBooleanTrue, kSecAttrSynchronizable as NSString : kCFBooleanTrue ] as NSDictionary var returnedData: Unmanaged<AnyObject>? = nil results = Int(SecItemCopyMatching(query, &returnedData)) if results == Int(errSecSuccess){ let data = returnedData!.takeRetainedValue() as! NSData let value = NSString(data: data, encoding: NSUTF8StringEncoding) print("Value - \(value!)") } else { print("Error: \(results)") } } else { print("Error: \(results)") }
Summary
In this article, I have briefly discussed some of the important new features available in the new watchOS 2. Although watchOS 1 applications will continue to be supported, you should seriously consider migrating your existing code to watchOS 2 if you want to develop some of the more interactive apps on the Apple Watch.
In addition to the new features that I have discussed in this article, two additional topics must wait for space in a later article:
- Watch Connectivity. A framework for sending data between the containing iOS app and the Watch app.
- ClockKit. A framework for managing complications (additional information displayed on the clock face of the Apple Watch).