WatchKit FAQ

Check out the answers to the most commonly asked questions about WatchKit – Apple’s framework for making Apple Watch apps! By Soheil Azarpour.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Contents

Hide contents

WatchKit FAQ

30 mins

What happens if I don’t implement static or dynamic notification interfaces?

Even if you don’t have a watch app, the system will still display notifications for your iPhone app on the watch. However, the default notification interface has no custom styling. If you have interactive notifications, the system will hide those actions on the watch.

What’s the difference between setImage(_:) and setImageNamed(_:)?

You should use setImageNamed(_:) when the image you want to display is either cached on the watch on is in an asset catalog in the watch app’s bundle, and use setImage(_:) when the image isn’t cached — this will transfer the image data to the Apple Watch over the air!

An image is cached on the watch if it’s one of the following:

  1. Bundled with the watch app target in the project, meaning that the image resides in the asset catalog belonging to Watch App target in the project
  2. Explicitly cached on the watch beforehand via one of these WKInterfaceDevice APIs: addCachedImage(_:name:) or addCachedImageWithData(_:name:)

Can I use iCloud in a watch App?

Yes, you can use iCloud with the Apple Watch app. Lister: A Productivity App, which is one of the sample projects provided by Apple, demonstrates how to use iCloud in this context.

Animation

How can I add animations to my watch app?

There is only one way to display animations on the Apple Watch: image sequences. To make something appear animated, you have to pre-generate a series of images, and then cycle through them like a flip-book. The era of the animated GIF is back! ;]

ClappingDude

You can display a sequence of static images in a WKInterfaceImage object to create your own custom animations.

@IBOutlet weak var image: WKInterfaceImage?
...
image?.setImageNamed(image1) // Load the initial image using the required <name><number> format
image?.startAnimating() // Starts animating
...
image?.stopAnimating() // Optional. Stops animating.

You can also animate only a subset of images as shown in the following code snippet:

image?.startAnimatingWithImagesInRange(range, duration: 2, repeatCount: 1)

Can I create animations for the Apple Watch in code?

Yes, but perhaps not in the way you think – as I mentioned above there is no Core Animation framework or equivalent. You can use Core Graphics to render each frame of the animation to an offscreen context, render it to an instance of UIImage, and at the end you’ll have a sequence of images you can animate on the Apple Watch.

The following code snippet shows how you can create an animation for a moving circle using Core Graphics by generating an image sequence for each frame (code courtesy of Jack Wu from WatchKit by Tutorials):

// Create an offscreen context
UIGraphicsBeginImageContextWithOptions(size, opaque, scale)
let context = UIGraphicsGetCurrentContext()

for centerX in 0..100 {
  // Draw a circle at centerX.
  // Create a snapshot.
  let image = UIGraphicsGetImageFromCurrentImageContext()

  // Write the image as a file
  let data = UIImagePNGRepresentation(image)
  let file = "path\\image_\(centerX).png"
  data.writeToFile(file, atomically: true)
}

// End the context.
UIGraphicsEndImageContext()

fitness_2

What is the maximum animation frame rate on the Apple Watch?

You can’t set the animation frame rate on the Apple Watch. However, it’s possible to set the animation length and the let the system automatically determine the frame rate.

If the sequence of images are sent over the air, for instance when images are not cached, they will run up to 10 fps. If the images are already cached on the Apple Watch via the image cache or asset catalog in the WatchKit app bundle, they will run up to 30 fps.

Debugging and Unit Testing

How can I run and debug both the iPhone app and the Apple Watch app at the same time using the simulators?

  1. Build and run the watch app; this launches the Apple Watch simulator, runs the watch app and attaches it to the debugger.
  2. Then in the iOS simulator, tap on your iPhone app’s icon to launch it.
  3. Now go back to Xcode, and from the top menu select Debug\Attach To Process and then select the appropriate iPhone app. This will add a new process to your Xcode Debug Navigator and attach the iPhone app to the Debugger.

How can I unit test my WatchKit extension?

You can write unit tests for your watch app in the same way you write unit tests for your iPhone or iPad apps; simply add a new Unit Test target for the WatchKit extension to your project. However, you can’t specify the watch app as a Host Application.

For the iPhone app target, the iPhone application appears as the Host Application

01

For the iPhone app target, the iPhone application appears as the Host Application

But for the WatchKit Extension target there is no Host Application

02

But for the WatchKit Extension target there is no Host Application

You have to add every single file that you want to test to the WatchKit extension Unit Test target explicitly.

Add the files that you want to test from the WatchKit extension to the Unit Test target

03

Add the files that you want to test from the WatchKit extension to the Unit Test target

Sharing Data

How can you share data between a WatchKit extension and its containing iOS app?

You need to enable App Groups; this refers to a container on the local file system that both an extension and its containing iPhone app can access. You can define multiple app groups and enable them for different extensions.

Once App Groups are enabled, you can use either of the following techniques, based on your needs:

  • Read and write to a shared container directly. You get the URL of the shared container by asking NSFileManager. There is a single API for this:
    let kMyAppGroupName = "com.raywenderlich.mywatchapp.container"
    var sharedContainerURL: NSURL? = NSFileManager.defaultManager().
      containerURLForSecurityApplicationGroupIdentifier(kMyAppGroupName)
    	
  • Use shared NSUserDefaults. To create defaults storage in the shared container, you need to initialize a new NSUserDefaults instance using NSUserDefaults(suiteName:) and pass in the unique identifier of the app group, like so:
    let kMyAppGroupName = "com.raywenderlich.mywatchapp.container"
    let sharedUserDefaults = NSUserDefaults(suiteName: kMyAppGroupName)
    	
let kMyAppGroupName = "com.raywenderlich.mywatchapp.container"
var sharedContainerURL: NSURL? = NSFileManager.defaultManager().
  containerURLForSecurityApplicationGroupIdentifier(kMyAppGroupName)
	
let kMyAppGroupName = "com.raywenderlich.mywatchapp.container"
let sharedUserDefaults = NSUserDefaults(suiteName: kMyAppGroupName)
	

Note: When you want to read from or write to a shared container, you must do so in a coordinated manner to avoid data corruption, because a shared container can be accessed simultaneously by separate processes. The recommended way to coordinate reads and writes is using NSFilePresenter and NSFileCoordinator. However, it’s advised against using file coordination APIs in an app extension because they can result in deadlocks.

The reason behind this is the lifecycle of app extensions. App extensions have only 3 states: (a) running, (b) suspended and (c) terminated. If an app extension that uses file coordination APIs is suspended while writing, it never gets the chance to relinquish ownership, so other processes get deadlocked.

However, an iPhone or an iPad application is notified via its app delegate when it’s placed in the background, and it should then remove file presenters. When the app is brought back to the foreground, the file presenters can be re-added.

Instead, you can use atomic safe-save operations like NSData‘s writeToURL(_:atomically:). SQLite and Core Data also allow you to share data in a shared container in safe manner between multiple processes, even if one is suspended mid-transaction.

You can learn more about this in Technical Note TN2408: Accessing Shared Data from an App Extension and its Containing App.

Note: When you want to read from or write to a shared container, you must do so in a coordinated manner to avoid data corruption, because a shared container can be accessed simultaneously by separate processes. The recommended way to coordinate reads and writes is using NSFilePresenter and NSFileCoordinator. However, it’s advised against using file coordination APIs in an app extension because they can result in deadlocks.

The reason behind this is the lifecycle of app extensions. App extensions have only 3 states: (a) running, (b) suspended and (c) terminated. If an app extension that uses file coordination APIs is suspended while writing, it never gets the chance to relinquish ownership, so other processes get deadlocked.

However, an iPhone or an iPad application is notified via its app delegate when it’s placed in the background, and it should then remove file presenters. When the app is brought back to the foreground, the file presenters can be re-added.

Instead, you can use atomic safe-save operations like NSData‘s writeToURL(_:atomically:). SQLite and Core Data also allow you to share data in a shared container in safe manner between multiple processes, even if one is suspended mid-transaction.

You can learn more about this in Technical Note TN2408: Accessing Shared Data from an App Extension and its Containing App.