Creating Extensions for iOS and OS X, Part 1

Session 205 WWDC 2014

Extensions enable you to keep people engaged with your content across iOS and OS X. Learn how to create extensions that share content with social websites, engage users in Notification Center, and enable a variety of other exciting features. Tap into the potential of extensions and prepare to dive into advanced topics in Part 2.

[ Applause ]

Hello. I'm Ian Baird, iOS Frameworks Engineer, and today I'm here to tell you about creating extensions for iOS and OS X.

Going to lay out a quick agenda for today's talk.

First, we're going to talk about extensions and extension points.

Then I'm going to invite my colleague, Matt Gamble, from the Notification Center Team to the stage to tell you more and show you more about creating one of the hottest new tickets at WWDC14, Today Extensions.

And then, following on from that, we're going to invite our colleague, Guy Fullerton, onto the stage to tell you more about how to create Share Extensions like the one you saw in the keynote with Pinterest.

Moving along, what are extensions?

Well, extensions are almost like, it's a way for you to extend the system and other applications.

It's almost like more surface area for you to be able to apply bits and pieces of your app.

As you can see here, we have a screenshot of the Apple homepage on an iPad showing the new activity ViewController, and you'll notice a lot of familiar faces, a lot of familiar icons in that activity ViewController.

You can see messages, mail, and we have a new one, the Butterfly.

And Butterfly is a sharing extension.

You can see it's at home right there with its brethren right in the midst of the sharing extensions.

And we also support sharing extensions on OS X.

You can see we have the Butterfly sharing extension on Yosemite.

We support Notification Center Extensions also known as Today Extensions.

And as you can see, notification extensions are really good for giving you at-a-glance information; something where you just want to pull your phone out of your pocket, take a quick look, check some sports scores, maybe traffic conditions, the weather, and then put it back in your pocket.

It gives you at-a-glance, quick access to information you want.

And of course, we support this on Yosemite and you can see it looks gorgeous blended right over that new background.

It's just beautiful, stunningly beautiful.

The team did a great job.

Next, I want to show you the Custom Action Extensions.

You can see here we have our new Annotate Image Extension, which I'm going to show you how to build tomorrow, right in that row of actions.

Actions can be UI and non-UI actions, as you saw with the Bing Translate Extension.

And we support custom actions on OS X as well.

As a matter of fact, this is the infrastructure for the Markup Extension on OS X.

And you'll notice this is a very common theme.

We're sharing infrastructure.

We're making it easy for you to build things for both platforms.

Next, on iOS only we have the photo extensions.

Now, if there is a photo filter that you've been missing-I don't know, maybe?-or an annotation extension you would like to make for photos, you can build it now right into the photos UI.

This is really cool.

And we also support Document Providers.

Document Providers bring storage and things like that from the Cloud right into applications inside of iOS.

This is a first.

It really breaks down a lot of barriers that we've all been living with for, I believe, seven releases.

And next I want to tell you about Finder Extensions.

Finder Extensions are really cool.

In the past if you wanted to be able to badge or annotate items and folders inside of the finder, you may have to resort to sort of sketchy behavior like mock inject or some sort of hackery and now you don't have to do that.

Now, you actually have a supported way to badge items in the finder and we think this is really cool.

And then last, but not least, third-party keyboards.

We're supporting third-party keyboards in iOS now, using extensions.

So, I've told you about extensions and some of the extension points that we're offering in iOS and OS X, and next I'd like to tell you about delivering extensions.

Extensions are delivered as part of your app known as the Extension Container.

An Extension Container can contain many different types of extensions.

For example, it could contain a Today View Widget.

It could contain a sharing extension and it could also contain maybe multiple custom action extensions.

They're all bound up within your application's bundle and delivered via the App Store.

Extension Points-so I've talked a little bit about Extension Points earlier, but I'll tell you what they are.

Extension Points, again, are the new bits and pieces of surface area that we've exposed on the system for your extensions to bind to.

For example, a Today View Widget binds to the Notification Center via the Notification Center Extension or the Today View Extension point.

This means you're never going to see a Today View Extension in the activity ViewController, for example.

And one thing to remember about all extensions is that they're purpose built binaries.

See, these are not apps.

These are not even a piece of your app.

It's not a special mode that we launch your app in.

It's a special purpose binary.

It has its own code signature.

It has its own set of entitlements and it has its own container.

Damien's going to speak to this further in tomorrow's session.

So, as I said, they're not apps.

And they're always accessed via Apple Frameworks code.

You never really directly launch them.

Apple Frameworks code is in charge of discovering them for you and providing usually the user interaction by which they're launched, and I'll talk about that later.

And, as I was talking with a developer at lunch about this right before we came here, these are not facilitating app-to-app IPC.

You don't get, like, a pipe back to the containing app for your extension to be able to talk to it.

If you want to build workflows that incorporate app-to-app IPC you can still use things like UIApplication openURL.

But, what I would ask you to do is reconsider some of these workflows that you had before that involved sort of contextually switching from one application to another on a user's phone and reconsider casting this, maybe in terms of custom actions, which are really the spiritual successors to services.

So next, what are Extension Points?

They're very high level.

They mark extensible parts of the system.

They're always packaged in System Frameworks.

We don't support third-party extension points and they combine API and Policy, as you can imagine.

Again, with the Today View Widget.

The Today View Widget, it's very simple.

It's largely, as Matt's going to tell you.

a ViewController.

But, this ViewController also comes with its own policy-policy, which incorporates things like launch characteristics.

Now, an important thing to remember about extensions, most extensions on the system are bound one-to-one with their hosting app.

So, let me give you an example of this.

Let's say you write a sharing extension and the sharing extension is invoked in Safari.

An instance of your extension, a new process is going to be spun up to serve Safari.

Now, if the user homes out and goes over to mail and launches your extension again-let's say, maybe to share an attachment-a brand new process is going to be spun up for your extension.

Those processes are not going to share address spaces and this protects you from mistakes in your code.

If you're still using Objective-C and you have a wild pointer or something like that, you're not going to be able to bring down all of the instances of your extension that are running on the system.

It's really cool.

The extension point also governs the presentation of your extension.

An important thing to remember is that extensions are UI ViewControllers.

You're going to hear that multiple times today.

But, UI ViewControllers can be exposed to the user in multiple ways.

And usually since extensions live inside of the user interface of another application, it's up to that application and up to the system as to how it decides to present you.

You can either be contained or presented.

Notification Center Widgets, Today View Extensions are contained while sharing extensions are presented.

And when many extension points support or a few extension points support view controller and non-view controller variance of the extension point.

For instance, custom actions, as you'll see tomorrow, support a view controller variant, which allows the user to interact with the content before it's transformed and sent back to the host.

As you saw during the keynote, when Craig tapped the Bing translate extension, there was no UI and that was a non-view extension, and we'll tell you more about that later.

So, how are extensions invoked?

This is-again, going back to our example, we're going to show you what happens when the user decides to tap on the Butterfly sharing extension.

The user taps and the extension host, which is Safari at this time, goes and talks to the Notification Center Framework.

The notification-sorry, not notification, the Social Framework.

And the Social Framework goes and discovers and loads and presents the extension.

You'll notice that Safari is not directly launching the extension at any point.

It's bouncing through Apple Frameworks to do this.

So, in summary, extensions are small pieces of functionality.

They're purpose built to do one thing and then get out of the way.

They extend the system in new and interesting ways.

We can't wait to see what you guys do with these things.

And they mark extendable parts of the system, as I said, new surface area for you to attach the functionality of your app in interesting and meaningful ways for our users.

And now, to tell you more about Notification Center Extensions I'd like to invite my colleague, Matt Gamble, to the stage.

[ Applause ]

Hello, I'm Matt Gamble.

I'm an Engineer on the iOS Notifications Team, and today I'm going to tell you about Notification Center Extensions, or widgets as we call them.

Now, we've had widgets in some fashion on the system for a while now.

But, for the first time in iOS 8 we are giving developers the power to add their own content to the Notification Center's Today View.

Now, perhaps most fundamentally widgets are view controllers so that means everything you know about view controllers including the lifecycle and containment behavior in API will serve you well when you're constructing your widget.

For example, the appearance calls.

These will tell you when your widget is coming and going in the Notification Center.

And of particular interest is viewWillAppear, as you want to be sure that your view is up and ready to be used by the user by the time you're returning from viewWillAppear.

So, speaking of getting ready, it's important that you get ready fast.

Performance is a very big consideration with Notification Center widgets.

So, a couple tips.

Be sure to load cached data, so you're bringing up your view quickly and you're ready to go.

And if you have some new data that comes in you can do your own custom transition.

Also, kick off your expensive operations as early as possible and definitely in the background.

And when those expensive operations return be sure to cache this data.

So, layout is another concern.

Of course, your widgets and your widget has control over its sub view hierarchy and laying all of your content out.

But, just as with any other parent-child view controller relationship, the Notification Center will be laying out your views frame.

So, that means that the Notification Center is setting the frames.

You don't set your own widgets views frame.

But, of course, not all content is created equal.

So, if you might have, need some more room for your content you can indicate your preferred height and you can do this in two ways.

The first is through auto layout.

If you can describe your widget's height with constraints then those will be honored automatically in the Notification Center.

Otherwise, if you're using more manual layout, you can use the view controller method setPreferredContentSize.

And both of these systems work across both iOS and OS X.

So, while you will definitely want to set your size as early as possible to ensure that you're showing your content the right way in Notification Center, that's not the only time that you can call it.

If, for some reason, you are showing different content or changes you can call this later.

For instance, let's say that we had a button that would show a little bit more content or a little bit more detail about some content that we have in our widget.

You could tie this up to this action, calculate the new height for this new content and then just call setPreferredContentSize.

The Notification Center will take care of this.

It'll automatically resize your widget, but that's only half the story.

What you probably want to do is ensure that your content animates along with this transition and you can do that on iOS with the new method viewWillTransitionToSize with TransitionCoordinator.

So, if your widget implements this method it'll get called and pass in the TransitionCoordinator and with this coordinator you can call animateAlongsideTransition passing in your block with your animations and these will be run in parallel with the systems resize animation, and then again, and then a completion to know when everything's finished up.

Now, there's a similar story on OS X.

In this case it's viewWillTransitionToSize.

Again, this will be called and you can take advantage of NSAnimationContext to run your own animation group and again pass in a completionHandler so you know when everything is finished up.

Now, new to iOS 8 is a protocol, NCWidgetProviding.

And one of the optional methods in this protocol is widgetPerformUpdateWith CompletionHandler.

And it's important to implement this one, as it'll ensure that your widget's content stays up to date.

So, if this is implemented you will be passed a completionHandler, you want to perform your own update and then determine what is the appropriate result to pass back in that block.

So, if you have some new data go ahead and use NCUpdateResultNewData.

If there's nothing new go ahead and use no data.

And if something went horribly wrong, pass failed and we'll try to leave well enough alone.

And then finally, be sure to call the completionHandler with the result that you have.

Alright, let's take a look at this.

So, some of you may remember from last year we had a presentation and we showed off an awesome iOS application for staying in touch with all of the clowns that are in your clown network.

We called it ClownTown.

Well, we're going to take this application a little bit further by adding a widget for the Notification Center.

Now, adding a widget is as simple as opening up an existing application project and adding a new target.

For both iOS and OS X you'll see there's an application extension section and you can just select the Today Extension.

But, I've actually already started work on one so we're going to go right into our view controller here, because widgets are ViewControllers.

And the first thing I'm going to do is ensure that my widget has the right height.

So, I'm using an Interface Builder storyboard for my interface.

So, awakeFromNib is a pretty handy place to take care of this.

So, after calling super and then setting up a little state I'm going to go ahead and get the most recent posts.

Once I've gotten those I'll update my table view.

This doesn't need to be animated.

We're just initializing here.

And then the important step is just making sure to set my preferred content size to, in this case, just the content size of my table view.

So, this will make sure that we're initializing the state where we're showing a single post in our widget, which is nice, but perhaps you want to see a little bit more of what's going on in your clown network.

Well, how about we add a button and an action for our button to show a little bit more.

So, the first thing we'll do is update our state and then calculate our preferred height, which, since we're using a table view, we want to count the number of rows and also make sure to account for our footer view.

Once we have our preferred height we want to set our preferred content size and then make sure we tell ourselves that we had to do an update in the future.

That'll come in handy later.

So, again, we've set our preferred content size so this will be sure to animate the widget to the appropriate height.

But, we want to make sure that our content comes in in a really nice animated fashion.

And so to do that we'll implement viewWillTransitionToSize withTransitionCoordinator.

And so, if we have a coordinator then we know that this is an animated transition and so we'll call animateAlongsideTransition completion.

So, we'll make sure to update our table view in an animated fashion and in the completion be sure to update the title of our button.

Then, if for some reason we didn't get a coordinator, then we'll just update our table without animation and then update the button.

So, this will make sure that our adding of our rows comes in a nice animated way alongside the system animation that's resizing our widget.

Now, one more thing I want to do before we give this a try and that is making sure that our content stays up to date.

So, I'm going to implement the method widgetPerformUpdate withCompletionHandler.

So, I'll start by getting the recent, most recent posts, make sure I get any updates, and then calculate the result.

So, if we already know that we needed to do an update or if we actually did get some new content then we want to use NCUpdateResultNewData.

Otherwise, we can just go ahead and use No Data.

So, if we actually did get some new data we want to do a couple things.

We'll definitely want to update our table view to ensure that it's reflecting our most recent content and then update our preferred size to ensure that our widget is the right height.

And in either case we certainly don't need any more updates and we'll call our completionHandler with our result.

Alright, I think we can go ahead and give this a shot.

So, I'm going to go ahead and build and run for the simulator.

I'll switch over to the simulator now.

Oh, and here we have our ClownTown application.

Well, I'm going to home out of this and present the Notification Center.

Go to the edit menu and our widget is not there.

Excellent, let's give that one more shot.

[ Clicking Sounds ]

Oh yes, there we go.

[ Clicking Sounds ]

There's our application.

Bring down this, one new widget available.

There it is.

[ Applause ]

Alright, so hit done and here's our widget.

Well, we can see that Corbin the Clown has some multicolored suspenders he's giving away, got to make sure to take care of that.

Alright, now here's our More button.

If we tap that, get some new rows animate in nicely-wow, lots of weddings happening.

Less collapses everything back down in a nice animated fashion.

This is looking pretty good.

Well, I definitely want to know more about these multicolored suspenders, so I'm going to go ahead and-huh, okay.

Well, we've definitely seen-if you've looked at the system widgets it's a pretty common pattern to tap something in the widget and then transition to the application and maybe see some more details or get some more content.

Well, that's definitely something that we're interested in doing.

So, let's return to Xcode.

I'm going to stop the debugger.

And since I'm using a table view I'll want to implement tableView didSelectRowAtIndexPath.

So, after we make sure we have a valid post I'm going to take advantage of an object called the extensionContext.

Now, the extensionContext is an object that you'll have access to in your widget or in any extension that's running in a host.

And it has-tells you some interesting things about what's happening in your current host.

It also has a couple of really interesting methods.

In our case we're going to take advantage of openURL completionHandler.

Now, I've defined a custom URL scheme that's shared between my widget and my application so I can construct the URL with this scheme and then add some identifying information for my post.

And then one final thing I'll want to do is just make sure to deselect this row so it's not selected the next time the user pulls down Notification Center.

Alright, so let's build and run this again.

And here's our application.

Let's present here, add this back, and here's Corbin again.

So, let's see.

If I tap this, hopefully I'll be able to get some more information about those suspenders.

There we go.

It brings us right to the application and get some more detail and make sure we don't miss out on those suspenders.

[ Background Sounds]

[ Applause ]

So, a few things to remember-widgets are ViewControllers.

So, everything you know about ViewControllers and the API and the behavior is going to serve you really well when you're constructing your widget.

Also, be sure your widgets resume immediately.

You don't want the users bringing down Notification Center and tapping and not having anything happen.

Now, while the Notification Center handles the layout not all widgets are created equal.

You'll want different heights, and you can so this with the preferred height with either constraints and auto layout or explicitly through preferredContentSize, and this is across both platforms.

Also, you want to be sure to-if you animate your content along with the resize animation, this again, we have APIs on both iOS and OS X to help you do this.

And then be sure to handle update requests to make sure that all of the content looks up to date.

Now, I'd like to invite Guy up on stage to tell us about Share Extensions.

[ Applause ]

Hi. So, my name is Guy Fullerton and I am an Engineer on the iOS Social and Accounts teams, and I'm going to give you everything you need to know to implement Share Extensions.

So, Share Extensions are a way to take the sharing and upload functionality in your app and package it up and get that presented in the standard activity and sharing affordances in the operating system.

Actually, let's go back.

So, like, the UI activity view and the share menu in Mac OS X.

So, this is particularly useful for social network apps or for apps that like to do video or photo hosting, for example, and lots of other stuff.

So, let's go through a concrete example.

Let's say I've got a photo blogging application that normally allows my user to launch the app, pick a photo from the photos library, annotate that a little bit, maybe make a-choose an audience to present it to and then upload it to a photo blog.

But, wouldn't it be great if I could do that upload directly from within photos app?

Well, that's what a Share Extension would let you do.

So, let's say I'm in photos, I select a photo that I like, tap the share button, up pops the activity view and you can see my Photo Blog application icon there in the activity view.

Tap that and up slides a compose sheet for Photo Blog, lets your user type some text to annotate it, choose an audience, whether they want to limit the exposure of that photo and whatnot, and then post it.

So, we're going to hammer this home a lot in the next couple days, but a lot of extensions are basically just ViewControllers.

That's where most of the work is and you're probably already familiar with how to do that.

But, Share Extensions have a couple other concerns having to do with their packaging, specifically the extensions Info.plist, so let's talk about that.

So, normally when a Share Extension shows up in one of these sharing affordances you generally want that extension to have the app's name.

That's going to be the thing the user's most familiar with looking for when they want to select that extension.

But, some applications may have multiple share affordances.

The Photo Blog, for example, might want a basic posting share functionality and maybe it wants a way to specifically set the blog's header photo, for example.

So, it's important to be able to customize your Share Extension's name.

And the way you do that is with CFBundleDisplayName in your Info.plist.

Just set that to the name you want your Share Extension to show up with and the sharing UI will display that name appropriately for your extension.

So, the next thing to talk about are activation rules, and this is probably best explained through an example.

Let's say you're in photos and you have a set-a trio of things selected.

You've got two photos and a video and the user wants to share that.

So, they tap the share button, up pops the UI activity view or the share menu on OS X and behind the scenes we've created an extension context and that extension context is the conduit that your extension will use to pull data from the host application into itself.

So, this extension context for this particular scenario has three things on it.

It's got a reference to two of those images, and of course, a reference to the video as well.

Now, the user might a bunch of Share Extensions on their system and each of them wants to get invoked in different kinds of situations.

And so the activation rules are what lets the system make the right decision about which extensions to put in the activity view or the share menu based on the kind of data that's in the extension context.

So, there are two ways for your extension to supply its activation rules.

The first is a predicate.

Now, every extension's Info.plist has an NSExtension dictionary.

And within that NSExtension dictionary is the NSExtensionAttributes.

The system uses those attributes as part of its decisionmaking process about which extensions to show.

But, importantly for Share Extensions the NSActivationRule within the NSExtensionAttributes is what you need to set.

If you set that to a string that's a predicate, we will run that predicate in the host app and figure out if your extension is appropriate.

And that predicate can be as detailed as you need it be or it can be simple if you want it to be-whatever you need to get the job done.

And this will serve the needs of a lot of extensions.

Other extensions, however, may have a little more fine grain needs.

What we found internally is that a lot of our extensions just wanted to know basic or just wanted to say that they support a basic set of images or videos or some URLs or text.

So, we offer these condensed rules that allow extensions to specify just those particular things without having to write a complex predicate.

Now, ultimately these condensed rules boil down to a predicate behind the scenes, but you don't need to worry about that.

So, to specify condensed rules you still put in an NSActivationRule inside your NSExtensionAttributes.

But, this time it's a dictionary and there are a number of keys you can put in the dictionary that indicate the amount of that type of data your extension is interested in.

Now, the bottom of those two activation rules both had to do with web content and they're slightly different.

We have a WebURL support and a WebPage support and I want to go into the details about these.

So, some Share Extensions are all about taking the link you're currently looking at in Safari and posting it someplace else to some feed so somebody else can click on that link and go see the webpage.

For those kinds of Sharing Extensions you want to specify that you support the WebURLWithMaxCount.

Now, another class of Sharing Extensions are all about looking at the page that Safari's currently got displayed and pulling data out of that page.

And for those Sharing Extensions we have the WebPageWithMaxCount activation rule.

Now, in order to support the WebPageWithMaxCount your extension also needs to supply a JavaScript that's run to determine what part of the web content to actually stick on the extension context.

I'm not going to go into detail on that here, but in tomorrow's session we dive into detail on that so please check that session out.

Hammering it home yet again, extensions are basically just ViewControllers so it's a lot of familiar territory.

Share Extensions can be implemented with two kinds of ViewControllers.

We support them on both iOS and OS X and you can subclass UIViewController or NSViewController as you wish.

You probably already have ViewController code in your apps today that you can repurpose for a Share Extension.

Sure, maybe you want to make some tweaks to it to conform better to maybe the limited amount of space you have on the screen or whatnot.

But you can take that existing code, massage it a little bit and deploy it in an extension, and you can get it to look exactly how you want using your own branding or existing UI or whatever.

Some extensions, however, want to conform to the standard system share sheet look.

And for them we offer the SLComposeService ViewController that you can subclass to get, among other things, the standard look, the standard animations, text editing, a indication of the remaining characters based on the characters typed so far, the post and cancel buttons, built-in previewing and other limited amounts of customization.

So, once you've implemented your ViewController the next thing you need to be concerned about is actually doing the upload or post to wherever you're sharing this data to.

The key here is that you need to use NSURLSession with a background session configuration, reason being, your extension does not live beyond its presentation-at least not very long, and we'll talk more about that tomorrow.

So, you need to make sure that by using a background NSURLSession you're allowing the system to handle the upload for you.

So, you create a background NSURLSessionConfiguration, build an NSURLSession around that, build an NSURLRequest that encompasses the items that are being shared and create an upload task wrapped around that NSURLRequest and then start that upload task.

Once you've done that the system is going to handle the upload for you.

At that point your extension needs to tell the host app that it's done and it can tear down the presentation of your ViewController and you do that by calling a method on the extension context called completeRequestReturningItems completionHandler.

If the user chooses to cancel your sheet, or whatever you're presentation happens to be, there's another extension context method you need to call.

This is called cancelRequestWithError.

So, Matt already talked about performance with request-with respect to the today widgets and I want to talk about that in a little bit different way in terms of Share Extensions.

Share Extensions often have to grab a bunch of data from the host app.

They might be dealing with 10 photos or an enormous video and it can be expensive to be pulling that from the host app while the presentation is happening.

So, if you notice that your presentation animation is stuttering you may need to defer some of that heavy lifting until after the presentation is completed.

If you subclass UIViewController or NSViewController, do whatever makes sense for the rest of your code.

Just defer that task so it's not impacting the animation.

If you subclass SLComposeService ViewController, however, we will call your subclass' presentationAnimationDidFinish method giving your ViewController a good hook with which to start that heavy lifting.

So, let's take a look at the Photo Blog app and the Photo Blog Share Extension real quick.

Before going into the code I'm going to show you the Share Extension running so some of the code will make a little bit more sense.

So, we're going to go into photos just like that set of screenshots I showed.

Let's pick a different one.

Puppies-we'll do just one.

Oh, let's not do that.

There we go.

Click on the Photo Blog Share Extension and up comes the compose view.

So, this is clearly using the standard system compose sheet look derived from SLComposeService ViewController, and it can do a couple different things.

You can type some text and as I'm typing text, it's kind of hard to see, but there's a little indicator of the remaining characters, and once I type too much text it goes negative and the Post button disables.

This is all happening more or less for free.

You'll see some details when we look at the code.

The image preview there, it happens for free, the base class will do that for you.

We also, this particular subclass adds an audience picker so this Photo Blog supports the notion of posting publicly or only to my friends or completely privately.

And you can choose a different audience.

Let's delete some text here so we're within the allowed range, then we can post.

So, let's go back into Xcode and see how we implemented that.

So, the first thing I did-and it's hard to see, sorry, but this is as good as I can do with the Info.plist.

The first thing I did was set the bundle display name for my extension to Photo Blog and then I set up an activation rule.

This particular activation rule says my Share Extension only wants one image.

That's all you need to give me.

I don't care about anything else.

This is one I'm relevant.

And now my ViewController.

So, my ViewController subclasses from SLComposeService ViewController.

Now, let's go look at the implementation.

But, before I start adding code to this, I want to talk about a couple artificial constraints for this demo app.

So, for purposes of showing off the remaining characters count and generally updating the Post button, I'm going to say that my photo blog has two constraints associated with it.

The first is that no photo can be posted with more than 20 characters alongside the photo, because it's meant to just let you see the photo and not get distracted by the text.

And also it's supposed to be a lightweight photo blog.

It doesn't want to spend a ton of storage space on the photos.

So, no photo that you post can be bigger than one megabyte.

And this last constraint is particularly important, because when you pull data from the host application you're not in control of the data's size.

If you've got a photo coming from photos app it might be quite large and therefore it's your extension's responsibility to down sample that image data to fit within your size constraints.

So, let's start implementing the ViewController.

So, I'm going to add an image data property to my subclass.

This is going to be the fully down sampled image data that I'm going to ultimately upload to the photo blog.

I'm going to keep track of the audience that the user wants to post it to.

And I'm going to keep a configuration item that I'm going to talk about a little bit later on.

Basically, this represents that AudiencePicker table cell down in the bottom of the compose view.

Alright, so in viewDidLoad we're going to initialize the audience.

We already have a utility routine in our app that knows how to fetch the right defaultAudience based on a number of factors.

So, we're just going to reuse that code from the app.

Now, those constraints I was talking about, the 20-character limit and the down sampling, both need to affect whether the Post button is enabled.

So, if the user has typed more than 20 characters we don't want them to be able to post.

If the down sample is not finished, likewise, we don't want them to be able to post, so we need to make sure the Post button is disabled in those cases.

Also, since we're using the remaining character count indicator we need to make sure that's up to date at all times.

The right place to do that is the isContentValid method.

The SLComposeService ViewController calls your subclass' isContentValid method whenever it needs to update the status of the Post button so it gives you your chance to do those things.

So, let's implement some of that.

The first thing I'm going to do is look at the number of characters that the user has typed so far.

SLComposeService ViewController has a content text property, which is the text that the user has typed so far, so we grab its length.

Next, we calculate the number of characters remaining based on our Photo Blog's constraints.

Then we set SLComposeService ViewController's charactersRemaining property.

This is an NS number and by setting that to an NS number the base class automatically updates the text on the compose sheet to-sorry, to reflect the number of characters you still have left.

And I just add it here.

So, next we are going to say that the Post button should be enabled if the user has not typed too much text and if we already have our fully down-sampled image data.

Otherwise, don't enable the Post button.

Now, we got to get our fully down-sampled image data and, like I said, this can be a potentially expensive operation, because we don't know how much data is going to coming over from the host.

So, we want to defer that to presentationAnimationDidFinish.

This method is called by sub-called on subclasses of SLComposeService ViewController after the compose sheet has slid up onscreen.

So, the first thing we're going to do is look at the extension context again and in particular look at the input items.

Now, I'm going to go off in the weeds a little bit here, but bear with me.

The extension context can have multiple input items on it.

Each input item represents a separate upload blob.

For most Share Extensions you only care about doing one upload at a time.

But, you might imagine a particular Sharing Extension that's allowed to do five posts to one social network at one go.

So, for that kind of hypothetical Share Extension you're going to need to look at all the input items.

But, since this Photo Blog extension only cares about posting one photo at a time with one post at a time we just look at the first input item.

Next, we're going to iterate over all of the attachments on that extension item.

Each extension-each post request could come with multiple pieces of data.

Like, you could go into photos and select two photos and choose to share that.

You would have an extension context with one item with two attachments.

So, we're going to iterate over the attachments and find the photo that we care about.

So, what we do is we iterate over all the item providers in the extension item and we call hasItemConforming ToTypeIdentifier passing kUTTypeImage.

This says, 'Hey, item provider, do you have an image in you?' And if we find out that the answer is yes, we are going to do some work.

We're going to need to pull that particular item provider's data and we're going to want to do that on a background cue so as to not disrupt the main thread.

And since we've found the one image that we support we can stop iterating now.

So, the actual work of pulling the data happens with the LoadItemForTypeIdentifier options completionHandler method, again, saying 'Give me some UTTypeImage, please.' And the completionHandler that you pass actually has a flexible first parameter that indicates the way you would like the item provider to supply your data.

In this case, my Share Extension is interested in NSData, so that's what my completionHandler's prototype is.

And when I get the-scroll up for me, let's see if I can make this a little wider, not wrap so much It's important to note that loadItemForTypeIdentifier completes on an arbitrary cue and since we're going to do view controller work I'm going to need to dispatch async onto the main cue to actually do that work.

Then I'm going to call a method on my subclass called imageDataLoadDidFinish and pass it that data.

So let's go implement that.

So, my application already has Downsampling code, right?

And it's what it would in the normal case in the normal application case.

So, I'm just going to repurpose that and ask it to downsample the image data, passing my map size.

And that's going to complete on an arbitrary queue, so once that completes I'm going to do view controller work, so I dispatch async onto the main queue and then I remember the downsampled imageData.

Now that I've got that image data, I need that Post button to get updated, and the way I do that is call a method on SL composer ViewController called validateContent.

ValidateContent calls your subclasses IsContentValid method and gives it another chance to determine whether or not to enable the Post button.

Okay, so, when the user taps the Post button on an SL composer ViewController subclass that subclasses didSelectPost method.

So let's react to that.

The first thing we're going to do is perform the upload and after that we need to tell the host that our extension is done and you can dismiss our ViewController.

And, again, we do that by calling a method on the extension context called completeRequestReturningItems completionHandler.

So let's look at the upload again.

So the upload is going to be very similar to what I already showed on the slides, but let's go through it again because it's very important for extensions because they have a short lifespan.

The first thing we're going to do is create a background NSURLSessionConfiguration, create a NSURLSession from that configuration and create a request representing the upload.

Now, again, this is code we stole, well, not stole, but code we borrowed from the main application that builds an NSURLRequest from the photo data from the content text that the user has typed and the audience.

Build an UploadTask around that request and then start that upload.

Now, in this particular case-well, let's see, let's step back for a second.

So, we implemented didSelectPost method.

SL compose service ViewController will also call a DidSelect cancel method on your subclass, but in this particular case we don't need to implement it.

The default implementation of didSelectCancel will go ahead and cancel the extension context for you automatically.

But if your extension needs to do some cleanup work in response to a cancel, you can override didSelectCancel.

So that's enough to handle the basic uploading and interaction with the sheet, but at the bottom of the sheet-remember there was that little audiencePicker cell.

I'll show you how to implement that.

The base class will call your subclasses configuration items method as a way to give your subclass a chance to supply that set of table cells to put on the bottom of the share sheet.

So, we're going to implement the configurationItems method.

It's our subclass' responsibility to return an array of SLComposeSheet ConfigurationItems, one for each configuration item in the table.

Now, the example here only has one configuration item, but you might imagine a share extension that needs multiples.

In fact, some of the built-in system extensions have multiple configuration items on the bottom.

So, we instantiate one and we set its title.

The title is what's displayed on the left side of the table cell.

Then we set its value, which is the audience, displayed on the right side of the table cell.

And then we need to react to when that table cell is tapped.

And the way we do that is by setting a tapHandler on the configuration item.

Now, I'm going to avoid a retain cycle between my view controller and the configuration item by capturing a weak reference to myself inside the tapHandler.

And then what we do is we grab a strong reference and do that weak strong dance.

And if our view controller is still around.

At that point we know the table cell has been tapped.

We want to show that audiencePicker UI.

So we've already implemented inAudienceViewPickerController.

It's just a basic table view controller that happens to be audience specific for this particular use.

So we instantiate that.

We set ourselves to be its delegate.

Our custom AudiencePickerViewController class has a delegate method that's called when the user taps an audience, so that we get to react to the specific audience chosen.

We're going to tell it what the currently selected audience is so it can check the right thing.

And then we're going to call a method on SLComposeServiceViewController telling it to please animate this other ViewController onscreen and that method is pushConfigurationViewController.

So, this slides your new view controller in a navigation controller-like style and it resizes the composed sheet to fit your view controller's desired size.

And then, finally, now that we've implemented that we can return our single configuration item from configuration items.

All right, so here is our delegate method that is called when the audiencePickerViewController sees the user tap a cell.

We need to react to that.

We're going to remember the selected audience.

We're going to tell our configuration item that hey, it's got a new value.

And by doing that the table cell is updated automatically showing the new audience.

And then finally we want to auto dismiss that audiencePickerViewController.

You don't have to do this.

It really depends on what your configuration item's needs are.

But generally, for this kind of simple audiencePicker, once you tap a cell you want it to swipe away revealing the main compose sheet.

So this is just some boiler-plate code for executing code after a short delay.

And then we call popConfigurationViewController.

So, in a navigation controller-like way, this says remove the topmost view controller from the stack, and since we only had one there, it restores the default share sheet appearance.

So, that is it.

Let's go ahead and rerun this guy and take a look at it one more time.

Photo Blog app.

Let's home out and go back to photos.

There's our photo.

We can bring up the photo blog extension and there it is.

We can choose an audience, make our comment and post away.

So, that's how you make a share extension for iOS.

But if you are looking at that Xcode project closely you can see that there's a couple OS X targets in there.

Now, it turns out that share-yeah, let's hide some more stuff-that share extensions are very portable.

It was pretty easy for me to take my iOS share extension and massage the code a little bit for OS X and so that is working here.

I can go to some photos and select a photo, preview it, bring up the share menu.

Hey, there's my photo blog item and there's my OS X photo blog share extension.

[ Applause ]

So, that demo was all about a subclass of SLComposeService ViewController.

But we know a lot of extension developers are going to want to just subclass from UIViewController and NSViewController and we'd love you to do that because you can go to town and customize the UI and put your branding in and what not.

But the basic work that you need to do beyond implementing the view controller is pretty much what I already showed you in the demo.

You've got those Info.plist concerns you need to still set your display name.

You need to still provide your activation rules and you still need to do your upload with NSURL background session.

So, you just learned all about app extensions.

You learned a little bit about how they're packaged, about the different extension points, how the communication works between the host and the extension itself.

You learned how to make a Today widget and a share extension on both iOS and OS X.

So go take a look at your application, figure out what pieces of your sharing functionality or other functionality you can package up into an app extension and deploy.

So, we think this is really cool technology and we're really looking forward to all of the good features and functionality you're able to supply users with your app extensions.

[ Applause ]

Jake Behrens is our super Evangelist.

We have a really comprehensive app extensions programming guide that goes into tons of detail on all the share points and all the mechanics of all this.

Tomorrow, we have another great Extensions session where we go into detail on more share points.

We also talk about more of the underlying infrastructure.

We also go into great detail on the JavaScript support for web content.

And importantly, more detail on the extension context in NSI to providers.

So there's tons of good information.

I urge you to check those out.

We have two labs where you can talk to us Apple engineers.

We'd love to get you started implementing your share extensions.

One of those labs is immediately after this.

So, please meet us downstairs and we'd love to chat.

Thank you.

[ Applause ]

Apple, Inc. AAPL
1 Infinite Loop Cupertino CA 95014 US