Extensibility PG
Extensibility PG
Extensibility PG
Contents
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
Contents
Share 42
Understand Share Extensions 42
Use the Xcode Share Template 43
Design the UI 44
Posting Content 45
Validating Input 46
Previewing Content (iOS Only) 47
Configuring a Post (iOS Only) 47
Action 49
Understand Action Extensions 49
Use the Xcode Action Extension Template 50
Design the UI 51
Returning Edited Content to the Host 52
Photo Editing 53
Understand How a Photo Editing Extension Works with Photos 53
Use the Xcode Photo Editing Template 54
Design the UI 55
Handling Memory Constraints 55
Testing a Photo Editing Extension 55
Finder Sync 57
Understand Finder Sync 57
Creating a Finder Sync Extension in Xcode 58
Set the Required Property List Values 58
Specify Folders to Monitor 59
Set Up Badge Images 60
Implement FIFinderSync methods 60
A Typical Finder Sync Use Case 62
Performance Concerns 63
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
Contents
Document Provider 64
Understand Document Provider Extensions 64
Document Picker View Controller Extension 65
Life Cycle 66
Creating the Document Picker View Controller Extension 67
File Provider Extension 71
Creating the File Provider Extension 71
Providing a Great User Experience in an Uncertain World 75
File Coordination 75
Downloading Files 76
Detecting and Communicating Conflicts 77
Logging in and out 77
Custom Keyboard 79
Understand User Expectations for Keyboards 79
Keyboard Features That iOS Users Expect 79
System Keyboard Features Unavailable to Custom Keyboards 80
API Quick Start for Custom Keyboards 82
Development Essentials for Custom Keyboards 85
Designing for User Trust 85
Providing a Way to Switch to Another Keyboard 89
Getting Started with Custom Keyboard Development 90
Using the Xcode Custom Keyboard Template 90
Configuring the Info.plist file for a Custom Keyboard 92
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
Share 42
Listing 6-1
Listing 6-2
Action 49
Listing 7-1
Document Provider 64
Figure 10-1
Custom Keyboard 79
Figure 11-1
Figure 11-2
Table 11-1
Table 11-2
Table 11-3
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
Important: This is a preliminary document for an API or technology in development. Apple is supplying
this information to help you plan for the adoption of the technologies and programming interfaces described
herein for use on Apple-branded products. This information is subject to change, and software implemented
according to this document should be tested with final operating system software and final documentation.
Newer versions of this document may be provided with future betas of the API or technology.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
Starting in iOS 8.0 and OS X v10.10, an app extension lets you extend custom functionality and content beyond
your app and make it available to users while theyre using other apps or the system. You create an app
extension to enable a specific task; after users get your extension, they can use it to perform that task in a
variety of contexts. For example, if you provide an extension that enables sharing to your social sharing website,
users can use it to post a remark while surfing the web. Or if you provide an extension that displays current
sports scores, users can put it in Notification Center so that they can get the latest scores when they open the
Today view. You can even create an extension that provides a custom keyboard that users can use in place of
the iOS system keyboard.
Extension point
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
Extension point
Because the system defines specific areas for app extensions, its important to choose the area that best matches
the functionality you want to deliver. For example, if you want to create an extension that enables a sharing
experience, use the Share extension point, starting with the Share Extension Xcode template.
Important: Each app extension you create matches exactly one of the extension points listed in Table 1-1.
You dont create a generic extension that matches more than one extension point.
Xcode and the App Store Help You Create and Deliver App
Extensions
An app extension is different from an app. Although you must use an app to contain and deliver your extensions,
each extension is a separate binary that runs independent of the app used to deliver it.
You create an app extension by adding a new target to an app. As with any target, an extension target specifies
settings and files that combine to build a product within your app project. You can add multiple extension
targets to a single app (an app that contains one or more extensions is called a containing app).
The best way to start developing an app extension is to use one of the templates that Xcode provides for each
extension point on both platforms. Each template includes extension pointspecific implementation files and
settings, and produces a separate binary that gets added to your containing apps bundle.
To distribute app extensions to users, you submit a containing app to the App Store. When a user installs your
containing app, the extensions it contains are also installed.
After installing an app extension, a user must take action to enable it. Often, users can enable an extension
within the context of their current task. If your extension is a Today widget, for example, users can edit the
Today view in Notification Center to enable your extension. In other cases, users can use Settings (in iOS) or
System Preferences (in OS X) to enable and manage the extensions they install.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
An app extension is not an app. It implements a specific, well scoped task that adheres to the policies defined
by a particular extension point.
In step 2 of Figure 2-1, the system instantiates the app extension identified in the host apps request and sets
up a communication channel between them. The extension displays its view within the context of the host
app and then uses the items it received in the host apps request to perform its task (in this example, the
extension receives the selected text).
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
10
In step 3 of Figure 2-1, the user performs or cancels the task in the app extension and dismisses it. In response
to this action, the extension completes the host apps request by immediately performing the users task or,
if necessary, initiating a background process to perform it. The host app tears down the extensions view and
the user returns to their previous context within the host app. When the extensions task is finished, whether
immediately or later, a result may be returned to the host app.
Shortly after the app extension performs its task (or starts a background session to perform it), the system
terminates the extension, as shown in step 4.
There is no direct communication between an app extension and its containing app; typically, the containing
app isnt even running while a contained extension is running. An app extensions containing app and the
host app dont communicate at all.
In a typical request/response transaction, the system opens an app extension on behalf of a host app, conveying
data in an extension context provided by the host. The extension displays a user interface, performs some
work, and, if appropriate for the extensions purpose, returns data to the host.
The dotted line in Figure 2-2 represents the limited interaction available between an app extension and its
containing app. A Today widget (and no other app extension type) can ask the system to open its containing
app by calling the openURL:completionHandler: method of the NSExtensionContext class. As indicated
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
11
by the Read/Write arrows in Figure 2-3, any app extension and its containing app can access shared data in a
privately defined shared container. The full vocabulary of communication between an extension, its host app,
and its containing app is shown in simple form in Figure 2-3.
Figure 2-3
Note: Behind the scenes, the system uses interprocess communication to ensure that the host app
and an app extension can work together to enable a cohesive experience. In your code, you never
have to think about this underlying communication mechanism, because you use the higher-level
APIs provided by the extension point and the system.
Access a sharedApplication object, and so cannot use any of the methods on that object
Use any API marked in header files with the NS_EXTENSION_UNAVAILABLE macro, or similar unavailability
macro, or any API in an unavailable framework
For example, in iOS 8.0, the HealthKit framework and EventKit UI framework are unavailable to app
extensions.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
12
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
13
When youre ready to develop an app extension, begin by choosing the extension point that supports the user
task you want to facilitate. Use the corresponding Xcode app extension template and enhance the default files
with custom code and user interface (UI). After you optimize and test your app extension, youre ready to
distribute it within your containing app.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
14
To add a new target to your Xcode app project, choose File > New > Target. In the sidebar on the left side of
the new target dialog, choose Application Extension for iOS or OS X. In the pane on the right side of the dialog,
Xcode displays the templates you can choose. For example, Figure 3-1 shows the templates you can use to
create an iOS app extension.
Figure 3-1
After you choose a template and finish adding the target to your project, you should be able to build and run
the project even before you customize the extension code. When you build an extension based on an Xcode
template, you get an extension bundle that ends in .appex.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
15
Note about 64-bit architecture: An app extension target must include the arm64 (iOS) or x86_64
architecture (OS X) in its Architectures build settings or it will be rejected by the App Store. Xcode
includes the appropriate 64-bit architecture with its Standard architectures setting when you create
a new app extension target.
If your containing app target links to an embedded framework, the app must also include 64-bit
architecture or it will be rejected by the App Store.
For more information about 64-bit development, see 64-Bit Transition Guide for Cocoa Touch or
64-Bit Transition Guide for Cocoa , depending on your target platform.
In most cases, you can test the default app extension by enabling it in System Preferences or Settings and then
accessing it through another app. For example, you can test an OS X Share extension by opening a webpage
in Safari, clicking the Share toolbar button, and choosing your extension in the menu that appears.
NSExtensionAttributes
NSExtensionPrincipalClass
The name of the principal view controller class created by the template, such as SharingViewController.
When a host app invokes your extension, the extension point instantiates this class.
The default storyboard file for the extension, usually named MainInterface.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
16
In addition to the property list settings, a template may set some capabilities by default. Each extension point
can define capabilities that make sense for the type of task the extension point supports. For example, an iOS
Document Provider extension includes the com.apple.security.application-groups entitlement.
All templates for OS X app extensions include the App Sandbox and
com.apple.security.files.user-selected.read-only entitlements by default. You might need to
define additional capabilities for your extension if it needs to do things like use the network or access the users
photos or contact information.
Note: In general, when users give a containing app access to their private data, all extensions in
the containing app also receive access.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
17
Of particular interest is the context objects inputItems property, which can contain the items your app
extension needs to use. The inputItems property contains an array of NSExtensionItem objects, each of
which contains an item the extension can work on. To get the items from the context object, you might use
code like this:
NSArray *inputItems = myExtensionContext.inputItems;
Each NSExtensionItem object contains a number of properties that describe aspects of the item, such as its
title, content text, attachments, and user info.
Note that the attachments property contains an array of media data thats associated with the item. For
example, in an item associated with a sharing request, the attachments property might contain a
representation of the webpage a user wants to share.
After users work with the input items (if doing so is part of using the app extension), an app extension typically
gives users a choice between completing or canceling the task. Depending on the users choice, you call either
the completeRequestReturningItems:completionHandler: method, optionally returning
NSExtensionItem objects to the host app, or the cancelRequestWithError: method, returning an error
code.
Important: If your app extension calls the completeRequestReturningItems:completionHandler:
method, provide a completionHandler block to, at minimum, suspend your app extension should the
system ask you to. For details, read the documentation for the completionHandler block of this method,
in NSExtensionContext Class Reference .
In iOS, your app extension might need a bit more time to complete a potentially lengthy task, such as uploading
content to a website. When this is the case, you can use the NSURLSession class to initiate a transfer in the
background. Because a background transfer uses a separate process, the transfer can continue, as a low priority
task, after your extension completes the host apps request and gets terminated. To learn more about using
NSURLSession in your extension, see Performing Uploads and Downloads (page 29).
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
18
Important: Although you can set up a background URL upload or download task, other types of background
tasks, such as supporting VoIP or playing background audio, are not available to extensions.
If you include the UIBackgroundModes key in your app extensions Info.plist file, the extension will
be rejected by the App Store. (To learn more about this key, see UIBackgroundModes in Information Property
List Key Reference .)
Design a Streamlined UI
Most extension points require you to supply at least some custom UI that users see when they open your app
extension. An extensions UI should be simple, restrained, and focused on facilitating a single task. To improve
performance and the users experience, avoid including extraneous UI that doesnt support your extensions
main task.
Most Xcode app extension templates provide a placeholder UI that you can use to get started.
Users identify your app extension by its icon and its name. An extensions icon must be the same as the app
icon of its containing app. Using the containing apps icon helps a user be confident that an extension is in
fact provided by the app they installed.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
19
In iOS, a custom Action extension uses a template image version of its containing apps icon, which you must
provide.
iOS Share extensions automatically employ the containing apps icon. If you provide a separate icon in your
Share extension target, Xcode ignores it. For all other app extension types, you must provide an icon that
matches the containing apps icon.
For information on how to add an icon to your app extension, see Creating an Asset Catalog and Adding an
App Icon Set or Launch Image Set. For more about icon requirements for iOS app extensions, see App
Extensions in iOS Human Interface Guidelines
An app extension needs a short, recognizable name that includes the name of the containing app, using the
pattern <Containing app name><App extension name> . This makes it easier for users to manage extensions
throughout the system. You can, optionally, use the containing apps name as-is for your extension, in the
common case that your containing app provides exactly one extension.
The displayed name of your app extension is provided by the extension targets CFBundleDisplayName
value, which you can edit in the extensions Info.plist file. If you dont provide a value for the
CFBundleDisplayName key, your extension uses the name of its containing app, as it appears in the
CFBundleName value.
Make sure you localize the app extensions name when you provide a localized app extension.
Some app extensions also need short descriptions. For example, an OS X widget displays a description to help
users choose the widgets they want to see in the Today view. To provide this text, edit the value of the
widget.description key in your widgets InfoPlist.strings file.
In the Xcode project navigator for your keyboard project, select the project file.
If the project & targets list in the project editor is hidden, show it. To do this, click the button at the left of
the project editor tab bar.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
20
2.
In the targets group in the project & targets list, select the target for your app extension.
3.
4.
In the Deployment group in the project editor, view the Targeted Device Family setting. For both the
Debug and Release configuration, the value should be iPhone/iPad.
If you find different values, correct them to be iPhone/iPad.
Employ Auto Layout and size classes when designing and building your app extension. Test your app extension
to ensure it behaves as you expect it to for all device sizes and orientations. Do this in iOS Simulator, as described
in iOS Simulator User Guide , and, if possible, also test on physical devices in both orientations.
Remember that even if your containing app targets only the iPad device family, your contained app extension
can appear in the context of an iPhone app running in compatibility mode.
Important: To pass App Review, you must specify iPhone/iPad (sometimes called universal ) as the
targeted device family for your app extension, no matter which targeted device family you choose for your
containing app.
In a future iOS update, an app extension will run only on devices (or in device compatibility modes) natively
supported by the extensions containing app. For example, an extension provided in an iPad-only containing
app will not be visible when using an iPhone app in compatibility mode. To ensure the best user experience
possible we recommend your containing app and its app extensions are universal.
Using Xcode to debug an app extension is a lot like using Xcode to debug any other process, but with one
important difference: In your extension schemes Run phase, you specify a host app as the executable. Upon
accessing the extension through that specified hosts UI, the Xcode debugger attaches to the extension.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
21
The scheme in an Xcode app extension template uses the Ask On Launch option for the executable. With this
option, each time you build and run your project youre prompted to pick a host app. If you want to instead
specify a particular host to use every time, open the scheme editor and use the Info tab for the app extension
schemes Run phase.
The steps for attaching the Xcode debugger to your app extension are:
1.
Enable the app extensions scheme by choosing Product > Scheme > MyExtensionName or by clicking
the scheme pop-up menu in the Xcode toolbar and choosing MyExtensionName.
2.
Click the Build and Run button to tell Xcode to launch your specified host app.
The Debug navigator indicates it is waiting for you to invoke the app extension.
3.
Note: Before you build and run your app extension project, ensure the extensions scheme is selected.
If you instead build and run using the containing app scheme, Xcode does not attach to your app
extension unless you invoke it from the containing app, which is an unusual scenario and might not
be what you want.
If you access your app extension from a host app different from the one specified in the scheme,
the Xcode debugger does not attach to the extension.
In OS X, you need to perform the user step of enabling an app extension before you can access it from a host
app for testing and debugging. You enable most extension types by using the Extensions pane of System
Preferences. You can also open the Extensions pane by choosing More in the Share or Action menu.
For an OS X Today widget, use the Widget Simulator to test and debug it. (There is no separate step for you
to perform in System Preferences to enable the widget.)
For a custom keyboard in iOS, use Settings to enable the app extension (Settings > General > Keyboard >
Keyboards).
Xcode registers a built app extension for the duration of the debugging session on OS X. This means that if
you want to install the development version of your extension on OS X you need to use the Finder to copy it
from the build location to a location such as the Applications folder.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
22
Note: In the Xcode debug console logs, an app extensions binary might be associated with the
value of the CFBundleIdentifier property, instead of the value of the CFBundleDisplayName
property.
Because app extensions must be responsive and efficient, it's a good idea to watch the debug gauges in the
debug navigator while you're running your extension. The debug gauges show how your extension uses the
CPU, memory, and other system resources while it runs. If you see evidence of performance problems, such
as an unusual spike in CPU usage, you can use Instruments to profile your extension and identify areas for
improvement. You can open Instruments while youre in a debugging session by clicking Profile in Instruments
in any debug gauge report (to view a debug gauge report, click the gauge in the debug area). To learn more
about the debug gauges, see Debug Your App in Xcode Overview ; to learn how to use Instruments, see
Instruments User Guide .
Note: Choosing Product > Profile in Xcode builds and runs an app extension in Instruments directly.
Instruments uses the executable set in the Profile section of the scheme as the host for the extension.
To test an app extension using the Xcode testing framework (that is, the XCTest APIs), write tests that exercise
the extension code using your containing app as the host environment. To learn more about testing, see
Testing with Xcode .
To pass app review, your containing app must provide functionality to users; it cant just contain app extensions.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
23
As you write custom code that performs your app extensions task, you may need to handle some scenarios
that are common to many types of extensions. Use the code and recommendations in this chapter to help you
implement your solutions.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
24
For more on creating and using embedded frameworks, watch the WWDC 2014 video Building Modern
Frameworks, available at https://developer.apple.com/videos/wwdc/2014.
// Use the shared user defaults object to update the user's account.
[mySharedDefaults setObject:theAccountName forKey:@"lastAccountName"];
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
25
Figure 4-1shows how an extension and its containing app can use a shared container to share data.
Figure 4-1
Important: You must set up a shared container if your app extension uses the NSURLSession class to
perform a background upload or download, so that both the extension and its containing app can access
the transferred data. To learn how to perform an upload or download in the background, see Performing
Uploads and Downloads (page 29).
When you set up a shared container, the containing appand each contained app extension that you allow
to participate in data sharinghave read and write access to the shared container. To avoid data corruption,
you must synchronize data accesses. Use Core Data, SQLite, or Posix locks to help coordinate data access in a
shared container.
Accessing a Webpage
In Share extensions (on both platforms) and Action extensions (iOS only), you can give users access to web
content by asking Safari to run a JavaScript file and return the results to the extension. You can also use the
JavaScript file to access a webpage before your extension runs (on both platforms), or to access or modify the
webpage after your extension completes its task (iOS only). For example, a Share extension can help users
share content from a webpage, or an Action extension in iOS might display a translation of the users current
webpage.
To add webpage access and manipulation to your app extension, perform the following steps:
Create a JavaScript file that includes a global object named ExtensionPreprocessingJS. Assign a new
instance of your custom JavaScript class to this object.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
26
In the NSExtensionActivationRule dictionary in your app extensions Info.plist file, give the
NSExtensionActivationSupportsWebPageWithMaxCount key a nonzero value. (To learn more about
the activation rule dictionary, see Declaring Supported Data Types for a Share or Action Extension (page
30).)
When your app extension starts, use the NSItemProvider class to get the results returned by the execution
of the JavaScript file.
In an iOS app extension, pass values to the JavaScript file if you want Safari to modify the webpage when
your extension completes its task. (You use the NSItemProvider class in this step, too.)
To tell Safari that your app extension includes a JavaScript file, add the
NSExtensionJavaScriptPreprocessingFile key to the NSExtensionAttributes dictionary. The value
of the key should be the file that you want Safari to load before your extension starts. For example:
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionJavaScriptPreprocessingFile</key>
<string>MyJavaScriptFile</string> <!-- Do not include the ".js" filename
extension -->
</dict>
On both platforms, your custom JavaScript class can define a run() function that Safari invokes as soon as it
loads the JavaScript file. In the run() function, Safari provides an argument named completionFunction,
with which you can pass results to your app extension in the form of a key-value object.
In iOS, you can also define a finalize() function that Safari invokes when your app extension calls
completeRequestReturningItems:completion: at the end of its task. A finalize() function can use
items your extension passes in completeRequestReturningItems:completion: to change the webpage
as desired.
For example, if your iOS app extension needs the base URI of a webpage when it starts and it changes the
background color of the webpage when it stops, you might write JavaScript code like that shown in Listing
4-1.
Listing 4-1
MyExtensionJavaScriptClass.prototype = {
run: function(arguments) {
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
27
On both platforms, you need to write code to handle the values that get passed back from your run() function.
To get the dictionary of results, specify the kUTTypePropertyList type identifier in the NSItemProvider
method loadItemForTypeIdentifier:options:completionHandler:. In the dictionary, use the
NSExtensionJavaScriptPreprocessingResultsKey key to get the result item. For example, to get the
base URI passed in the run() function in Listing 4-1 (page 27), you might use code like this:
[imageProvider loadItemForTypeIdentifier:kUTTypePropertyList options:nil
completionHandler:^(NSDictionary *item, NSError *error) {
NSDictionary *results = (NSDictionary *)item;
NSString *baseURI = [[results
objectForKey:NSExtensionJavaScriptPreprocessingResultsKey] objectForKey:@"baseURI"];
}];
To pass a value to the finalize() function when your iOS app extension finishes its task, use the
NSItemProvider initWithItem:typeIdentifier: method to pack the value in the dictionary for the
NSExtensionJavaScriptFinalizeArgumentKey key. For example, to specify red for the background color
used in the finalize() function in Listing 4-1 (page 27), your extension might use code like this:
NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init];
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
28
After your app extension initiates the upload or download task, the extension can complete the host apps
request and be terminated without affecting the outcome of the task. To learn more about how an extension
handles the request from a host app, see Respond to the Host Apps Request (page 17). In iOS, if your extension
isnt running when a background task completes, the system launches your containing app in the background
and calls the application:handleEventsForBackgroundURLSession:completionHandler: app
delegate method.
Important: If your app extension initiates a background NSURLSession task, you must also set up a shared
container that both the extension and its containing app can access. Use the sharedContainerIdentifier
property of the NSURLSessionConfiguration class to specify an identifier for the shared container so
that you can access it later.
Refer to Sharing Data with Your Containing App (page 25) for guidance on setting up a shared container.
Listing 4-2 shows one way to configure a URL session and use it to initiate a download.
Listing 4-2
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
29
[myTask resume];
- (NSURLSession *) configureMySession {
if (!mySession) {
NSURLSessionConfiguration* config = [NSURLSessionConfiguration
backgroundSessionConfigurationWithIdentifier:@com.mycompany.myapp.backgroundsession];
// To access the shared container you set up, use the sharedContainerIdentifier
property on your configuration object.
config.sharedContainerIdentifier = @com.mycompany.myappgroupidentifier;
mySession = [NSURLSession sessionWithConfiguration:config delegate:self
delegateQueue:nil];
}
return mySession;
}
Because only one process can use a background session at a time, you need to create a different background
session for the containing app and each of its app extensions. (Each background session should have a unique
identifier.) Its recommended that your containing app only use a background session that was created by one
of its extensions when the app is launched in the background to handle events for that extension. If you need
to perform other network-related tasks in your containing app, create different URL sessions for them.
If you need to complete the host apps request before you initiate a background URL session, make sure that
the code that creates and uses the session is efficient. After your app extension calls
completeRequestReturningItems:completionHandler: to tell the host app that its request is complete,
the system can terminate your extension at any time.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
30
For example, to declare that your Share extension can support up to ten images, one movie, and one webpage
URL, you might use the following dictionary for the value of the NSExtensionAttributes key:
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>10</integer>
<key>NSExtensionActivationSupportsMovieWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
</dict>
</dict>
If you dont support a particular data type, use 0 for the value of the corresponding key or remove the key
from your NSExtensionActivationRule dictionary.
Note: If your Share or iOS Action extension needs to access a webpage, you must include the
NSExtensionActivationSupportsWebPageWithMaxCount key with a nonzero value. (To learn
how to use JavaScript to access a webpage from your extension, see Accessing a Webpage (page
26).)
The keys in the NSExtensionActivationRule dictionary are sufficient to meet the filtering needs of typical
app extensions. If you need to do more complex or more specific filtering, such as distinguishing between
public.url and public.image, you can create a predicate statement. Then, use the bare string that
represents the predicate as the value of the NSExtensionActivationRule key. (At runtime, the system
compiles this string into an NSPredicate object.)
For example, an app extension item's attachments property can specify a PDF file like this:
{extensionItems = ({
attachments = ({
registeredTypeIdentifiers = (
"com.adobe.pdf",
"public.file-url"
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
31
);
});
})}
To specify that your app extension can handle exactly one PDF file, you might create a predicate string like
this:
SUBQUERY (
extensionItems,
$extensionItem,
SUBQUERY (
$extensionItem.attachments,
$attachment,
ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "com.adobe.pdf"
).@count == $extensionItem.attachments.@count
).@count == 1
This statement iterates over an array of NSExtensionItem objects, and secondarily over the attachments
array in each extension item. For each attachment, the predicate evaluates the uniform type identifier (UTI)
for each representation in the attachment. When an attachment representation UTI conforms to any of of two
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
32
different specified UTIs (which you see on the right-hand side of each UTI-CONFORMS-TO operator), collect
that UTI for the final comparison test. The final line returns TRUE if the app extension was given exactly one
extension item attachment with a supported UTI.
During development only, you can use the TRUEPREDICATE constant (which always evaluates to true) as a
stub predicate statement, to test your code path before you implement your predicate statement.
Important: Before you submit your containing app to the App Store, be sure to replace all uses of
TRUEPREDICATE stub predicates with functional predicate statements or with
NSExtensionActivationRule keys. If any app extensions in your containing app include the string
TRUEPREDICATE, the app will be rejected.
To learn more about the syntax of predicate statements, see Predicate Format String Syntax in Predicate
Programming Guide .
Important: If your containing app target links to an embedded framework, it must include the arm64
architecture or it will be rejected by the App Store.
To set up an app extension Xcode project to take advantage of conditional linking
1.
For each of your contained app extensions, set the deployment target to be iOS 8.0 or later, as usual.
Do this in the Deployment info section of the General tab in the Xcode target editor.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
33
2.
For your containing app, set the deployment target to be the oldest version of iOS that you want to support.
3.
In your containing app, conditionalize calls to the dlopen command within a runtime check for the iOS
version by using the systemVersion method.
Call the dlopen command only if your containing app is running in iOS 8.0 or later. Be sure to use
Objective-C, not Swift, when making this call.
Certain iOS APIs use embedded frameworks via the dlopen command. You must conditionalize your use of
these APIs just as you do when calling dlopen directly. These APIs are from the CFBundleRef opaque type:
CFBundleGetFunctionPointerForName
CFBundleGetFunctionPointersforNames
load
loadAndReturnError:
classNamed:
In a containing app you are deploying to versions of iOS older than 8.0, call these APIs only within a runtime
check that ensures you are running in iOS 8.0 or newer, and call these APIs using Objective-C.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
34
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
35
Today
App extensions in the Today view are called widgets. Widgets give users quick access to information thats
important right now. For example, users open the Today view to check current stock prices or weather conditions,
see todays schedule, or perform a quick task such as marking an item as done. Users tend to open the Today
view frequently, and they expect the information theyre interested in to be instantly available.
A Today widget can appear on the lock screen of an iOS device.
Before you begin: Make sure that the Today extension point is appropriate for the functionality
you want to provide. The best widgets give users quick updates or enable very simple tasks. If you
want to create an app extension that enables a multistep task or helps users perform a lengthy task,
such as uploading or downloading content, the Today extension point is not the right choice.
To learn about other types of app extensions you can create, see Table 1-1 (page 7).
Perform well (in particular, iOS widgets must use memory wisely or the system may terminate them)
Because user interaction with Today widgets is quick and limited, you should design a simple, streamlined UI
that highlights the information users are interested in. In general, its a good idea to limit the number of
interactive items in a widget. In particular, note that iOS widgets dont support keyboard entry.
Note: Avoid putting a scroll view inside a Today widget. Its difficult for users to scroll within a
widget without inadvertently scrolling the Today view.
Users configure Today widgets differently depending on the platform theyre using.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
36
Today
Use the Xcode Today Template
iOS. Because Today widgets dont allow keyboard entry, users need to be able to use the containing app to
configure a widgets content and behavior. In the Stocks widget, for example, users can switch between different
representations of a symbols value, but they must open the Stocks app to manage the list of symbols.
OS X. The containing app might not perform any functions, so the Today widget may need to give users ways
to configure it while its running. For example, the Stocks widget in OS X lets users find and add market symbols
they want to track. The Notification Center API in OS X includes methods you can use to let users configure
widgets.
After users install an app that contains a Today widget, they can add the widget to the Today view. When users
choose Edit in the Today view, Notification Center reveals a view that lets users add, reorder, and remove
widgets.
If you use a custom view controller subclass, use the custom class name to replace the TodayViewController
value for the NSExtensionPrincipalClass key.
iOS. If you dont want to use the storyboard file provided by the template, remove the
NSExtensionMainStoryboard key and add the NSExtensionPrincipalClass key, using the name of
your view controller for the value.
Most of the work you do to create a Today widget involves designing the UI and implementing a view controller
subclass that performs your custom functionality.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
37
Today
Design the UI
Design the UI
Important: For best results, use Auto Layout to design the view of a Today widget.
Because space in the Today view is limited and the expected user experience is quick and focused, you shouldnt
create a widget that's too big by default. On both platforms, a Today widget must fit within the width of the
Today view, but it can increase in height to display more content.
A Today widget created using the Xcode Today template includes Auto Layout constraints for standard margin
insets. To get the inset values for your calculations, implement the
widgetMarginInsetsForProposedMarginInsets: method. (The templates principal view controller
conforms to the NCWidgetProviding protocol, which provides this method.) Be sure to draw all your widget
content within these standard margin insets. To learn more about designing the appearance of your widget,
see Today Widgets in iOS Human Interface Guidelines .
If a widget has additional content to display, you can rely on Auto Layout constraints to adjust the widgets
height as appropriate. If you dont use Auto Layout, you can use the UIViewController property
preferredContentSize to request a height for the widget. For example:
- (void)receivedAdditionalContent {
self.preferredContentSize = [self sizeNeededToShowAdditionalContent];
}
Note: Dont specify a height for your widget that would require a user to scroll to see all its content.
iOS. If you want to animate the display of your content to coincide with the resize animation, implement
viewWillTransitionToSize:withTransitionCoordinator:, using
animateAlongsideTransition:completion: to add your animations to the coordinator parameter.
To ensure that your widget gets the vibrancy effect thats appropriate for displaying items in the Today view,
use notificationCenterVibrancyEffect.
OS X. Widgets inherit NSAppearanceNameVibrantDark from the view their view controller is placed in.
When you use standard controls, you automatically get the right appearance. If you use custom colors, be sure
to choose colors that look good in a vibrant dark view.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
38
Today
Updating Content
Updating Content
The Today extension point provides API for managing a widgets state and handling updates to its content
(you can read about this API in the Notification Center Framework Reference ). Although there are a few
platform-specific differences in the Today API, the functionality supported on both platforms is mostly the
same.
To help your widget look up to date, the system occasionally captures snapshots of your widgets view. When
the widget becomes visible again, the most recent snapshot is displayed until the system replaces it with a
live version of the view.
To update a widgets state before a snapshot is taken, be sure to conform to the NCWidgetProviding protocol.
When your widget receives the widgetPerformUpdateWithCompletionHandler: call, update your widgets
view with the most recent content and call the completion handler, using one of the following constants to
describe the result of the update:
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
39
Today
Opening the Containing App
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
40
Today
Testing a Today Widget
To learn about debugging app extensions in general, see Debug, Profile, and Test Your App Extension (page
21).
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
41
Share
Share extensions give users a convenient way to share content with other entities, such as social sharing
websites or upload services. For example, in an app that includes a Share button, users can choose a Share
extension that represents a social sharing website and then use it to post a comment or other content.
Before you begin: Make sure that the Share extension point is appropriate for your purpose. The
best Share extensions make it easy for users to share content with websites. If you want to create
an extension that lets users perform a different task with their content or that gives users updates
on information they care about, the Share extension point is not the right choice.
To find out about other types of app extensions you can create, see Table 1-1 (page 7).
Note: You may want to specify the types of content your Share extension can work with so that
users understand what they can share. You can learn more about specifying content types in Declaring
Supported Data Types for a Share or Action Extension (page 30).
Users get access to Share extensions in the system-provided UI. In iOS, users tap the Share button and choose
a Share extension from the sharing area of the activity view controller that appears. In OS X, users can reveal
the list of sharing services in a few different ways. For example:
Select some content, Control-click to reveal a contextual menu, and choose Share.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
42
Share
Use the Xcode Share Template
When users choose your Share extension, you display a view in which they compose their content and post
it. You can base your view on the system-provided compose view controller, or you can create a completely
custom compose view. The system-provided compose view controller builds in some support for common
tasks, such as previewing and validating standard items, synchronizing content and view animation, and
configuring a post.
When you create a target that uses the standard compose view UI, the principal view controller class inherits
from SLComposeServiceViewController and the default files include stubs for methods such as
didSelectPost and isContentValid.
By default, the Share template supplies the following Info.plist keys and values (shown here for an iOS
target):
<key>NSExtension</key>
<dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>
Depending on the functionality of your Share extension, you might need to add keys and values to the default
property list. For example, to provide a JavaScript file that accesses a webpage, add the
NSExtensionAttributes key and a dictionary that specifies the file. (To learn more about how to use
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
43
Share
Design the UI
JavaScript to access a webpage, see Accessing a Webpage (page 26).) You also add keys and values if you
want to specify the data types your extension works with (to learn more, see Declaring Supported Data Types
for a Share or Action Extension (page 30)).
A Share extension uses its principal view controllers extensionContext property to get the
NSExtensionContext object that contains the users initial text and any attachments for a post, such as links,
images, or videos. The extension context object also contains information about the status of the posting
operation. (To learn more about how an extension can interact with its context, see Respond to the Host Apps
Request (page 17).)
The default SLComposeServiceViewController object includes a text view that displays the users editable
text content. When a user chooses Post, a Share extension validates the text views content (in addition to
attachments, if any) and calls the completeRequestReturningItems:expirationHandler:completion:
method of NSExtensionContext, using code like the following:
NSExtensionItem *outputItem = [[NSExtensionItem alloc] init];
// Set the appropriate value in outputItem
NSArray *outputItems = @[outputItem];
[self.extensionContext completeRequestReturningItems:outputItems
expirationHandler:nil completion:nil];
Design the UI
Important: For best results, use Auto Layout to design a Share extension.
The sharing UI on both platforms is constrained in size. In particular, your Share extension cant increase in
width, and although it may increase in height, you dont want to force users to scroll too much.
If the system-supplied compose view meets your needs, you dont need to supply any custom UI.
In general, you dont want to overcomplicate a simple task, but you also want to give users the options they
expect. For example, you want users to be able to post a simple remark with very little effort, but you may also
want to help users preview an attachment, tag a post, or specify details, such as a privacy setting or an album
to use.
When you have additional content to display, you can rely on Auto Layout constraints to adjust the views
height as appropriate. If you dont use Auto Layout, you can use the UIViewController property
preferredContentSize to specify the views new height.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
44
Share
Posting Content
iOS. If you want to animate the display of your content to coincide with the resize animation, implement
viewWillTransitionToSize:withTransitionCoordinator:, using
animateAlongsideTransition:completion: to add your animations to the coordinator parameter.
Posting Content
The primary purpose of a Share extension is to help users post content. When a user chooses the Post or Send
button in your Share extension, a system-provided animation provides feedback that the action is being
processed. The system then calls the didSelectPost method of the SLComposeServiceViewController
class. Implement this method to:
Set up a background-mode URL session (using the NSURLSession class) that includes the content to post
Listing 6-1 (page 45) shows one way to implement the didSelectPost method.
Listing 6-1
- (void)didSelectPost {
// Perform the post operation.
// When the operation is complete (probably asynchronously), the Share extension
should notify the success or failure, as well as the items that were actually
shared.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
45
Share
Validating Input
[self.extensionContext completeRequestReturningItems:outputItems
expirationHandler:nil completion:nil];
// Or call [super didSelectPost] to use the superclass's default completion behavior.
}
If a user cancels a post, or if a post is canceled for some other reason, the system calls the Share extensions
didSelectCancel method when the feedback animation completes. Implement this method if it makes
sense to customize the extension contexts completion operation.
Validating Input
Share extensions should validate the users content before posting it. Its best when the compose view gives
users feedback about their content by enabling or disabling the Post button and, optionally, by displaying the
current character count.
If youre using the standard compose view controller (an instance of the SLComposeServiceViewController
class), check the validity of the users current content by implementing the isContentValid method. The
system calls isContentValid when the user changes the text in the standard compose view, so you can
display the current character count and enable the Post button when appropriate. Listing 6-2 (page 46) shows
an example implementation of the isContentValid method for a sharing service that requires posts to
contain fewer than 100 characters.
Listing 6-2
- (BOOL)isContentValid {
NSInteger messageLength = [[self.contentText
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length];
NSInteger charactersRemaining = 100 - messageLength;
self.charactersRemaining = @(charactersRemaining);
if (charactersRemaining >= 0) {
return YES;
}
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
46
Share
Previewing Content (iOS Only)
return NO;
}
If your Share extension needs to validate content in custom ways, do the validation in an implementation of
the validateContent method. Depending on the result, you can return the correct value in your
isContentValid method.
For example, if you need to shrink an asset before letting users upload it, you dont want to enable the Post
button until the shrinking is complete. To find out if the shrinking is done, call validateContent within your
isContentValid method and return the appropriate result.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
47
Share
Configuring a Post (iOS Only)
To display a custom configuration view controller, you typically define a block of type
SLComposeSheetConfigurationItemTapHandler (in which you create the view controller) and then call
pushConfigurationViewController: to display it. The standard compose view controller uses a
UINavigationController instance to display your configuration view controller, so users can tap the Back
button to return to the sharing UI. You can also call popConfigurationViewController to return to the
sharing UI in response to some other user action.
If appropriate, you can replace the list of configuration items with a custom view controller that displays custom
autocompletion suggestions while a user is entering text. You might want to do this if your sharing service
defines certain textual items that users are likely to enter.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
48
Action
An Action extension helps users view or transform content originating in a host app. For example, an Action
extension might help users edit an image in a document that theyre viewing in a text editor. Another type of
Action extension might let users view a selected item in a different way, such as viewing an image in a different
format or reading text in a different language.
The system offers an Action extension to users only when the extension declares it can work with the type of
content a user is currently using. For example, if an Action extension declares it works only with text, it isnt
made available when a user is viewing images.
To learn how to declare the types of content an Action extension can work with, read Declaring Supported
Data Types for a Share or Action Extension (page 30)).
Before you begin: Make sure the Action extension point is appropriate for your purpose. Action
extensions enable targeted, lightweight tasks that display or change content. If you want to help
users share content on a social website or give users updates on information they care about, the
Action extension point is not the right choice.
To find out about other types of app extensions you can create, see Table 1-1 (page 7).
Can be an editor, in which users can make changes to selected content, or a viewer, in which users can
view selected content in a new way
Can transmit to the host app the content changes that users make within the app extension
Can appear in a modal view that emerges from the host apps window or in a custom view that surrounds
the users selected items
Automatically receives the users selected content as part of the extension context
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
49
Action
Use the Xcode Action Extension Template
On both platforms, users get access to Action extensions in the system-provided UI. In iOS, an Action extension
is listed in the action area of the activity view controller that appears when users tap the Share button. In OS X,
there are a few ways in which users can reveal a list of Action extensions. For example, users can:
Move the pointer over some selected content and click the button that appears
Note: In OS X, an Action extension that enables content viewing may work on multiple selected
items, but an extension that enables editing can work on only one item at a time.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
50
Action
Design the UI
To specify the task your OS X Action extension enables, use one of the following values for the required
NSExtensionServiceRoleType key:
Design the UI
Important: For best results, use Auto Layout to design the UI of an Action extension.
OS X. Use the size and position of the selected content in the host app to inform the size and position of the
Action extensions view.
Use the preferredContentSize property of NSViewController to specify the extension views preferred
size, based on the size of the selected content. (You can also specify minimum and maximum sizes for the
extensions view, to ensure that a host app doesnt make unreasonable adjustments to your view.) To specify
a preferred position for the extension view, set the preferredScreenOrigin property to the lower-left
corner of the extensions view.
iOS. Create a template image that represents your Action extension. A template image is an image that iOS
uses as a mask to create the final icon that users see in the activity view controller. To create a template image
that looks good in the final UI, follow these guidelines:
Use antialiasing.
For iPhone, the image should look good centered in an area that measures 60 x 60 points.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
51
Action
Returning Edited Content to the Host
For iPad, the image should look good centered in an area that measures 76 x 76 points.
If you want to present your iOS Action extension in full screen, add the following key-value pair to the extensions
NSExtension dictionary:
<key>NSExtensionActionWantsFullScreenPresentation</key>
<true/>
- (IBAction)done:(id)sender {
NSExtensionItem *outputItem = [[NSExtensionItem alloc] init];
outputItem.attributedContentText = self.myTextView.attributedString;
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
52
Photo Editing
In iOS, a Photo Editing extension lets users edit a photo or video within the Photos app. After users confirm
the changes they make in a Photo Editing extension, the adjusted content is available in Photos. Photos always
keeps the original version of the content, too, so that users can revert the changes they make in an extension.
Before you begin: Make sure that the Photo Editing extension point is appropriate for the
functionality you want to provide. A Photo Editing extension should make it easy for users to make
quick, targeted adjustments to a photo or video without requiring too much user interaction. If you
want to enable a more generic task or help users share photos and videos with others, the Photo
Editing extension point is not the right choice.
To learn about other types of app extensions you can create, see Table 1-1 (page 7).
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
53
Photo Editing
Use the Xcode Photo Editing Template
Important: Photos does not store a current version of video assets. If your extension cant read a video
assets adjustment data, it must work with the original version of the video, overwriting past edits.
When a user finishes using a Photo Editing extension, the extension returns the edited asset and the adjustment
data.
iOS users get access to Photo Editing extensions in the Photos app. When a user chooses your extension,
display a view that provides a custom editing interface. To present this view, use a view controller that adopts
the PHContentEditingController protocol.
Important: Embed no more than one Photo Editing extension for each media type (photo, video) in a
containing app. The Photos app displays to the user, at most, one extension of each type from a given
containing app.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
54
Photo Editing
Design the UI
In particular, make sure that the PHSupportedMediaTypes array specifies the types of media assets your app
extension can edit. The default value is Image, but you can also use Video.
Design the UI
Important: For best results, use Auto Layout to design the view of a Photo Editing extension.
The Photos app displays a Photo Editing extension within a navigation bar, so you should avoid designing a
navigation barbased UI for your extension.
Photos automatically displays your extensions view so that it occupies the full height of the screen, including
the area behind the navigation bar. If you want your content view to appear below the bar, and not underneath
it, be sure to use the views top layout guide appropriately.
Its best when a Photo Editing extension lets users preview the results of their edits. Giving users a preview
while theyre still using your extension means that they can get the effect they want without repeatedly exiting
and reentering the extension.
Because users are likely to spend time editing a photo or movie in your app extension, you dont want them
to lose their work if they accidentally choose Cancel. To improve the user experience, be sure to implement
the shouldShowCancelConfirmation method in your view controller, returning YES if there are unsaved
changes. When this method returns YES, Photos displays confirmation UI so that users can confirm if they
really want to cancel their changes.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
55
Photo Editing
Testing a Photo Editing Extension
To learn about debugging app extensions in general, see Debug, Profile, and Test Your App Extension (page
21).
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
56
Finder Sync
In OS X, the Finder Sync extension point lets you cleanly and safely modify the Finders user interface to express
file synchronization status and control. Unlike most extension points, Finder Sync does not add features to a
host app. Instead, it lets you modify the behavior of the Finder itself.
Receive notifications when the user starts or stops browsing the content of a monitored folder.
For example, the extension receives notification when the user opens a monitored folder in the Finder or
in an Open or Save dialog.
Add, remove, and update badges and labels on items in a monitored folder.
Display a contextual menu when the user Control-clicks an item inside a monitored folder.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
57
Finder Sync
Creating a Finder Sync Extension in Xcode
Before You Begin: Make sure the Finder Sync extension point is appropriate for the functionality
you plan to provide. The best Finder Sync extensions support apps that sync the contents of a local
folder with a remote data source. Finder Sync is not intended as a general tool for modifying the
Finders user interface.
To learn about other types of app extensions you can create, see Table 1-1 (page 7).
In particular, the NSExtensionPrincipalClass key must provide the name of your FIFinderSync subclass.
The system automatically instantiates this class when the Finder first launches. It instantiates an additional
copy whenever an Open or Save dialog is displayed. Each copy runs in its own process.
The Finder Sync Extension Xcode template configures these Info.plist keys automatically. If you want to
change the principal class, modify the value of the NSExtensionPrincipalClass key.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
58
Finder Sync
Creating a Finder Sync Extension in Xcode
For more information about app groups, see Adding an App to an App Group in Entitlement Key Reference .
Next, instantiate a new NSUserDefaults object by calling initWithSuiteName: and passing in the shared
groups identifier. This init method creates a user default object that loads and saves data to the shared
container.
// Set up the folder we are syncing.
NSUserDefaults *sharedDefaults =
[[NSUserDefaults alloc]
initWithSuiteName:@"com.example.domain.MyFirstFinderSyncExtension"];
if (self.myFolderURL == nil) {
self.myFolderURL = [NSURL
fileURLWithPath:[@"~/Documents/MyFirstFinderSyncExtension Documents"
stringByExpandingTildeInPath]];;
}
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
59
Finder Sync
Creating a Finder Sync Extension in Xcode
FIFinderSyncController.defaultController().setBadgeImage(uploadedImage,
label: NSLocalizedString("Uploaded", comment: "Upload complete label"),
forBadgeIdentifier: "UploadComplete")
You would typically do this in the sync controllers initialization method. You can set up as many badge images
as you need. The badge identifier string that you specify here allows you to later retrieve the image for applying
it to a monitored item, as described in A Typical Finder Sync Use Case (page 62).
beginObservingDirectoryAtURL:
The system calls this method when the user begins looking at the contents of a monitored folder or one
of its subfolders. It passes the URL of the currently open folder as an argument.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
60
Finder Sync
Creating a Finder Sync Extension in Xcode
The system calls beginObservingDirectoryAtURL: only once for each unique URL. As long as the
content remains visible in at least one Finder window, any additional Finder windows that open to the
same URL are ignored.
Note: The system creates additional instances of your extension for any Open and Save dialogs.
These extensions receive their own calls to beginObservingDirectoryAtURL:, even if the
folder is already open in a Finder window.
endObservingDirectoryAtURL:
The system calls this method when the user is no longer looking at the contents of the given URL. As with
beginObservingDirectoryAtURL:, the Open and Save dialogs are tracked separately from the Finder.
requestBadgeIdentifierForURL:
The system calls this method when a new item inside the monitored folder becomes visible to the user.
This method is called once for each file initially shown in the Finders view. The system continues to call
this method as each new file scrolls into view.
You typically implement this method to check the state of the item at the provided URL, and then call the
Finder Sync controllers setBadgeIdentifier:forURL: method to set the appropriate badge. You
might also want to track these URLs, in order to update their badges whenever their state changes.
FIMenuKindContextualMenuForItems
The user Control-clicked one or more items inside your monitored folder. Your extension should present
menu items that affect the selected items.
FIMenuKindContextualMenuForContainer
The user Control-clicked the Finder windows background while browsing the monitored folder. Your
extension should present menu items that affect the contents of the current folder.
FIMenuKindContextualMenuForSidebar
The user Control-clicked a sidebar item that represents the monitored folder or part of its contents. Your
extension should present menu items that effect the contents of the selected item.
FIMenuKindToolbarItemMenu
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
61
Finder Sync
A Typical Finder Sync Use Case
The user clicked on the toolbar button provided by the extension. Because the toolbar button is always
available, the user may or may not be browsing the monitored folder at this time. Your extension may
present menu items that represent global actions that should always be available to the user. It can also
present menu items that affect selected items inside your monitored folder, if any exist.
You can get additional information about the currently selected items using the Finder Sync controllers
targetedURL and selectedItemURLs methods. The targetedURL method returns the URL of the file or
folder that the user Control-clicked. The selectedItemURLs method returns an array containing the URLs
of all the currently selected items in the Finder window.
targetedURL and selectedItemURLs return valid values only inside the menuForMenuKind: method or
inside one of its menu actions. If the user is not browsing the monitored folder (for example, if the user clicked
the toolbar button while outside the monitored folder), both of these methods return nil.
When the user clicks the toolbar button, the system calls your primary classs menuForMenuKind: method,
passing FIMenuKindToolbarItemMenu as the menu kind. Your extension must return an appropriate menu.
The system then displays this menu.
The system calls beginObservingDirectoryAtURL: when the user first opens the monitored folder
or one of its subfolders.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
62
Finder Sync
Performance Concerns
2.
The system calls requestBadgeIdentifierForURL: for each item that is currently being drawn onscreen.
Inside this method, do the following:
a.
Check the state of the item and set its badge by calling setBadgeIdentifier:forURL:.
Your app is responsible for defining the states and their corresponding badges. For example, a typical
syncing app might have badges that indicate unsynced local changes, syncing operations in progress,
successfully synced items, and items with syncing errors or conflicts.
b.
3.
The system calls endObservingDirectoryAtURL: when the user closes the folder. Delete all the URLs
for the badged items inside that folder and stop monitoring their state.
Performance Concerns
Finder Sync extensions may have a much longer lifespan than most other extensions. Because of this long
lifespan, you must take particular care to avoid any possible performance issues. Ideally, Finder Sync extensions
should spend most of their time running but idle. Limit the number of resources the extension consumes. Most
important, be sure to avoid leaking any resources. Over time, even a small trickle can grow into a serious
problem.
The system may also launch additional copies of your extension whenever an Open or Save dialog is displayed.
This means that the user may have multiple copies of your extension running at once. Therefore, its generally
best if the extension focuses on handling the badges, contextual menus, and toolbar buttons. Place in a separate
XPC service any code that performs the sync, updates state, or communicates with remote data sources. This
approach ensures that there is only one syncing service running at a time.
For more information about XPC services, see Creating XPC Servicesin Daemons and Services Programming
Guide .
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
63
Document Provider
In iOS, any app that remotely stores commonly used document formats should consider creating a Document
Provider extension. This extension (sometimes shortened here to document provider ) allows other apps to
access the documents managed by your app. Additionally, apps that are strongly associated with a specific
document type may benefit from creating a document provider for their documents. Here, the document
provider act as a local repository for a particular type of document, letting the user gather all those documents
into one place.
Documents managed by your Document Provider extension can be accessed by any app using a document
picker view controller. This combination gives users a lot of flexibility when it comes to managing and sharing
their documents.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
64
Document Provider
Document Picker View Controller Extension
To opt-in to iCloud Drive support, open the Xcode capabilities pane and turn on iCloud documents. Next, open
the apps info.plist file. You will need to add an entry for your iCloud containers, and then define how you
wish to share each container. A sample entry is shown below:
<key>NSUbiquitousContainers</key>
<dict>
<key>iCloud.com.example.MyApp</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
<key>NSUbiquitousContainerName</key>
<string>MyApp</string>
</dict>
</dict>
These settings let iCloud Drive provide access to the files stored in your apps iCloud container. For a detailed
description of the valid keys and values, See Cocoa Keys in Information Property List Key Reference chapter in
Information Property List Key Reference .
Important: When using iCloud Drive in iOS 8.0, do not use file presenters or the UIDocument class to
access files. You can use file coordinators, Posix locks, Core Data, or SQLite.
Before You Begin: Make sure the document provider extension point is appropriate for the
functionality you plan to provide. The best document providers act as a public document repository
for other apps. If you just want to share your apps files, a document provider is not the right choice.
To learn about other types of extensions you can create, see Table 1-1 (page 7).
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
65
Document Provider
Document Picker View Controller Extension
class. Your subclass is instantiated when the user selects your document provider from a
UIDocumentMenuViewController object or when the host app opens your document provider directly
using a UIDocumentPickerViewController object.
In either case, the host app presents a document picker view controller. The system then imbeds your Document
Picker View Controller extension inside the apps view controller. The apps view controller provides a navigation
bar with the document providers name, a location switcher, and a Done button. Your extension must provide
the rest of the user interface. Figure 10-1 (page 66) shows the relative position of these UI elements.
Figure 10-1
Life Cycle
1.
2.
3.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
66
Document Provider
Document Picker View Controller Extension
4.
The prepareForPresentationInMode: method is called on your extension. The extension must present
an appropriate user interface for the given mode. The open and import modes often require a different
set of controls than the move or export modes. Your extension should therefore check the mode and
present the correct user interface.
The UIDocumentPickerExtensionViewController object acts as the root view controller for your
user interface; therefore, it is often convenient to make it a container controller. You can then create a
separate child view controllers for each mode, and your extension simply presents the appropriate child
view controller in your prepareForPresentationInMode: method.
5.
6.
When the user makes their selection, your extension calls its dismissGrantingAccessToURL: method.
This method dismisses the user interface and passes the provided URL back to the host app, calling its
-[UIDocumentPickerViewControllerDelegate documentPicker:didPickDocumentAtURL:]
method.
7.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
67
Document Provider
Document Picker View Controller Extension
<string>UIDocumentPickerModeOpen</string>
<string>UIDocumentPickerModeExportToService</string>
<string>UIDocumentPickerModeMoveToService</string>
</array>
<key>UIDocumentPickerSupportedFileTypes</key>
<array>
<string>public.content</string>
</array>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.fileprovider-ui</string>
</dict>
These entries are automatically set by the Document Picker Extension template. Edit them only if you plan to
change your extensions default behavior. In particular, the UIDocumentPickerModes array contains entries
for all the modes that your document picker supports. If you dont plan to implementing a File Provider
extension, remove the entries for UIDocumentPickerModeOpen and
UIDocumentPickerModeMoveToService.
Similarly, if you want to create a secure drop boxwhere users can export files to your extension but cant
open, browse, or otherwise view themremove all the modes except
UIDocumentPickerModeExportToService.
The UIDocumentPickerSupportedFileTypes array contains a list of uniform type identifiers that your
extension supports. Your extension appears as an option only when the type of files being transferred match
at least one of the UTIs listed in this array. By default, the public.content UTI matches all document types.
The NSExtensionMainStoryboard entry holds the name of your extensions storyboard. The storyboards
initial view controller must be your UIDocumentPickerExtensionViewController subclass. You can edit
this key to change how your extension instantiates its view controller and how it creates its view hierarchy.
For example, if you replace the NSExtensionMainStoryboard entry with an NSExtensionPrincipalClass
key whose value is the name of your UIDocumentPickerExtensionViewController subclass, the extension
then loads the view controller directly. This approach allows you to load your user interface from a .xib file,
or to create your user interface programmatically.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
68
Document Provider
Document Picker View Controller Extension
Subclassing UIDocumentPickerExtensionViewController
The Document Picker Extension template provides a simple UIDocumentPickerExtensionViewController
subclass. Modify this subclass to manage your user interface and respond to user interactions, just as you would
for most view controllers. Still, there are a couple of UIDocumentPickerExtensionViewController specific
methods and properties worth noting.
prepareForPresentationInMode:
If necessary, override this method to set up any required resources. In particular, if your app uses different
view hierarchies for the different modes, set up the correct view hierarchy in your implementation.
dismissGrantingAccessToURL:
Call this method to dismiss the document picker view controller and grant access to the provided URL.
Each mode has its own requirements for the URL. For the complete details, see Dismissing the User
Interface (page 70).
documentPickerMode
This read-only property returns the document pickers mode. It is valid only after the system calls your
prepareForPresentationInMode: method.
originalURL
This read-only property contains the original files URL when in export or move mode. Otherwise it contains
nil.
validTypes
This read-only property contains the array of valid UTIs when in import or open mode. Otherwise it contains
nil.
providerIdentifier
This read-only property contains the value returned by your File Provider extensions providerIdentifier
method. If you do not provide a File Provider extension, it returns nil.
documentStorageURL
This read-only property contains the value returned by your File Provider extensions documentStorageURL
method. If you do not provide a File Provider extension, it returns nil.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
69
Document Provider
Document Picker View Controller Extension
useful metadata about the files, including size, creation date, and whether its local or remote. You may even
want to create and display thumbnail images of the file. For more information about working with metadata
and thumbnails, see NSURL Class Reference .
Your document picker can present a flat list of all the available files or it can display a complex hierarchy with
directories and subdirectories. It all depends on your apps needs. Regardless, after the user chooses a file,
dismiss the user interface and pass the URL back to the host app.
For export and move operations, let the user select the destination for their file. For simple extensions, you
might just provide a confirmation button. More complex extensions might let the user navigate through a
hierarchy of directories and even create his or her own subdirectories. After the user has chosen a destination,
dismiss the user interface.
You may also decide to handle logins, downloads, and similar tasks directly in the Document Picker View
Controller extension. There are only two places where you can directly interact with the user: in the containing
app and in the Document Picker View Controller extension. This means that all account management, error
notifications and progress updates must be handled by one of these two components.
If you want to handle these tasks in the containing app, you can use notifications and badges to notify the
user. The extensions can contact your server and request an appropriate push notification. This notification
then launches the containing app in the background, letting it run in response. The containing app can also
use local notifications and badges to alert the user to important events. However, the user must either select
one of the notifications or open the containing app to bring it to the foreground. Therefore, this approach
often requires active participation by the user, and users can miss, ignore, or even disable these notifications
and badges.
Often, you can provide a better user experience by handling these tasks in the Document Picker View Controller
extension directly. For more informations, see Providing a Great User Experience in an Uncertain World (page
75).
Import Document Picker mode. Provide a URL for the selected file. The URL must point to a local file that
the Document Picker View Controller extension can access. If the user selected a remote file, your extension
should download the file and save a local copy before calling dismissGrantingAccessToURL:.
Alternatively, if you are also providing a File Provider extension, you can just provide a URL that meets the
requirements for the open operation and let the file provider download it for you.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
70
Document Provider
File Provider Extension
Open Document Picker mode. Provide a URL for the selected file. If the file does not yet exist at this
location, your File Provider extension is called to create a placeholder or to produce the file as needed.
The URL must point to a location inside the directory hierarchy referred to by your documentStorageURL
property. This property simply calls your file providers documentStorageURL method and returns its
value.
Export Document Picker mode. Provide a URL for the selected destination. This URL needs to be accessible
only by the Document Picker View Controller extension. The system saves a copy of the document at this
URL and returns the URL to the host app to indicate success. The host app cannot access the document
at this URL.
Move Document Picker mode. Provide a URL for the selected destination. The URL needs to be contained
inside the hierarchy referred to by your documentStorageURL property. The system moves the document
to this URL and returns the URL to the host app. The host app can then access the document at the new
URL.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
71
Document Provider
File Provider Extension
These entries are automatically created by the Document Picker Extension template. Modify them only if you
want to change the extensions default settings.
The NSExtensionPrincipalClass key must hold the name of an NSFileProviderExtension subclass.
The system automatically instantiates this class whenever it needs to provide documents to the host app.
The NSExtensionFileProviderDocumentGroup entry must hold the identifier for a shared container that
can be accessed by both the Document Picker and the File Provider extension. This is used by the extensions
documentStorageURL method. If you change your shared containers identifier, be sure to update this setting
as well.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
72
Document Provider
File Provider Extension
<string>com.example.domain.MyFirstDocumentPickerExtension</string>
</array>
For more information about app groups, see Adding an App to an App Group in Entitlement Key Reference .
Subclass NSFileProviderExtension
The NSFileProviderExtension class provides a number of different methodssome that you must not
override, a few that you may override, and several that you must override. These methods are described below.
Do Not Overwrite These Class Methods
writePlaceholderAtURL:withMetadata:error:
Call this method whenever you need to create a placeholder. Use placeholderURLForURL: to generate
the correct URL based on the local files URL. The metadata that you provide depends largely on the needs
of your document pickers user interface, but the common options include file size, filename, and thumbnails.
placeholderURLForURL:
This method maps file URLs into their corresponding placeholder URLs. You typically call this method to
generate the placeholder URL before calling writePlaceholderAtURL:withMetadata:error:
providerIdentifier
An identifier unique to this NSFileProviderExtension subclass. By default, this method returns the
bundle identifier of the containing app.
At least four separate processes may be trying to access the files provided by this extension at any one
time; therefore, you must use file coordinators for all read and write operations.
Both the Document Picker View Controller extension and the File Provider extension should pass the
provider identifier to their file coordinators setPurposeIdentifier: method. Sharing the purpose
identifier lets the extensions coordinate with each other and prevents possible deadlocks between them.
documentStorageURL
The root URL for all provided documents. The URL must be writeable by this extension, and it should
probably be in a container shared by both the UIDocumentPickerExtensionViewController and
the containing app.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
73
Document Provider
File Provider Extension
By default, this method returns a subdirectory of the app group container directory shared by the
UIDocumentPickerExtensionViewController and NSFileProviderExtension extensions. Specify
the shared container by using the NSExtensionFileProviderDocumentGroup key in the File Provider
extensions info.plist file.
persistentIdentifierForItemAtURL:
Defines a static mapping between URLs and their persistent identifiers. By default, the identifier is simply
the path relative to URL returned by the documentStorageURL method. You can override the
persistentIdentifierForItemAtURL: method to provide a different mapping.
URLForItemWithPersistentIdentifier:
This is the inverse of persistentIdentifierForItemAtURL:. It provides a URL for the given identifier.
providePlaceholderAtURL:completionHandler:
The system calls this method when the file is accessed. Both the
providePlaceholderAtURL:completionHandler: and
startProvidingItemAtURL:completionHandler: methods may be triggered as the user interacts
with the document picker view controller or with a coordinated read.
Exactly which methods get triggered, and what their sequence is, depend on the intent of the access. For
example, a coordinated read using
theNSFileCoordinatorReadingImmediatelyAvailableMetadataOnly option just triggers the
creation of a placeholder. As a result, your extension should not create any dependencies between these
methods. They may be called any order.
startProvidingItemAtURL:completionHandler:
When this method is called, your extension should begin to download, create, or otherwise make a local
file ready for use. As soon as the file is available, call the provided completion handler. If any errors occur
during this process, pass the error to the completion handler. The system then passes the error back to
the original coordinated read.
itemChangedAtURL:
The system calls this method after the host app completes a coordinated write. You can override this
method to upload the changes back to your server or to otherwise respond to the change.
stopProvidingItemAtURL:
The system calls this method as soon as no processes are accessing the provided URL. You can override
this method to remove the document from the local file system, freeing up storage space.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
74
Document Provider
Providing a Great User Experience in an Uncertain World
Note: If your extension deletes files to free up space, it must replace them with placeholders by
calling placeholderURLForURL: and writePlaceholderAtURL:withMetadata:error:.
File Coordination
Important: When using iCloud Drive in iOS 8.0, do not use file presenters or the UIDocument class to
access files. You can use file coordinators, Posix locks, Core Data, or SQLite.
Because multiple processes can access these files, use file coordination to perform all read and write operations.
This applies to the containing app, the Document Picker View Controller extension, the File Provider extension,
and the host app.
For the document picker and file provider, you must also set the file coordinators purpose identifier. Both
extensions provide a providerIdentifier method. You pass this methods return value to the file
coordinators purposeIdentifier property before performing your coordinated read or write. By sharing a
purpose identifier, the two app extensions can coordinate with each other, preventing any possible deadlocks
between them.
File coordination plays a number of important roles. First and foremost, it guarantees that each process can
safely read and write to the provided files. However, because file coordination can also act as triggers for a
number of other actions, coordinated reads may cause the creation of placeholders or the downloading of
files from a remote server. Coordinated writes may likewise trigger an upload back to the remote server. Finally,
file coordinators permit the propagation of error messages across process boundaries.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
75
Document Provider
Providing a Great User Experience in an Uncertain World
Downloading Files
Even downloading small files can take an arbitrarily long amount of time. Things may work perfectly when
you test it on your offices Wi-Fi connectionbut anything might happen when a user opens your document
provider in the basement of a parking garage.
Because downloading is inherently unreliable, provide useful feedback wherever possible. For example, let
the user know which files are local and which are remote. Let them know when files are downloading, and
display the downloads progress. Finally, present meaningful error messages when problems occur.
Because you have access to the user interface only from the containing app and from the Document Picker
View Controller extension, you may want to provide a user interface to download files directly in the document
picker. Downloading files in the document picker has several advantages over downloading files in the file
provider.
You can display the files size, setting the users expectation for how long the download might take.
However, downloading data from the document picker presents its own set of problems. If the user dismisses
the document picker view controller, the system may terminate your app extension.
You can use an NSURLSession background transfer to download the files, but be sure to set the
NSURLSessionConfiguration objects sharedContainerIdentifier property to a container shared by
the app extension and the containing app. This ensures that the results can be accessed by either the extension
or the containing app, as necessary.
Background transfers perform the download in a separate process, and the download continues even if your
document picker is terminated. If your document picker is not running when the download completes, the
system launches your containing app in the background and calls its
application:handleEventsForBackgroundURLSession:completionHandler: method.
Your containing app can use local notifications and badges to alert the user. If the user reopens the document
picker before the download completes, you can reconnect with your background transfers, presenting the
current status to the user, and handling the downloads completion directly.
Even if you download documents from within the Document Picker View Controller extension, you still need
to support downloading from the file providers startProvidingItemAtURL:completionHandler:
method. The host app may not always access your documents through a document picker view controller.
Any time the user opens a file, the host app can save a security-scoped bookmark for that file. This bookmark
lets the host app open that file directly. However, if the file provider has already deleted the local copy to free
up storage space, it must now download a new copy.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
76
Document Provider
Providing a Great User Experience in an Uncertain World
When downloading files in the File Provider extension, you do not have access to the user interface. If an error
occurs, you cannot display a message directly. Instead, you pass an NSError object to the
startProvidingItemAtURL:completionHandler: method's completion handler. The system then passes
this error back to the host apps coordinated read.
There is also no way to report the downloads progress back to the user. The coordinated read that triggered
the download wont run until after the download is complete. If not handled properly, the download can lead
to unexpected delays for the user.
This means that you may want to carefully balance the desire to conserve storage space with the desire to
avoid unnecessary downloads. If your extension is overeager about replacing files with placeholders in the File
Provider extensions stopProvidingItemAtURL: method, your users may be surprised when an app suddenly
needs to redownload a file they were just working on. Also, if you know your user will want a given file, you
may want to preemptively download it to the shared container from your containing app. This makes the file
instantly available when the user asks for it.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
77
Document Provider
Providing a Great User Experience in an Uncertain World
For more information on saving data to the keychain, see iOS Keychain Services Tasks in Keychain Services
Programming Guide .
When a user logs out, delete their login credentials and remove any placeholders to documents that they can
no longer access. You may also want to remove all the local copies of files stored on the server. In general,
users shouldnt be able to access those files until they log back in.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
78
Custom Keyboard
A custom keyboard replaces the system keyboard for users who want capabilities such as a novel text input
method or the ability to enter text in a language not otherwise supported in iOS. The essential function of a
custom keyboard is simple: Respond to taps, gestures, or other input events and provide text, in the form of
an unattributed NSString object, at the text insertion point of the current text input object.
Before you begin: Make sure a custom, systemwide keyboard is indeed what you want to develop.
To provide a fully custom keyboard for just your app or to supplement the system keyboard with
custom keys in just your app, the iOS SDK provides other, better options. Read about custom input
views and input accessory views in Custom Views for Data Input in Text Programming Guide for iOS .
After a user chooses a custom keyboard, it becomes the keyboard for every app the user opens. For this reason,
a keyboard you create must, at minimum, provide certain base features. Most important, your keyboard must
allow the user to switch to another keyboard.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
79
Custom Keyboard
Understand User Expectations for Keyboards
iOS users also expect autocapitalization: In a standard text field, the first letter of a sentence in a case-sensitive
language is automatically capitalized.
These features and others are listed next.
Appropriate layout and features based on keyboard type trait
Autocorrection and suggestion
Automatic capitalization
Automatic period upon double space
Caps lock support
Keycap artwork
Multistage input for ideographic languages
You can decide whether or not to implement such features; there is no dedicated API for any of the features
just listed, so providing them is a competitive advantage.
UIKeyboardTypePhonePad
UIKeyboardTypeNamePhonePad
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
80
Custom Keyboard
Understand User Expectations for Keyboards
When a user taps in a phone pad object, the system temporarily replaces your keyboard with the appropriate,
standard system keyboard. When the user then taps in a different input object that requests a standard keyboard
via its type trait, your keyboard automatically resumes.
An app developer can elect to reject the use of all custom keyboards in their app. For example, the developer
of a banking app, or the developer of an app that must conform to the HIPAA privacy rule in the US, might do
this. Such an app employs the application:shouldAllowExtensionPointIdentifier: method from
the UIApplicationDelegate protocol (returning a value of NO), and thereby always uses the system keyboard.
Because a custom keyboard can draw only within the primary view of its UIInputViewController object,
it cannot select text. Text selection is under the control of the app that is using the keyboard. If that app
provides an editing menu interface (such as for Cut, Copy, and Paste), the keyboard has no access to it. A
custom keyboard cannot offer inline autocorrection controls near the insertion point.
Custom keyboards, like all app extensions in iOS 8.0, have no access to the device microphone, so dictation
input is not possible.
Finally, it is not possible to display key artwork above the top edge of a custom keyboards primary view, as
the system keyboard does on iPhone when you tap and hold a key in the top row.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
81
Custom Keyboard
API Quick Start for Custom Keyboards
The Custom Keyboard template (in the iOS Application Extension target template group) contains a subclass
of the UIInputViewController class, which serves as your keyboards primary view controller. The template
also includes a basic implementation of the required next keyboard button, which calls the
advanceToNextInputMode method of the UIInputViewController class. Add objects such as views,
controls, and gesture recognizers to the input view controllers primary view (in its inputView property), as
suggested in Figure 11-1. As with other app extensions, there is no window in the target, and, therefore, no
root view controller per se.
The templates Info.plist file comes preconfigured with the minimal values needed for a keyboard. See
the NSExtensionAttributes dictionary key in the keyboard targets Info.plist file. The keys for configuring
a keyboard are described in Configuring the Info.plist file for a Custom Keyboard (page 92).
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
82
Custom Keyboard
API Quick Start for Custom Keyboards
By default, a keyboard has no network access and cannot share a container with its containing app. To enable
these things, set the value of the RequestsOpenAccess Boolean key in the Info.plist file to YES. Doing
this expands the keyboards sandbox, as described in Establishing and Maintaining User Trust (page 85).
An input view controller conforms to various protocols for interacting with the content of a text input object:
To insert or delete text in response to touch events, employ the UIKeyInput protocol methods
insertText: and deleteBackward. Call these methods on the input view controllers
textDocumentProxy property, which represents the current text input object and which conforms to
the UITextDocumentProxy protocol. For example:
[self.textDocumentProxy insertText:@"hello "]; // Inserts the string "hello
" at the insertion point
[self.textDocumentProxy deleteBackward];
to the left of the insertion point
[self.textDocumentProxy insertText:@"\n"];
a newline character at the insertion point
To get the data you need to determine how much text is appropriate to delete when you call the
deleteBackward method, obtain the textual context near the insertion point from the
documentContextBeforeInput property of the textDocumentProxy property, as follows:
NSString *precedingContext =
self.textDocumentProxy.documentContextBeforeInput;
You can then delete the appropriate textfor example, a single character, or everything back to a
whitespace character. To delete by semantic unit, such as by word, sentence, or paragraph, employ the
functions described in CFStringTokenizer Reference and refer to related documentation. Note that each
language has its own tokenization rules.
To control the insertion point position, such as to support text deletion in a forward direction, call the
adjustTextPositionByCharacterOffset: method of the UITextDocumentProxy protocol. For
example, to delete forward by one character, use code similar to this:
- (void) deleteForward {
[self.textDocumentProxy adjustTextPositionByCharacterOffset: 1];
[self.textDocumentProxy deleteBackward];
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
83
Custom Keyboard
API Quick Start for Custom Keyboards
To respond to changes in the content of the active text object, or to respond to user-initiated changes in
the position of the insertion point, implement the methods of the UITextInputDelegate protocol.
To present a keyboard layout appropriate to the current text input object, respond to the objects
UIKeyboardType property. For each trait you support, change the contents of your primary view accordingly.
To support more than one language in your custom keyboard, you have two options:
Create one keyboard per language, each as a separate target that you add to a common containing app
Create a single multilingual keyboard, dynamically switching its primary language as appropriate
To dynamically switch the primary language, use the primaryLanguage property of the
UIInputViewController class.
Depending on the number of languages you want to support and the user experience you want to provide,
pick the option that makes the most sense.
Every custom keyboard (independent of the value of its RequestsOpenAccess key) has access to a basic
autocorrection lexicon through the UILexicon class. Make use of this class, along with a lexicon of your own
design, to provide suggestions and autocorrections as users are entering text. The UILexicon object contains
words from various sources, including:
Unpaired first names and last names from the users Address Book database
Text shortcuts defined in the Settings > General > Keyboard > Shortcuts list
You can adjust the height of your custom keyboards primary view using Auto Layout. By default, a custom
keyboard is sized to match the system keyboard, according to screen size and device orientation. A custom
keyboards width is always set by the system to equal the current screen width. To adjust a custom keyboards
height, change its primary view's height constraint.
The following code lines show how you might define and add such a constraint:
CGFloat _expandedHeight = 500;
NSLayoutConstraint *_heightConstraint =
[NSLayoutConstraint constraintWithItem:
attribute:
relatedBy:
toItem:
self.view
NSLayoutAttributeHeight
NSLayoutRelationEqual
nil
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
84
Custom Keyboard
Development Essentials for Custom Keyboards
attribute: NSLayoutAttributeNotAnAttribute
multiplier: 0.0
constant: _expandedHeight];
[self.view addConstraint: _heightConstraint];
Note: In iOS 8.0, you can adjust a custom keyboards height any time after its primary view initially
draws on screen.
Trust. Your custom keyboard gives you access to what a user types, so trust between you and your user
is essential.
A next keyboard key. The affordance that lets a user switch to another keyboard is part of a keyboards
user interface; you must provide one in your keyboard.
For keyboards, the following three areas are especially important for establishing and maintaining user trust:
Safety of keystroke data. Users want their keystrokes to go to the document or text field theyre typing into,
and not to be archived on a server or used for purposes that are not obvious to them.
Appropriate and minimized use of other user data. If your keyboard employs other user data, such as from
Location Services or the Address Book database, the burden is on you to explain and demonstrate the benefit
to your users.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
85
Custom Keyboard
Development Essentials for Custom Keyboards
Accuracy. Accuracy in converting input events to text is not a privacy issue per se but it impacts trust: With
every word typed, users see the accuracy of your code.
To design for trust, first consider whether to request open access. Although open access makes many things
possible for a custom keyboard, it also increases your responsibilities (see Table 11-1).
Table 11-1
Open
Privacy considerations
access
Off
(default)
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
86
Custom Keyboard
Development Essentials for Custom Keyboards
Open
Privacy considerations
access
On
If you build a keyboard without open access, the system ensures that keystrokes cannot be sent back to you
or anywhere else. Use a nonnetworked keyboard if your goal is to provide normal keyboard functionality.
Because of its restricted sandbox, a nonnetworked keyboard gives you a head start in meeting Apples data
privacy guidelines and in gaining user trust.
If you enable open access (as described Configuring the Info.plist file for a Custom Keyboard (page 92)), a
variety of possibilities open up but your responsibilities increase as well.
Note: To submit an open-access keyboard to the App Store, you must adhere to all pertinent
guidelines in the documents linked from Apples App Review Support page.
Each keyboard capability associated with open access carries responsibilities on your part as a developer, as
indicated in Table 11-2. In general, treat user data with the greatest possible respect and do not use it for any
purpose that is not obvious to the user.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
87
Custom Keyboard
Development Essentials for Custom Keyboards
Table 11-2
Capability
Developer responsibility
Shared container
with containing app
Sending keystroke
data to your server
Enhanced touch-event
processing and input prediction
via developers computing
resources
Dynamic autocorrect
lexicon based on
network supplied
data
Location Services
access
An open-access keyboard and its containing app can send keystroke data to your server, which enables you
to apply your computing resources to such features as touch-event processing and input prediction. If you
employ this capability, do not store received keystroke or voice data beyond the time needed to provide text
back to the user or to provide features that you explain to the user. Refer to Table 11-2 for additional
responsibilities you have when using open-access-keyboard capabilities.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
88
Custom Keyboard
Development Essentials for Custom Keyboards
Your custom keyboard must also provide a way to switch to another keyboard.
Note: To pass App Review, you must provide obvious UI in your custom keyboard to allow a user
to switch to another keyboard.
To ask the system to switch to another keyboard, call the advanceToNextInputMode method of the
UIInputViewController class. The system picks the appropriate next keyboard; there is no API to obtain
a list of enabled keyboards or for picking a particular keyboard to switch to.
The Xcode Custom Keyboard template includes the advanceToNextInputMode method as the action of its
Next Keyboard button. For best user experience, place your next-keyboard control close to the same screen
location as the system keyboards globe key.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
89
Custom Keyboard
Getting Started with Custom Keyboard Development
In Xcode, choose File > New > Project, and in the iOS Application template group choose the Single View
Application template.
2.
Click Next.
3.
4.
Navigate to the location you want to save the project in, then click Create.
At this point, you have an empty app for your project to serve the simple role, for now, of containing the
keyboard target. Before you submit a containing app to the App Store, it must perform some useful
function. See App Store Review Guidelines , linked from Apples App Review Support page.
5.
Choose File > New > Target, and in the iOS template group choose the Custom Keyboard template, then
click Next.
6.
Name the target as youd like the keyboards name to appear in the iOS user interface (for example, Custom
Keyboard).
7.
Ensure that the Project and the Embed in Application pop-up menus display the name of the containing
app, then click Finish.
If you are prompted to activate the scheme for the new keyboard target, click Activate.
You can now optionally customize the keyboard group name as it appears in the Purchased Keyboards list in
Settings, as described next.
To customize the keyboard group name
1.
In the Xcode project navigator, choose the containing apps Info.plist file, located in the apps
Supporting Files folder. The property list editor opens, showing the contents of the file.
2.
Hover the cursor over the Bundle name row, then click the + button that appears. This creates a new,
empty property list row and selects its Key field.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
90
Custom Keyboard
Getting Started with Custom Keyboard Development
3.
Start typing Bundle display name and when the name autocompletes, press Return.
4.
Double-click in the Value field in the same row to obtain a cursor there, then enter the keyboard group
name you want.
5.
Choose File > Save to save your changes to property list file.
Table 11-3summarizes the UI strings for your custom keyboard that you can configure in the Info.plist
files for the keyboard and its containing app.
Table 11-3
User interface strings specified in target and containing app Info.plist files
Info.plist key
Now you can run the template-based keyboard in iOS Simulator, or on a device, to explore its behavior and
capabilities.
To run the custom keyboard and attach the Xcode debugger
1.
2.
Use the Xcode toolbar to ensure that the active scheme popup menu specifies the keyboards scheme
and an iOS Simulator or attached device.
3.
Choose Product > Run, or click the Play button at the upper left of the Xcode project window.
Xcode prompts you to select a host app. Select an app with a readily-available text field, such as Contacts
or Safari.
4.
Click Run.
Xcode runs your specified host app. If this is your first time deploying your keyboard extension to iOS
Simulator or a device, use Settings to add and enable the keyboard, as follows:
a.
b.
c.
In the Purchased Keyboards group, tap the name of your new keyboard. A modal view appears with
a switch to enable your keyboard.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
91
Custom Keyboard
Getting Started with Custom Keyboard Development
5.
d.
e.
In the warning alert, tap Add Keyboard to finish enabling your new keyboard. Then tap Done.
6.
Dismiss your keyboard (so that in step 8 you can hit the viewDidLoad breakpoint by again invoking the
keyboard).
7.
In Xcode, choose Debug > Attach to Process > By Process Identifier (PID) or Name.
In the sheet that appears, enter the name of your keyboard extension (including spaces) as you specified
it when creating it. By default, this is the name for the app extensions group in the project navigator.
8.
Click Attach.
Xcode indicates in the Debug navigator that it is waiting to attach.
9.
In any app in iOS Simulator or the device (depending on which you are using), invoke the keyboard by
tapping in a text field.
As your keyboards main view begins to load, the Xcode debugger attaches to your keyboard and Xcode
hits your breakpoint.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
92
Custom Keyboard
Getting Started with Custom Keyboard Development
<key>NSExtensionAttributes</key>
<dict>
<key>IsASCIICapable</key>
<false/>
<key>PrefersRightToLeft</key>
<false/>
<key>PrimaryLanguage</key>
<string>en-US</string>
<key>RequestsOpenAccess</key>
<false/>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.keyboard-service</string>
<key>NSExtensionPrincipalClass</key>
<string>KeyboardViewController</string>
</dict>
Each of these keys is explained in the App Extension Keys in Information Property List Key Reference chapter
in Information Property List Key Reference . Use the keys in the NSExtensionAttributes dictionary to express
the characteristics and needs of your custom keyboard, as follows:
IsASCIICapableThis Boolean value, NO by default, expresses whether a custom keyboard can insert ASCII
strings into a document. Set this value to YES if you provide a keyboard type specifically for the
UIKeyboardTypeASCIICapable keyboard type trait.
PrefersRightToLeftThis Boolean value, also NO by default, expresses whether a custom keyboard is for
a right-to-left language. Set this value to YES if your keyboards primary language is right-to-left.
PrimaryLanguageThis string value, en-US (English for the US) by default, expresses the primary language
for your keyboard using the pattern <language>-<REGION>. You can find a list of strings corresponding to
languages and regions at http://www.opensource.apple.com/source/CF/CF-476.14/CFLocaleIdentifier.c.
RequestsOpenAccessThis Boolean value, NO by default, expresses whether a custom keyboard wants to
enlarge its sandbox beyond that needed for a basic keyboard. If you request open access by setting this keys
value to YES, your keyboard gains the following capabilities, each with a concomitant responsibility in terms
of user trust:
Access to Location Services, the Address Book database, and the Camera Roll, each requiring user permission
on first access
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
93
Custom Keyboard
Getting Started with Custom Keyboard Development
Option to use a shared container with the keyboards containing app, which enables features such as
providing a custom lexicon management UI in the containing app
Ability to send keystrokes, other input events, and data over the network for server-side processing
Ability to play audio, including keyboard clicks using the playInputClick method
Access to iCloud, which you can use, for example, to ensure that keyboard settings and your custom
autocorrect lexicon are up to date on all devices owned by the user
Access to Game Center and In-App Purchase, via the containing app
Ability to work with managed apps, if you design your keyboard to support mobile device management
(MDM)
When considering whether to set the RequestsOpenAccess keys value to YES, be sure to read Designing
for User Trust (page 85), which describes your responsibilities for respecting and protecting user data.
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
94
Date
Notes
2014-12-10
2014-10-20
2014-10-16
In the API Quick Start for Custom Keyboards (page 82) section, added a
code snippet for deleting forward and added a link to CFStringTokenizer
Reference .
Expanded the discussion in Specifying When a Widget Should
Appear (page 39).
2014-09-17
2014-12-10 | Copyright 2014 Apple Inc. All Rights Reserved. Apple Confidential Information.
95
Apple Inc.
Copyright 2014 Apple Inc.
All rights reserved.
No part of this publication may be reproduced,
stored in a retrieval system, or transmitted, in any
form or by any means, mechanical, electronic,
photocopying, recording, or otherwise, without
prior written permission of Apple Inc., with the
following exceptions: Any person is hereby
authorized to store documentation on a single
computer or device for personal use only and to
print copies of documentation for personal use
provided that the documentation contains
Apples copyright notice.
No licenses, express or implied, are granted with
respect to any of the technology described in this
document. Apple retains all intellectual property
rights associated with the technology described
in this document. This document is intended to
assist application developers to develop
applications only for Apple-branded products.
Apple Inc.
1 Infinite Loop
Cupertino, CA 95014
408-996-1010
Apple, the Apple logo, Cocoa, Cocoa Touch,
Finder, Instruments, iPad, iPhone, iPod, iPod
touch, Keychain, Mac, Objective-C, Safari, Sand,
Spotlight, and Xcode are trademarks of Apple
Inc., registered in the U.S. and other countries.
AirDrop and Retina are trademarks of Apple Inc.
iCloud is a service mark of Apple Inc., registered
in the U.S. and other countries.
App Store and Mac App Store are service marks
of Apple Inc.
IOS is a trademark or registered trademark of
Cisco in the U.S. and other countries and is used
under license.
Java is a registered trademark of Oracle and/or
its affiliates.
APPLE MAKES NO WARRANTY OR REPRESENTATION,
EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THIS
DOCUMENT, ITS QUALITY, ACCURACY,
MERCHANTABILITY, OR FITNESS FOR A PARTICULAR
PURPOSE. AS A RESULT, THIS DOCUMENT IS PROVIDED
AS IS, AND YOU, THE READER, ARE ASSUMING THE
ENTIRE RISK AS TO ITS QUALITY AND ACCURACY.
IN NO EVENT WILL APPLE BE LIABLE FOR DIRECT,
INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES RESULTING FROM ANY DEFECT, ERROR OR
INACCURACY IN THIS DOCUMENT, even if advised of
the possibility of such damages.
Some jurisdictions do not allow the exclusion of
implied warranties or liability, so the above exclusion
may not apply to you.