Sharing Data Between a WatchKit App and the Containing iOS App

PreviousTable of ContentsNext
Handling User Input in a WatchKit AppWatchKit Extension and iOS App File and Data Sharing - A Tutorial


Purchase the fully updated watchOS 2/Swift 2 edition of this 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


It has already been established in previous chapters that the elements that make up a WatchKit app are bundled into the same package as the containing (or parent) iOS app and that the WatchKit app extension executes on the same device as the iOS app. Regardless of these facts, however, the WatchKit app extension and the containing iOS app each run within separate processes. This process separation (sometimes referred to “sandboxing”) means that the extension and parent iOS app do not, by default, have access to each other’s data and file storage. This chapter will explore the use of app groups to enable a WatchKit extension and iOS app to share access to files and data.

Sandboxes, Containers and User Defaults

Although both the containing iOS app and the corresponding extension for a WatchKit app both execute on the same physical iPhone device they are said to run in separate sandbox environments. Sandboxes are a security mechanism that enforce sets of rules in terms of what an app can and cannot do when running on an iOS device. Sandboxing, for example, prevents one app on a device from interfering with, or accessing files and data belonging to another app installed on the same device.

Included within the sandbox of each app is a container. This is essentially a file system area containing directories into which the app can store and access files. These can be any type of file including images, videos, plain text files or even SQLite or Core Data databases. The sandbox rules dictate that an app in one sandbox cannot access the files in the container of a second app and these same rules apply equally to the relationship between a WatchKit extension and the containing iOS app.

In addition to files, each app can have associated with it a set of user defaults. User defaults are used to store preferences selected by the user for the way in which an app appears and functions. Consider, for example, an iOS app that provides the user with the ability to configure a range of preferences such as the preferred currency in which to display monetary values or the font to be used when displaying text. The ideal location to store such settings is in the user’s defaults database. As with file access, each app or extension has its own set of defaults data and sandboxing prevents one process from accessing the user defaults data of another sandboxed process.

Sharing Data Using App Groups

The sharing of files and data between a WatchKit extension and the parent iOS app can be achieved through the use of shared app groups. When the iOS app and WatchKit extension are enrolled in the same app group they are given access to a shared container and shared user defaults data. This allows files to be shared between the parent app and the extension, along with any user defaults configured using the NSUserDefaults class. The diagram in Figure 14-1 illustrates this concept:


An iOS sandbox diagram

Figure 14-1



Adding an App or Extension to an App Group

App Group settings are contained in entitlement files using the com.apple.security.application-groups key. Both the WatchKit app Extension and the parent iOS app must include this entitlement. The value assigned to the com.apple.security.application-groups key indicates the app group to which membership is required. This value is typically set to the package name of the iOS app prefixed with “group.” and must match in both the parent app and extension entitlement files.

While it is possible to manually create the necessary entitlement files, by far the easiest way to configure app group membership is to do so through the Xcode Capabilities panel. To configure app group support for the iOS app, select the target located at the top of the Project Navigator panel and click on the Capabilities tab in the main panel. Within the capabilities panel, locate the App Groups section and switch it to the On position:


Enabling app groups in Xcode

Figure 14-2


When app groups have been enabled in the Capabilities screen, any existing app groups associated with your Apple developer account will be listed. To make the current app a member of any of those groups simply enable the checkbox next to the group name:


Specifying the app group identifier

Figure 14-3


To add a new app group to your account, simply click on the + button and enter the new app group name. Add the current app to the newly added app group by enabling the checkbox next to the group name. The app group will subsequently appear as an option in all other project targets within the Xcode Capabilities panel.

With the iOS app added to the app group, the WatchKit app extension must also be added as a member of the same group in order to gain access to the shared container. By default, the Capabilities panel displays the settings for the iOS app target. To access the capability settings for the WatchKit Extension, use the menu located in the top left-hand corner of the Capabilities panel as indicated in Figure 14-4:


The target selection menu in the Xcode Capabilities panel

Figure 14-4


When clicked, this menu will present a list of targets contained within the current project as shown in Figure 14-5, one of which will be the WatchKit extension. Select this option and repeat the steps followed for the iOS app to enable and configure app group support, making sure to select the same group name as that chosen for the iOS app.


Choosing the WatchKit extension in the Xcode Capabilities panel

Figure 14-5


A review of the files in the Project Navigator panel will reveal that entitlement files have been added for each of the two targets, the contents of which will read as follows (allowing for differences in the app group name):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>com.apple.security.application-groups</key>
        <array>
                <string>group.com.payloadmedia.SharingData</string>
        </array>
</dict>
</plist>

With both the parent iOS app and the WatchKit extension added to the same app group, code can now be written to access the shared data container.

Purchase the fully updated watchOS 2/Swift 2 edition of this 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

App Group File Sharing

The first step in terms of sharing files via an app group is to identify the URL of the shared container. This can be achieved by obtaining a reference to the app’s default NSFileManager instance and making a call to the containerURLForSecurityApplicationGroupIdentifier method of that object, passing through as an argument the name of the app group. For example:

let fileManager = NSFileManager.defaultManager()

let url =    
   fileManager.containerURLForSecurityApplicationGroupIdentifier(
				"group.com.payloadmedia.SharingData")

Once the URL has been obtained, it can be used to share data in terms of files and SQLite or Core Data based databases. When writing data to flat files in a container shared by an extension and a containing app, Apple advices against using the standard file coordination techniques and recommends using atomic write operations to avoid deadlocks occurring. An atomic write operation writes new data to a temporary file and then renames it to replace the original file. The following code, for example, obtains a reference to the shared container, appends a file name (datafile.dat) to the URL, checks that the file exists and then atomically writes a string to it:

let fileManager = NSFileManager.defaultManager()

let url = 
   fileManager.containerURLForSecurityApplicationGroupIdentifier(
		"group.com.payloadmedia.SharingData")

let dirPath = url?.path
let filePath = dirPath?.stringByAppendingPathComponent("datafile.dat")

if fileManager.fileExistsAtPath(filePath!) {
    let databuffer = ("hello world" 
         as NSString).dataUsingEncoding(NSUTF8StringEncoding)
    databuffer?.writeToFile(filePath!, atomically: true)
}

App Group User Default Sharing

iOS allows applications to access and store user settings via the NSUserDefaults class. These settings are stored in the user’s defaults database by the app and can be accessed via a variety of methods provided by the NSUserDefaults class.

As with file storage, the rules imposed by sandbox security prevent the default settings for one app from being accessed by another app or app extension. In terms of WatchKit app development, therefore, app group sharing must be used if the extension and parent app need to share user default settings. In addition to sharing user default settings, this technique also provides a useful and efficient way to share small amounts of data that are not necessarily related to user default preferences between the iOS app and the WatchKit extension.

When sharing user defaults, the shared database is identified by a suite name which is the name assigned to the app group when it was created. The following code, for example, obtains a reference to a shared user defaults suite before storing into it a string value in the form of a key-value pair:

let myDefaults = NSUserDefaults(suiteName: 
			"group.com.payloadmedia.SharingData")

myDefaults?.setObject("sterling", forKey: "currency")

Once a default has been set, it can then be accessed from within the WatchKit app extension (as long as it is a member of the same app group) as follows:

let myDefaults = NSUserDefaults(suiteName: 
		"group.com.payloadmedia.SharingData")

let preference = myDefaults?.stringForKey("currency")

The data shared using this approach can take the form of NSString, NSData, NSArray, NSDictionary, NSNumber and NSDate objects, allowing for a wide range of data sharing options.

Summary

The WatchKit extension and containing iOS app are separate processes that execute within individual sandbox environments. The purpose of sandboxing is to prevent one process from accessing the files and data stored by another process. In order for files and data to be shared between the iOS app and the WatchKit extension, both must belong to the same app group. App groups allow processes to share files through a shared container and to gain access to shared data using the NSUserDefaults class.


Purchase the fully updated watchOS 2/Swift 2 edition of this 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
Handling User Input in a WatchKit AppWatchKit Extension and iOS App File and Data Sharing - A Tutorial