Mobile11 minute read

A Tutorial on iOS 8 App Extensions

iOS 8 introduced a new concept called App Extensions. This new feature did not break down the walls between the applications, but it opened a few doors providing gentle yet tangible contact between some apps. The latest update gave us an option to customize the iOS ecosystem, and we are eager to see this path opening up as well.


Toptalauthors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.

iOS 8 introduced a new concept called App Extensions. This new feature did not break down the walls between the applications, but it opened a few doors providing gentle yet tangible contact between some apps. The latest update gave us an option to customize the iOS ecosystem, and we are eager to see this path opening up as well.


Toptalauthors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.
Marija Efremova

Marija (BA Informatics and Software Engineering) has developed many iOS projects in the last 7+ years, including an augmented reality app.

Share

Few had tried before (take a look at this), but it was Apple with the first iPhone who defined how a Smartphone and a mobile OS should look. Apple made an incredible breakthrough in hardware and user experience. However, we often forget that they also set standards in how a mobile OS should work, and how a Smartphone applications should be made.

Building concrete walls between the applications, making them completely isolated and unaware of each other, was the best method to keep them secure and protect their data. All activities were closely monitored by iOS, and there was only a handful of actions an app could have done outside it’s scope.

“Abstinence is the best protection!” - but where is the fun in that?

It took them a while; too long if you ask me, but with iOS 8 Apple decided to have some fun. iOS 8 introduced a new concept called App Extensions. This new feature did not break down the walls between the applications, but it opened a few doors providing gentle yet tangible contact between some apps. The latest update gave iOS developers an option to customize the iOS ecosystem, and we are eager to see this path opening up as well.

ios 8 app extensions

What Are the iOS 8 App Extensions and How Do They Work?

In simple terms, iOS 8 App Extensions provides a new method of interacting with your application, without starting it or showing it on the screen.

As expected, Apple made sure they stay on top of everything, so there are just a handful of new entry points that your application can provide:

  • Today (also called a widget) - an extension displayed in the Today view of the Notification Center shows brief information and allows performance of quick tasks.
  • Share - an extension that enables your app to share content with users on social networks and other sharing services.
  • Action - an extension which allows creating custom action buttons in the Action sheet to let users view or transform content originating in a host app.
  • Photo Editing - an extension that lets users edit a photo or a video within the Photos app.
  • Document Provider - an extension used for allowing other apps to access the documents managed by your app.
  • Custom Keyboard - an extension that replaces the system keyboard.

App extensions are not stand alone apps. They are providing extended functionality of the app (which can be accessed from other apps, called the host apps) that is meant to be efficient and focused towards a single task. They have their own binary, own code signature, and own set of elements, but are delivered via the App Store as a part of the containing app’s binary. One (containing) app can have more than one extension. Once the user installs an app that has extensions, they will be available across iOS.

Let’s look at an example: A user finds a picture using Safari, hits the share button and chooses your application extension for sharing. Safari “talks” to the iOS Social framework, which loads and presents the extension. The extension’s code runs, passes data using the system’s instantiated communication channels, and once the task is done - Safari tears down the extension view. Soon after this, the system terminates the process, and your application was never displayed on the screen. Yet it completed a picture sharing function.

iOS, using inter process communication, is the one responsible for ensuring that the host app and an app extension can work together. The developers use high-level APIs provided by the extension point and the system, so they never have to worry about the underlying communication mechanisms.

Life Cycle

App extention life cycle

App Extensions have a different life cycle than the iOS apps. The host app kicks off the extension’s life cycle as a response to a user’s action. Then the system instantiates the app extension and sets up a communication channel between them. The extension’s view is displayed within the context of the host app using the items received in the host app’s request. Once the extension’s view is displayed, the user can interact with it. In response to the user’s action, the extension completes the host app’s request by immediately performing/canceling the task or, if necessary, initiating a background process to perform it. Right after that, the host app tears down the extension’s view and the user returns to their previous context within the host app. The results from performing this process could be returned to the host app once the process is completed. The extension usually gets terminated soon after it completes the request received from the host app (or starts a background process to perform it).

The system opens the extension of a user’s action from the host app, the extension displays the UI, performs some work, and returns data to the host app (if that’s appropriate to the extension’s type). The containing app isn’t even running while its extension is running.

Creating an App Extension - Hands-on Example Using the Today Extension

The Today extensions, also called widgets, are located in the Notification center’s Today view. They are a great way for presenting an up-to-date content for the user (such as showing weather conditions) or performing quick tasks (like marking the things done in a to-do list app’s widget). I have to point out here that the keyboard entry is not supported.

how app extensions work

Let’s create a Today extension that will display the most up-to-date information from our app (code on GitHub). In order to run this code, please make sure that you’ve (re)configured the App Group for the project (select your Development Team, keep in mind that App Group name has to be unique and follow Xcode’s instructions).

ios 8 app extension

today app extensions

app extension using today

Creating a New Widget

As we said before, the app extensions are not stand alone apps. We need a containing app on which we’ll build the app extension. Once we have our containing app, we choose to add a new target by navigating to File -> New -> Target into Xcode. From here we choose the template for our new target to add a Today Extension.

creating a widget template

In the next step we can choose our Product Name. That’s the name that will appear in the Notification Center’s Today view. There is an option for choosing the language between Swift and Objective-C in this step too. With finishing these steps, Xcode creates a Today template, which provides default header and implementation files for the principal class (named TodayViewController) with Info.plist file and an interface file (a storyboard or .xib file). The Info.plist file, by default, looks like this:

<key>NSExtension</key>
<dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widget-extension</string>
</dict>

If you don’t want to use the storyboard provided by the template, remove the NSExtensionMainStoryboard key and add the NSExtensionPrincipalClass key with the name of your view controller as a value.

A Today widget should:

  • ensure that the content always looks up to date
  • respond appropriately to user interactions
  • perform well (iOS widgets must use memory wisely or they’ll be terminated by the system)

Sharing Data and a Shared Container

The app extension and its containing app both have access to the shared data in their privately defined shared container - which is a way of an indirect communication between the containing app and the extension.

Don’t you just love how Apple makes these things so “simple”? :)

Sharing data trough NSUserDefaults is simple and a common use case. By default, the extension and its containing app use separate NSUserDefaults data sets, and cannot access each other’s containers. To change this behavior, iOS introduced App Groups. After enabling app groups on the containing app and the extension, instead of using [NSUserDefaults standardUserDefaults] use [[NSUserDefaults alloc] initWithSuiteName:@"group.yourAppGroupName"] for accessing the same shared container.

Updating the Widget

To ensure that the content is always up to date, the Today extension provides an API for managing a widget’s state and handling content updates. The system occasionally captures snapshots of the widget’s view, so when the widget becomes visible, the most recent snapshot is displayed until it is replaced with a live version of the view. A conformation to the NCWidgetProviding protocol is important for updating a widget’s state before a snapshot is taken. Once the widget receives the widgetPerformUpdateWithCompletionHandler: call, the widget’s view should be updated with the most recent content and the completion handler should be called with one of the following constants for describing the result of the update:

  • NCUpdateResultNewData - The new content requires redrawing of the view
  • NCUpdateResultNoDate - The widget doesn’t require updating
  • NCUpdateResultFailed - An error occurred during the update process
- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
// Perform any setup necessary in order to update the view.

// If an error is encountered, use NCUpdateResultFailed
// If there's no update required, use NCUpdateResultNoData
// If there's an update, use NCUpdateResultNewData

[self updateTableView];

completionHandler(NCUpdateResultNewData);
}

Controlling When the Widget Is Viewable

To control when a widget is displayed, use the setHasContent:forWidgetWithBundleIdentifier: method from NCWidgetController class. This method will let you specify the state of the widget’s content. It can be called from the widget or from its containing app (if it is active). You can pass a NO or a YES flag to this method, defining that widget content is ready or not. If the content is not ready, iOS will not display your widget when Today view is opened.

NCWidgetController *widgetController = [[NCWidgetController alloc] init];
[widgetController setHasContent:YES forWidgetWithBundleIdentifier:@"com.your-company.your-app.your-widget"];

Opening the Containing App from the Widget

The Today widget is the only extension which can request opening its containing app by calling the openURL:completionHandler: method. To ensure that the containing app opens in a way that makes sense in the context of the user’s current task, a custom URL scheme (that both of the widget and the containing app can use) should be defined.

[self.extensionContext openURL:[NSURL URLWithString:@"customURLsheme://URLpath"] completionHandler:nil];

UI Considerations

When designing your widget, take advantage of the UIVisualEffectView class, keeping in mind the views that should be blurred/vibrant must be added to the contentView and not to the UIVisualEffectView directly. Widgets (conforming to the NCWidgetProviding protocol) should load cached states in viewWillAppear: in order to match the state of the view from the last viewWillDisappear: and then transition smoothly to the new data when it arrives, which is not a case with a normal view controller (UI is setup in viewDidLoad and handles animations and loading data in viewWillAppear). Widgets should be designed for performing a task, or opening the containing app with a single tap. The keyboard entry is not available within a widget. This means that any UI requiring text entry should not be used.

Adding scrolls into a widget, both vertical and horizontal, is not possible. Or more precisely, adding a scroll view is possible but scrolling won’t work. Horizontal scrolling gesture in a scroll view in the Today extension will be intercepted by the notification center which will cause scrolling from Today to the Notification center. Scrolling vertically a scroll view inside a Today extension will be interrupted by scrolling the Today’s View.

Technical Notes

Here I’ll point out some important things to keep in mind when creating an App Extension.

Features Common to All Extensions

The following items are true for all extensions:

  • sharedApplication object is off limits: App extensions cannot access a sharedApplication object, or use any of the methods related to that object.

  • Camera and microphone are off limits: App extensions cannot access the camera or microphone on the device (but this is not a case for all the hardware elements). This is a result of the some APIs’ unavailability. To access some hardware elements in the app extension, you’ll have to check whether its API is available for app extensions or not (with the API availability checking described above).

  • Most background tasks are off limits: App extensions cannot perform long-running background tasks, except initiating uploads or downloads, which is discussed below.

  • AirDrop is off limits: App extensions cannot receive (but can send) data using AirDrop.

Uploading/Downloading in the Background

The one task that can be performed in the background is uploading/downloading, using the NSURLSession object.

After the upload/download task is initiated, the extension can complete the host app’s request and be terminated without any effect on the task’s outcome. If the extension isn’t running at the time the background task completes, the system launches the containing app in the background and the app’s delegate method application:handleEventsForBackgroundURLSession:completionHandler: is called.

The app which extension initiates a background NSURLSession task must have a shared container set up that both the containing app and its extension can access.

Make sure to create different background sessions for the containing app and each of its app extensions (each background session should have a unique identifier). This is important because only one process can use a background session at a time.

Action vs. Share

The differences between the Action and Share extensions are not completely clear from a coder’s perspective, because in practice they are very similar. Xcode’s template for the share extension target uses SLComposeServiceViewController, which provides a standard compose view UI you can use for social sharing, but it’s not required. A share extension can also inherit directly from UIViewController for a fully custom design, the same way an Action extension can inherit from SLComposeServiceViewController.

The differences between these two type of extensions is in how they are meant to be used. With the Action extension, you can build an extension with no UI of its own (for example, an extension used for translating the selected text and returning the translation to the host app). Share extension lets you share comments, photos, videos, audio, links, and more right from the host app. The UIActivityViewController drives both Action and Share extensions, where Share extensions are presented as color icons in the top row and the action extensions are presented as monochrome icons in the bottom row (Image 2.1).

Forbidden APIs

APIs marked in the header files with the NS_EXTENSION_UNAVAILABLE macro, or similar macro for unavailability, cannot be used (for example: HealthKit and EventKit UI frameworks in iOS 8 are not available for use in any app extension).

If you are sharing code between an app and an extension, you have to keep in mind that even referencing an API that is not allowed for the app extension will lead to rejection of your app from the App Store. You can choose to deal with this by re-factoring the shared classes into hierarchies, with a common parent and different sub-classes for different targets.Another way is to use the pre-processor by #ifdef checks. Because there is still not built-in target conditional, you have to create your own.

Another nice way to do this is by creating your own embedded framework. Just make sure that it won’t contain any APIs unavailable for extensions. To configure an app extension for using an embedded framework, navigate to the target’s build settings and set the “Require Only App-Extension-Safe API” setting to Yes. When configuring the Xcode project, in the Copy Files build phase, “Frameworks” must be chosen as the destination for the embedded framework. If you choose the “SharedFrameworks” destination, your submission will be rejected by App Store.

A Note on Backwards Compatibility

Although app extensions have only been available since iOS 8, you can make your containing app available to the prior iOS versions.

Apple Human Interface Compliance

Keep in mind Apple’s iOS Human Interface Guidelines when designing an app extension. You must ensure that your app extension is universal, no matter which device your containing app supports. To ensure that the app extension is universal, use the targeted device family build setting in Xcode specifying the “iPhone/iPad” value (sometimes called universal).

Conclusion

App extensions definitely have the most visible impact in iOS 8. Since 79% of devices are already using iOS 8 (as measured by the App Store on April 13, 2015), the app extensions are incredible features that apps should take advantage of. With combining the API’s restrictions and the way of sharing data between the extensions and their containing app, it seems that Apple managed to address one of the biggest complaints about the platform without compromising its security model. There is still no way for the third party apps to directly share their data with one another. Although this is a very new concept, it looks very promising.

Hire a Toptal expert on this topic.
Hire Now

About the author

Marija (BA Informatics and Software Engineering) has developed many iOS projects in the last 7+ years, including an augmented reality app.

World-class articles, delivered weekly.

Subscription implies consent to our privacy policy

World-class articles, delivered weekly.

Subscription implies consent to our privacy policy

Join the Toptal® community.