Guides and Sample Code

Developer

App Programming Guide for watchOS

On This Page

Leveraging iOS Technologies

watchOS has access to many of the same technologies found in iOS apps; however, even if a technology is available, you may not be able to use it in quite the same way you did on iPhone. Here are some guidelines for deciding when and how to use a particular technology:

  • Be aware that permission for some technologies must be accepted on the user’s iPhone. The user must grant permission to use specific system technologies, such as Core Location. Using one of these technologies in your WatchKit extension triggers the appropriate prompt on the user’s iPhone. Apple Watch also displays a prompt of its own, asking the user to view the permission request on the iPhone. For information about the technologies that require user permission, see “Supporting User Privacy” in App Programming Guide for iOS.

  • Do not use background execution modes for a technology. In general, Watch apps are considered foreground apps; they run only while the user interacts with one of their interfaces. As a result, the corresponding WatchKit extension cannot take advantage of most background execution modes to perform tasks. However, there are a few exceptions:

    • Use NSURLSession objects to initiate network-based data transfers.

    • Use either the WKAudioFilePlayer class or the WKAudioFileQueuePlayer class to play extended audio files in the background. Use of these objects requires enabling the Audio, AirPlay, and Picture in Picture background mode capability in your WatchKit App target (not your WatchKit extension target). Enabling this mode adds the UIBackgroundModes key to the app’s Info.plist file.

    • Use an HKWorkoutSession object to start and stop workouts. Use of this object requires enabling the Workout Processing background mode capability in your WatchKit extension target (not your WatchKit App target). Enabling this mode adds the UIBackgroundModes key to the extension’s Info.plist file.

  • Avoid long-running tasks. A WatchKit extension is suspended soon after the user stops interacting with the corresponding Watch app. Because interactions with the Watch app are typically brief, the extension is likely to be suspended before a long-running task can complete.

The best solution for performing any long-running tasks is to perform the task in your iOS app instead. For example, instead of starting location services in your WatchKit extension, start it in your iOS app and report updates back to your extension. Your iOS app can gather the needed data and send it to your extension using the Watch Connectivity framework. For information about how to handle communication between your iOS app and WatchKit extension, see Communicating with Your Companion iOS App.

Performing Network-Based Operations

WatchKit extensions can communicate directly with the network using NSURLSession objects. The NSURLSession class in watchOS supports all of the same transfer options as iOS, including background transfers. For information on how to use this class, see URL Session Programming Guide.

When fetching data for your Watch app, remember that your app can be deactivated at any time. If you use a default session for your request, your app might exit before it receives any data. Instead, use background sessions. Background sessions let the system manage the file transfer, even after your app has deactivated. The system then triggers a background task when the download completes, letting you process the downloaded information. For more information, see WKURLSessionRefreshBackgroundTask Class Reference.

If your Watch app and iOS app use the same server, it is usually simpler for each process to fetch its data separately rather than trying to sync from one app to the other. If you want to ensure the two apps are synchronized, use the updateApplicationContext:error: method to communicate basic information such as record IDs or title strings so that each app knows what the other has fetched.

For information about using an NSURLSession object to perform network requests, see URL Session Programming Guide.

Requesting Background Assertions for Asynchronous Tasks

Due to the Watch app’s short lifecycle, you need to be careful when using asynchronous tasks. Otherwise the system may suspend or terminate your app before the task can complete. This is particularly true when implementing methods like the didReceiveNotification:withCompletion: or applicationDidEnterBackground methods. In both cases, you are only given a brief period of time to respond, and the system may suspend your app shortly after the method returns.

When performing critical tasks (for example, saving data), you can request a background assertion to complete the task by calling the NSProcessInfo class’s performExpiringActivityWithReason:usingBlock: method. When you are granted a background assertion, your app continues to run in the background until the provided block returns. Note, however, that the system can revoke the assertion at any point. If this happens, the system calls your block again, passing a true value to the expiring parameter.

Listing 5-1 shows how you would use the performExpiringActivityWithReason:usingBlock: method to request a background assertion. This sample uses the semaphore’s wait method to halt the execution of the provided block. This lets the app continue running in the background until the semaphore’s signal method is called. If you call an asynchronous API from within this block, you must call the semaphore’s signal method once the asynchronous API completes. If, on the other hand, you are only performing synchronous tasks, you can delete the semaphore entirely.

Listing 5-1Requesting a background assertion
  1. let semaphore = DispatchSemaphore(value: 0)
  2. ProcessInfo.processInfo.performExpiringActivity(withReason: "Debug String") { expiring in
  3. // This block is executed on an anonymous background queue.
  4. // Note: this block can be called multiple times. If you are given
  5. // a background task assertion, the block will be called with
  6. // expiring == false. Then, if the app suddenly needs to terminate, the system can revoke
  7. // the assertion by calling this block a second time with expiring == true.
  8. if expiring {
  9. // Your app has not been given a background task assertion,
  10. // or the current background task assertion has been revoked.
  11. // You have a brief bit of time to perform any clean-up activities.
  12. // Clean up any running tasks.
  13. // Cancel any long synchronous tasks (if possible).
  14. // Unblock the thread (if blocked).
  15. semaphore.signal()
  16. } else {
  17. // Your app has been given a background task assertion.
  18. // The background task assertion lasts until this method returns, or until
  19. // the assertion is revoked.
  20. // perform long synchronous tasks here
  21. // or kick off asynchronous task and block this thread.
  22. // Be sure to unblock the thread when the asynchronous task is complete.
  23. semaphore.wait(timeout: .now() + .seconds(60))
  24. }
  25. }

Storing Data Securely in the Keychain

To store information securely on Apple Watch, use the keychain interfaces. Setting the kSecAttrAccessible attribute of a data item to kSecAttrAccessibleWhenUnlocked makes that item accessible when Apple Watch is on the user’s wrist and has been unlocked. When the user removes Apple Watch, the keychain is locked again to prevent access to any items with this attribute.

Listing 5-2 shows how to use the kSecAttrAccessibleWhenUnlocked key to save a data item securely to the user’s keychain. The example saves password information, but you can use the key to save other types of data.

Listing 5-2Securing data in the user’s keychain

Objective-C

  1. NSString* secret = @"SomeSecretInformation";
  2. NSData* secretData = [secret dataUsingEncoding:NSUnicodeStringEncoding];
  3. if (secretData) {
  4. NSDictionary* attributes = [NSDictionary dictionaryWithObjectsAndKeys:
  5. kSecClassGenericPassword, kSecClass,
  6. kSecAttrAccessibleWhenUnlocked, kSecAttrAccessible,
  7. @"myservice", kSecAttrService,
  8. @"account name", kSecAttrAccount,
  9. secretData, kSecValueData, nil];
  10. SecItemAdd((__bridge CFDictionaryRef)attributes, nil);
  11. }

Swift

  1. let secret = "SomeSecretInformation"
  2. if let secretData = secret.dataUsingEncoding(NSUnicodeStringEncoding) {
  3. let attributes: [NSString: NSObject] = [
  4. kSecClass: kSecClassGenericPassword,
  5. kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked,
  6. kSecAttrService: "myservice",
  7. kSecAttrAccount: "account name",
  8. kSecValueData: secretData ]
  9. SecItemAdd(attributes, nil)
  10. }

For more information about how to secure information in the user’s keychain, see Keychain Services Programming Guide.

Handoff Support

Apple Watch supports the creation of activities that can be completed on other devices using Handoff. Use the updateUserActivity:userInfo:webpageURL: method of WKInterfaceController to create activities and advertise them to other devices.

Apple Watch does not handle activities generated by other devices.

Opening URLs

Apps can initiate telephone calls or SMS messages using the openSystemURL: method of the shared WKExtension object. When you open a URL that uses the tel or sms scheme, Apple Watch redirects that URL to the appropriate system app for handling. You can also open Passbook URLs by using the openSystemURL: method to open the URL associated with a PKPass object.

For information about how to create URLs using the tel and sms URL schemes, see Apple URL Scheme Reference. For more information about using PassKit, see Wallet Developer Guide.

Remote Control Events and Now Playing Information

Apple Watch uses the remote control events system to manage the playback of audio or video on a user’s paired iPhone. Whenever Now Playing information is showing in iPhone’s Control Center, the Now Playing app also appears as the last app in Apple Watch’s dock. Users can add the Now Playing app to the dock permanently.

The transport controls of the Now Playing app generate remote control events for the iOS app that is currently playing content. An iOS app that registers handlers with the commands of the MPRemoteCommandCenter object receives these events automatically when it is the Now Playing app. You do not need to do any extra work to support remote control events coming from Apple Watch.

The watch’s Now Playing app automatically displays any Now Playing information provided by the currently playing iOS app. An iOS app provides this information using the MPNowPlayingInfoCenter object. As your app plays its content, it should update the values of the nowPlayingInfo dictionary. Apple Watch automatically retrieves this information and displays it. In addition, tapping the track title in the Now Playing app launches the iOS app’s Watch app if one is available.

For information on how to implement support for remote control events and now playing information in your iOS app, see Remote Control Events.

The Now Playing app also displays information for tracks played by Watch apps that support extended audio content. The app obtains the information to display from the properties of the WKAudioFileAsset and WKAudioFilePlayerItem objects currently being played.

For information about playing extended audio from your Watch app, see Playing Extended Audio Content.

Sharing Code Between an iOS App and a watchOS App

You can share code, but not frameworks, between your iOS app and Watch app. Because the apps run on separate platforms with different architectures, source files must be compiled separately for each platform. If you still want to use a framework to manage any shared source files, you must create separate framework targets for each platform and add your shared source files to each framework.

If you already have an iOS framework, you can duplicate the framework and modify it to support watchOS.

To duplicate and configure your framework target for watchOS
  1. Open the project editor pane of Xcode. (The pane is normally closed.)

  2. Control-click the target to display a context menu with a Duplicate command.

  3. Change the name of the target so that you can identify it easily later.

  4. In Build settings, change the following values:

    • Change the Supported Platforms setting to watchOS.

    • Change the Base SDK setting to the latest watchOS.

    • Change the Product Name setting so that it matches the name of your iOS framework. You want both frameworks to be built with the same name.

  5. Add the framework to your WatchKit extension’s list of linked frameworks.