App extensions allow powerful ways to expose your application's abilities throughout iOS and other apps. Discover best practices for various extension types and see how to effectively communicate between your extension and its parent application or the network. Learn techniques for using the shared keychain and see how to improve discovery of your share and action extensions.
SOPHIA TEUTSCHLER: Good afternoon.
Welcome to App Extension Best Practices.
I am Sophia Teutschler, I am an engineer
in the UIKit Framework Team.
Later in this session I will be joined by my colleague,
Ian Baird from CoreOS.
The first part of the session I would like to talk
about the two main types of extensions on iOS,
Action and Share extensions.
The later session, Ian will join us to talk
about Today Widget enhancements.
Throughout the session we will show you many real-world
examples to help you make the most
out of those types of extensions.
Now, Action and Share extensions.
Let me give you a brief overview of the two types
of extensions, how they differ.
Now, Action extensions are all about changing content in place,
where the Share extensions are all about moving content
from the current host application
to your application or Web Service.
Now, let me give you more examples here.
As I just mentioned,
Action extensions act on a current content.
Because of that, they use the content
as the main user interface.
I will show an example later on about that.
But you also have the opportunity
to present additional options before you perform that action.
Also, your application functionality can be separated
in different Action extensions that live
within that container application.
Now, let me show an example here.
We are in Safari.
I am going to tap on the action item on the top right,
bring up our Share Sheet with our two types of extensions,
the Share extension is on the top
and the Action extension on the bottom.
We made this Translate extension, and when we tap it,
the following will happen.
Safari will transfer a webpage to that extension.
There it is translated and then the translated corners move back
into place in Safari.
Here's a variation of the same one.
When you tap Translate, you bring up this form sheet
where a user can select one of the languages.
Then when you tap Language, we transfer the webpage
to the extension, then again it's translated and moved back
into place in Safari So so much about Action extensions.
So how are Share extensions different?
As I said earlier, Share extensions are all
about sharing content from the host application
to your app or Web Service.
And because of that, it's very important
that you offer the user to validate and edit
that content before it moves out to your Web Service.
And we offer you an API for that
that I will show you a few examples on later on.
Also, your Share extension represents your application
in the Share Sheet, and to prevent confusion,
we only allow you to offer one Share extension per
Now, let me show an example for the Share extension here.
We have the Share in the bottom
and our Share extension on the top.
Now, we select four photos, and when we tap
on the iCloud Photo Sharing extension,
the following will happen.
We get a little preview sheet where a user can add a comment,
maybe tap the Shared album, and a little preview on the right.
But we haven't transferred any data yet.
Now, if the information is correct, the user can decide
to either cancel out or tap Post to actually perform the Action.
Now let's take a closer look at this sheet here in the Center.
This is actually the API I was talking about earlier.
It's called the SLComposeServiceViewController.
This is how it looks straight out of the box.
There's a text field, a little preview on the right.
However, it's very customizable, and for our special service,
for example, we like to customize it
to make it look more like this.
We added a place holder, we even added a character
We even change the preview view,
and we have some options on the bottom part.
Now let's go through some examples to build that up.
First, to set a place holder,
set a place holder property on that sheet.
There we go.
And your Web Service might have a maximum character limit,
so we have a little indicator on the bottom left for that.
To update it, just set a property there.
And again, we offer you a preview view out of the box,
but you can have your own one.
To support that simply subclass the composer's controller
and override load preview view,
and then return your custom view controller for the preview.
One thing to keep in mind, however,
make sure that the view has --
otherwise it might not show up on the sheet.
Now, there are these options on the bottom.
Those are simply cells with a value on the left,
value on the right, and to tap on them,
we push an option view controller
that can select your options, your custom options there.
Let me show you how you set that up in code.
Subclass SL -- composer, then override the -- method.
Each row is represented as an SL compose sheet
Quite a mouthful.
On the left is the title.
The value is on the right-hand side.
Now, if the user taps on a cell, we call this tap handler here,
and there you are supposed
to create your custom view controller
for your custom option.
And to push it, simply call push configuration view controller.
This works the same way as the API
from your navigation controller.
And then finally, return the area of items.
Now, we even offer you auto complete support
on the compose sheet.
To auto complete, you set a view controller
to update the button part here.
In this case, it's a table view where we auto complete names,
but it could be really anything.
To set it, just set the auto completion view property,
we do all the animations for you.
When you are done auto completing,
you set it back to nil.
All pretty simple.
provides a consistent and familiar UI.
It's very customizable for you to adopt it in your app.
However, if you have different needs
for your service extension,
you can always go back subclassing your view
Now, I will just show you how to make Action Share extensions.
Now let's change gears a little bit
and discuss how you can support extensions made by someone else
in your host application.
For example, let's say we made a text edit application,
and we'd like to share those text documents
as well, plain text.
However, some extensions might not understand text.
They might only support PDFs
or other extensions might only support HTML.
Now, you would like to support as many extensions as possible,
so a good way would be to offer all those three file types.
But when you think about that,
those are not really three separate documents
when you share a text document,
but what you really have is a single document
that supports different file formats.
And exactly for that we have an API called NSItemProvider.
In one sentence, NSItemProvider is a single item
with multiple representations.
Let me show you a code example to explain it better.
Now, we create NSItemProvider, then we call register the item
for type identifier to add a new representation
to that item provider.
In that case, it's plain text.
Now, if the extension asks for the plain text representation,
the system will call this load handler block here.
And there you are supposed to create your text document
on the fly and call the completion handler
to return the data back to the extension.
Same for PDF.
As soon as the extension asks for a PDF representation,
we call the load handler, there you create your PDF on the fly
and call the completion and let it return data back
to the extension.
To display Share Sheet,
simply create a UI activity view controller
and offer the item provider options in the initializer.
Now, in the first example with the compose sheet you saw,
we have this little preview in the top right.
The hosts should offer previews as well.
Those previews represent what will be shared,
and also they need to be simple and efficient to create.
They can't be too large.
Again, for that, we have an API in NSItemProvider as well.
It's called preview image handler.
And as soon as the extension asks for a preview,
we call this block here, and there you are supposed
to create your thumbnail or your preview representation
as an image on the fly and call the completion handler
to return that data back.
Now we need to change our perspective yet again.
We talked first about implementing extensions.
Then we talked about supporting extensions as a host app.
Now let's look at what it's
like to be an extension developer again and how
to declare your support with the kind
of data the host app can offer to your extension.
For example, maybe you like to have full sharing service,
and you want to declare that you are sharing extension support
images and movie.
Now, let's make three extensions in that case.
One extension should support -- one extension supports images,
another one can handle movies,
and a third one handles images and movies.
Now let's see what happens
if the host app shares the single image.
In that case, the first
and third extension will appear in the Share Sheet.
Now, same way for the movie case.
If the host application shares a single movie, the second
and third extension in the Share Sheet, that makes sense.
However, what happens if the host
of a share supports image and movie?
You would think all the extensions would appear
in the sheet; however, this is currently not the case
since in iOS 8 the implementation requires
that extension can handle all two types of file formats.
It's a bit unfortunate
since user extension developer would probably like to support
as many extensions as possible, and to do that, you should offer
as many representations as possible as well.
But in that case, you actually get less extensions
than by just sharing a single representation,
like movie or image.
So we added a new behavior in iOS 9.
You can opt in by adding the NSExtension activation
in new extensions info plist, set it to two.
After that, your extension will appear even
if the host app shares both image and movie,
and even if you are only interested in movies or images.
Now, those are high-level activation rules,
but we also support activation rules that are just predicates.
Again, predicates can be very simple but also very,
very powerful like this one here.
I won't go into details right now,
but check out the App Extension Developer Guide
for more information.
Now, let's talk about icons for extensions.
As I mentioned before,
Share extensions represents your application
of Web Service within a Share Sheet.
Because of that, we simply use your application icon
or a containing applications icon as the icon
in the Share Sheet, so there's no additional work
for you needed.
Action extensions, on the other hand, require template images.
We require two sizes, one for the iPad, one for the iPhone.
And they reside in the extension bundle.
Now, let me explain how template images work.
Think of it as, like, a stencil.
A template image is a black-and-white representation
with a transparent background, and the system then takes
that stencil of that black-and-white image
and creates the actual icon for the Share Sheet.
And again, require two sizes, 60 points for the iPhone,
76 points for the iPad.
However, I encourage you to use image assets in your extension
and offer all different -- all the different icon sizes
that are for application icons.
That way your app extension will be far more future-proof.
So much about Action Share extensions.
Let's invite Ian on stage to talk
about Today Widget enhancements.
IAN BAIRD: Thank you, Sophia.
Hi. I'm Ian Baird, CoreOS engineer, and today I am going
to talk to you about Today Widget enhancements.
First a quick recap.
As you remember from last year,
Today Widgets give you quick information at a glance,
so your stock prices, your sports scores.
It tells you how long it's going to take for you
to get home from work.
So today I am going to talk to you about how
to enhance your Today Widget, how to make sure
that the model data is always up-to-date and in sync
with your containing app.
I'm also going to tell you about how to make sure
that your visual representation is up-to-date
and reflects your widget's content as well.
And then I am going to go over some general best practices
that you can use for all of your extensions,
including your Today Widgets.
Let's get started.
This is a view of the Notification Center.
It's populated by Today Widgets.
You can scroll it to see all of the widgets,
and if you want more information about anything you can see here,
you can simply tap on the widget, and it will take you
to the containing app for more information.
Now, how do we facilitate this interaction
between the Today Widget and its containing app?
Well, we do this by the use of URL schemes.
And now I am going to tell you a little bit more about those.
Interactions use these URL schemes and some open URL API
on NSExtension contexts to take the user to the app.
Now, we have some best practices that I'd like to talk
about today governing the use of these URL schemes.
First, we'd really like you to use and concentrate
on your app's registered URL schemes.
This is great.
The next thing you want
to use is you can use system URL schemes, like HTTPS.
That's going to open a webpage in Safari.
You can also create messages, start phone calls,
and perform other interactions and start interesting workflows
with system components.
And here's how you do it.
You'll notice that I am showing a table view, did select row
at index path callback because a lot
of widgets are simply table views.
The first thing we are going to want
to do is actually construct a URL.
We are going to do this with the myapp URL scheme here,
just for the sake of example.
This is going to take us to my containing app.
The next thing we are going to want
to do is we are actually going to want to call
that open URL API that you can find on the extension context,
which is probably attached to your view controller.
Now, this is a little bit different from the API we expose
on UI application in that it takes a call-back block,
and this call-back block has one boolean parameter
that I am going to tell you more about now.
The success parameter is going to be set to true
if we were able to take you to the containing app
or to the system component,
which has registered the URL scheme.
It's going to be set to false if we were not able to do this,
like if the user has not unlocked the phone and pulls
down the Notification Center when the phone is locked.
There are many other ways of interacting
with the containing app and sharing data, and today I want
to tell you about how to use defaults
with your containing app; containers;
Keychain items; and framework data.
Framework data is going to be scribbled into Shared containers
on your behalf by system frameworks.
And all of this is neatly grouped up into app groups.
First, user defaults.
You probably know what user defaults are
if you've been developing for Cocoa
or Cocoa Touch for a while.
They are small pieces of configuration data.
They are like little strings, numbers, booleans,
and other things that affect the configuration of your app.
You can share these pieces of configuration data
between your containing app and your extensions
by using the NSUser defaults init with suite name API.
You are going to pass the app group identifier to this API.
Now there's an important thing to remember about this,
this API doesn't merely unlock your containing app's defaults,
your standard user defaults in your containing app.
What it does is it creates a new default suite
which your containing app can also have access
to if it participates in the app group, so it's super important
that you use this API, not only in your extension,
but in your containing app when you want
to change these configuration items in the defaults.
The next thing I want to talk to you about are things
that you can use inside of the shared container
which the containing app and its extension all have access to.
The first thing you can keep in there is model data.
Model data is stuff like SQLite files, core data data stores
and maybe even model objects persisted
to things like plist files.
You can store all of this inside of a shared container
where the containing application
and its extensions can all have access to it.
You can also store documents in there.
Earlier, Sophia was talking about a text application,
and maybe this text application should store its text documents
inside of the shared container, where it can edit them
and the extension can edit it.
Next, you can also store media, media items like images,
video clips, audio files, and other things that you want
to have accessible to both your containing application
and its extensions.
Now, if you are storing cacheable items
in the shared container, that's probably not all that good.
I think you want to keep that in caches where it can be purged
if we start running low on space.
Now, to set up core data to use the shared container,
I want to show you a little bit of sample code,
and this is sample code that you are going to use both
in your containing app and any of its extensions.
You are simply modifying the code that Xcode already produced
for you via the template.
So the first thing you will want to do is create a new property
or change the existing one.
And in this case, we are calling it secure app group presentation
Of course, the first thing we need to do is get an instance
of the NSFile manager.
And next, on file manager, we are going to call container URL
for security application group identifier, and again,
pass the app group identifier that we had set up previously.
After that, we are going
to append the store file name to that URL.
That URL points in to the shared container,
where both the containing app and the extension can access it.
Now we need to use this store URL, so we are going to set
up our persistent store coordinator.
And the way we are going to do this is
by first creating a store coordinator using our manage
object model, and then we are going to grab the instance
of the store URL we just created.
Finally, we are going to add this persistent store
to the store coordinator using the URL we just created.
This is going to point -- remember --
to the SQLite file that's backing this persistent store
in the Shared container.
Finally, we return it to the caller.
Lastly, we actually want to set up the manage object context,
and we will do this by grabbing our persistent store coordinator
and then creating a manage object context.
And then we will set this coordinator
to be the persistent store coordinator
for the manage object context and then return it.
And that's all you have to do to set up core data inside
of your containing app and any of its extensions
to use a shared persistent store which is located
in the shared container.
So core data may not be what you need for your application.
Let's pretend that you are using the plist files that I talked
about earlier to serialize your model objects
into the shared container.
At this point, you are going to have to worry
about synchronization, access, and all this kind of stuff
in the shared container because very potentially, your extension
and your application could be attempting
to simultaneously modify these files,
which is not good for data consistency.
So you may actually have to use exclusive locks.
And you need to be really super careful
about using exclusive locks for data in the shared container
because when an extension is killed,
if it's suspended while holding on to an exclusive lock.
Again, this isn't good for data consistency.
So what do you need to do about this?
Well, I'd like to talk to you a little bit more
about task assertions.
Task assertions are a great way for your extension
to tell the operating system that, hey, I'm doing something
that you probably shouldn't interrupt.
And if you interrupt it, well, I'd like to get some sort
of call-back so I can clean things up first.
Remember that extensions are suspended pretty aggressively
when they are no longer in use.
When the user swipes down to expose the Notification Center
and then swipes back up to dismiss it, we are going
to suspend all of the extensions in play at that time.
So that could be pretty quick.
So you are going to want to protect serialization
and other cleanup tasks
with these background task assertions.
And I am going to show you how to do
that now using the NSProcess info API.
So the first thing you are going to want
to do is get the NSProcess info instance
by calling the process info factory method.
Next you are going to want
to call the API perform expiring activity with reason,
and I am going to take you through how to set up this call.
The first parameter you are going to pass
to this method is a very short string.
This short string is for you, not for the operating system.
This tells you what you are doing inside
of the protected task.
The next thing you are going to pass
to this API is a call-back block,
and the call-back block is going to be used in one of two ways.
And let me go through the first way with you.
First, it's going to be called if we were able
to acquire a background task assertion on your behalf
with expired set to false.
This means to you that it's safe for you
to perform some sensitive task like serializing data
that you don't want to be interrupted.
Now, since a task assertion cannot be taken
out indefinitely, when the task assertion is about to expire,
it's on the cusp of being let go,
the operating system is going to call your call-back again,
this time with expired set to true.
This means that you need to cancel whatever task you have
in flight and prepare to be suspended.
Now, when you exit the block, we are going
to release the task assertion on your behalf.
There's nothing else that you need to do.
So the second way that this can work is that we were unable
to acquire a task assertion for you at all.
What we are going to do in this case is we are going
to immediately call your call-back block
with expired set to true.
You probably don't want to take an exclusive lock
in the shared container at this point
because if you get suspended, nothing is protecting you.
At this point, you want to clean up
and get ready to be suspended.
You only have a few brief seconds to make sure
to clean everything up.
So that's a complex topic, but I think there are three things
that you need to come away with this talk about task assertions.
The first is that they're released
when your code exits the call-back block.
We grab one at the beginning, and if everything's successful,
we set expired to false, and then we hold on to
that task assertion for you
until your code exits that block scope.
The next thing to remember is
that there is potentially re-enter and execution
of your call-back for the purposes
of notifying you about expiration.
Again, we can call your call-back block again
with expired set to true to give you a hint that, hey,
this task assertion is about to expire
and it's time for you to clean up.
And then finally,
task assertions are not always available for your extension.
Sometimes you will call and expired will be immediately set
to true and your call-back block will be executed that way
and you will not get a chance to perform some critical operation.
You need to be prepared to deal with that
because it will happen.
Next, these task assertions only protect the code in the scope
of that block, so they are generally used
for very simple things, like quick serialization,
something that's synchronous.
What do you do if you need to coordinate
with work on another queue?
Because you remember that the task assertions are scoped
to that call-back block.
Well, a call-back block must synchronize
with protected work on other queues.
So for instance, if you have something on the main queue
that you wish to protect, you need to make sure
that that call-back block does not exit the original protected
block scope before that work has completed.
So here's an example of how not to do it.
By dispatch asyncing to the main queue from within the block,
execution will exit that block's scope,
possibly before perform interrupt will work
This is pretty bad.
So in this case, your block needs
to synchronize via dispatch sync or dispatch semaphores
or whatever method you choose with the main queue.
And remember that this work that you are dispatching
to another queue -- it doesn't necessarily have
to be the main queue -- needs to be cancelable
because we might call back your block with expired set to true,
and at this point, you will need to clean
up whatever you are doing, exit, go back to the block, drop out,
and release the task assertion.
And it's actually safe for you to do this
because this call-back block is executing
on a private system queue.
You are not going to dead-lock
because we are not going to call back into it.
Now, that was a lot.
Moving along here.
In our new multitasking world, we can now run into situations
where your extension and the containing application are
And this means that if something happens inside
of your containing app that changes your model data state,
potentially your extension could be out of sync
with the model state, and this is bad.
So one way, one thing you can use to keep everyone
on the same page is the Darwin Notification Center.
Now, the API is similar to the NSNotification Center,
but it's a lot simpler, and there's a smaller number
of use cases for which it is applicable
to your containing app and its extension.
For example today, we are going to show you how to use it
to hint your extension to reload the model.
And this is how you do it.
First, inside of that containing app, you are going to want
to get an instance of the Darwin Notification Center,
and you will do this by calling CFNotificationCenter,
get Darwin notify Center.
Then you are going to pass this notification center instance
to CFNotificationCenter post notification.
The next thing you are going to want to do is to pick a string.
This string is going to represent your notification,
and you are going to use it in your containing app
and any extensions with which to observe this notification.
And then you want to pass true.
In your extension, again, you are going to want
to get an instance of the Darwin Notification Center,
and you are going to want to pass this
to CFNotificationCenter at observer.
This allows you to observe for this notification.
The next thing you are going to want to do is you are going
to want to pass a call-back block.
This is the call-back block which is executed
when the system notices
that this notification has been emitted.
Then you want to pass the short string.
And finally, deliver immediately.
This makes sure that everything stays up-to-date
and the model inside of your extension is reloaded
when the containing app passes it this hint.
Now, remember I am calling it a hint so you probably don't want
to use this for coordinating locks between your extension
and your containing app because it's not always guaranteed
that your extension is going to be
around to receive these notifications.
So that's how to keep your data model up-to-date.
How do you keep your widgets visual
And next I would like to tell you about that.
We are going to use something called background refresh.
And background refresh is a way that the system works
with your widget to keep it up-to-date,
to keep the visual representation of it up-to-date.
So again, we are going back to the Notification Center,
and here you can see our stocks widget.
The stocks widget, again, shows the latest stock prices.
What happens if, in the course of the day, Apple stock changes?
Well, this notification will probably be emitted
to your phone, at which point the containing app will probably
be expected to do something with it.
And this will probably change the visual representation
of your widget.
Unfortunately, we will notice here that the widget is now
out of sync with the stock price.
It's showing $130.12.
It should be $132.12.
So how do we fix this?
How do we make sure that our visual representation is
Well, for starters, the system is going
to opportunistically refresh this content.
It's going to talk to the widgets and see if they need
to be updated without having to pull
down the Notification Center.
Each Today Widget view controller conforms
to NC widget providing.
And if the widget's view controller implements the update
delegate method, it gets to participate in this system,
and I will show you how to do that now.
The first thing we are going to do is we are going
to add widget perform update with completion handler
to our Today Widget view controller that conforms
to NC widget providing.
As a first step, we are going to refresh the model,
and this is going to do one thing for us,
one very important thing.
It's going to tell us whether or not there is a change
in the model that is going
to materially affect the widget's visual representation.
If it did, then we're going to tell the view hierarchy
that it needs to redraw.
And finally, inside of it, we are going
to take the completion handler that was passed to us,
and if there was a visual change,
we are going to pass new data.
If there was no material change that was going
to affect the visual representation of our widget,
then we are going to pass no data,
and this lets our widget be a good citizen
in the Notification Center because at this point,
we are hinting the operating system that it doesn't need
to expend any resources
to update the visual representation
of our Today Widget.
Since we've done this, our widget has been updated,
and you'll notice that the stock price is correct.
Extensions are ephemeral objects,
really ephemeral processes.
They come and they go.
Remember, I was talking about the user swiping down
and then quickly swiping up.
And this can play havoc with things that take a while
to work, like networking sessions.
And widgets are expected to call out to cloud services
and other things on the network in order to process data.
So how do we deal with this?
Well, I'd recommend that you take a look
at using NSURLSession background sessions.
And what are these?
These are tasks, networking tasks, that are performed
on your behalf by the system.
The updates and events are delivered to your extension
for as long as it stays active.
But if your extension gets suspended or killed
or for some reason hangs up the phone
with this critical system -- or with this system component,
then the containing application takes over error
and event handling on its behalf.
And I will show you how to set that up.
Inside of your extension you are going to want
to first create an NSURLSession configuration background session
configuration with identifier object.
Or sorry, use the method.
And, again, following in the pattern, you are going to want
to create an identifier that's going to be used
by the extension and the containing app
which are participating in this background session.
In our case, we are using com.example.my downloadsession.
The next thing you are going to want
to do is set the shared container identifier
on the session configuration.
Otherwise, you are going to have a lot
of fun debugging while all our sessions come back as invalid
as soon as you start a task.
At least I know I did.
Next, you are going to want to create an NSURLSession,
passing this session configuration
to the initializer.
Now, you will notice that we are using the delegate form
with the initializer because the completion handler form
of the initializer is not valid for use
with background URL sessions.
Next, for the sake of the example, we are going
to download the apple.com website.
We are going to create the task and resume it.
And now our extension has successfully created an
NSURLSession in the background and handed it off to the system,
and the system will begin to deliver events to the extension.
Next, inside of our containing app, as I was talking
about before, we need to prepare it for any eventualities,
for cases where the extension was suspended or killed
or otherwise dismissed and can no longer handle the events
coming from the system networking component.
We do this by adding application, handle events
for background URL session to our UI application delegate.
The first step we want to take
and the step I am taking here again, for the sake of example,
is to make sure the identifier is the identifier
that we expected.
The system is going to pass in the identifier associated
with the NSURLSession for the events that you are being asked
to handle in the application delegate.
Again, just like in the extension, we are going to set
up an NSURLSession configuration item
by calling the background session configuration
with identifier factory method.
And we need to set up the shared container identifier again.
And then we need to call NSURLSession,
passing this shared configuration, and again,
using the delegate variant of the initializer.
And then last but not least -- and this is very important --
you'll miss it if you don't look --
is we are going to store this completion handler back.
We are going to save it back in our properties
for when we're completed,
when we are done handling events for this URL session.
And you might ask, how do you know
when you are done handling events?
Well, I will show you.
In our NSURLSession delegate, we need to add this method.
URL session did finish events for background URL session.
It will be called when your containing app,
or in this case the delegate
that your containing app dutifully appointed to take care
of these events, is done processing all the URL session
events from the background URL session.
And the first thing we are going
to do is get the session configuration object,
and then we are going to look to make sure
that the session configuration's identifier matches the
identifier that we expect.
Then we are going to call this completion handler
that we saved earlier
and release our reference to the session.
And at this point, we are done.
We have successfully handled any of the events that have come
to our containing app from the system component
which was previously started by the extension.
Now, you can use this technique for any
of your other extension types, not just Today Widgets.
It's a really great technique.
But one place where you can't use it is
for your Watch OS 1 extensions.
Instead, we would prefer
that you use background task assertions
to protect these small tasks, and you can find out more
about it by going to WatchKit Tips and Tricks
in the Presidio tomorrow at 10 a.m., given to you
by Jake Behrens, also known as the man in plaid.
Next, when you are doing network transactions, it's inevitable
that you are going to be challenged for credentials.
How do you safely and securely protect your users' privacy
by sharing these secrets between your containing app
and its extensions?
We recommend that you use Keychain access groups
for this, as shown here.
You set this up inside of the capabilities for your extension
and for your containing app.
And then, using the identifier that you set
up in your containing app and its extensions,
you simply pass this identifier as the value for the k --
access group key whenever you add items to the Keychain.
As shown here.
The next thing to tell you about this is
that there's automatic search behavior
for query API using the Keychain API.
So sec item update.
Sec item delete.
And sec item copy matching all automatically do the right thing
by searching every accessible Keychain for you
so you don't have to pass the Keychain access group identifier
to these APIs.
It's just a tip to remember.
And you can find out more about these APIs
by watching the Security and Your Apps video.
In summary, today Sophia told you all about Action
and Share extensions, about what they were meant to do
and how best to use them to share data
with your app and the web.
She also showed you how to use NS item provider
to its fullest possible extent and how
to make it do the hard work of sharing data types for you.
She also showed you how to make your host application a great
environment for Action and Share extensions, and you know what?
This increases the value of your host app.
Then I showed you how to enhance your Today Widget by making sure
that your model objects and its visual representation always
stayed fresh and up-to-date.
And then we showed you a collection
of general best practices, like how to securely share secrets
and guard your customers' privacy.
There are the related sessions I referenced earlier,
the WatchKit Tips and Tricks one tomorrow, and Security
and Your Apps that you can view.
And for more information, we'd recommend that you go
to the App Extension Programming Guide or talk
to our Evangelist, Curt Rothert.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.