Making the Most of Search APIs

Search APIs make it easy for users to discover information in your app and the app itself. New features expand the search capabilities allowing users to search directly in your app and enable you to search your own app index. Learn about the latest API updates and the new privacy-friendly method for improving the ranking of your app content.

WWDC 2017

WWDC 2016

Welcome. My name is Vipul Ved Prakash.
I work on the Siri and Search at Apple, and today I'll be joined
by my colleague John Hornkvist, and we are going
to show you what's new in Search APIs
and how you can make the most of them.
As you know, Spotlight has been becoming a more powerful search
tool in recent releases of iOS.
In iOS 8 we introduced ability to search
through internet sources, like App Store, Maps and Wikipedia.

And then in iOS 9 we significantly increased the
scope and introduced sources like music,
currency conversions, live sports scores,
web, and much, much more.
Perhaps most importantly, we introduced simple
and powerful ways in which we could make your apps content
searchable through Spotlight.
So in iOS 10, today we are announcing extensions
of these Search APIs that will provide even deeper search
of your app's content.
We've also made Spotlight easier to use and easier to find.
So let me start by showing you some examples of things
that we've done to Spotlight.
Spotlight is now present in Notification Center,
which makes it a lot faster to get to.

Let's look at an example here.
User receives a message with a question in it.
Very typical thing that happens.

And they want to run a search to answer it.
Instead of having to quit messages and go to Home screen,
pull down Spotlight, they can simply drag Spotlight on top
of messages, run their search and find the answer.
It's super convenient.
We've also introduced Spotlight on Lock screen and added support
to quickly preview results with 3D Touch.
That's see what this looks like.
Suppose you're having an argument with a friend
on where Steph Curry was born.
You can go to the Lock screen,
run your search,
see the result, and now you can use 3D Touch to preview these --
and find the one you were looking for,
which is Wikipedia in this case.

Really, really convenient.
And, of course, when you're in Lock screen, the only results
that you present are public results from the internet.

Now, another neat feature that we've added
to Spotlight is query suggestions, which appear
in the quick type area above the keyword
when you type in the query.
Let's look at an example.
Here the user searches for banjo, and we know,
based on past queries, that two common completions
for banjo are banjo chords and banjo tuner.
So if the result that they are looking for is not already
on the screen, they can pick one of these and get to the answer.
This will make a lot of queries a lot faster
to execute in Spotlight.

Now, we've done many more improvements.
We've improved spell correction for the app launch use case.
We've improved support for Japanese
and Chinese scripts on device.
And we've made relevant, in general, much better.
And all these changes, of course,
apply to content that's inserted while Search APIs.
So now let's turn our attention to Search APIs.
Now, as many of you may remember,
we introduced three Search APIs in iOS 9.
There's CoreSpotlight, which allows you
to insert user specific or user created content right
on Spotlight's device index.
We also introduced NSUserActivity,
a searchable version of this, that provides an easy way
to index everything that the user has seen inside of your app
so that they can search for it and get back to it easily.
And finally, we introduced a Universal Links index,
which is an index on the server side consisting of links
that we have discovered by crawling app websites.
And we designed these APIs to work in concert,
so whether the results are coming from the device
or the server, they blend together
to provide a seamless experience of searching your app.

I'm happy to announce that over 50,000 apps
in the App Store have an app that Search APIs.
It's really incredible.

I think something really neat to see is how the users
who Search APIs have improved day to day interactions
that our customers are having
with your app and iOS as a whole.
I have some of my favorite examples
that I'd like to show you.
This is actually a recent example.
I'm planning a trip to Maui, Hawaii, this weekend,
and I've been planning this trip over the last month on and off.
Now, various pieces of information
like hotel reservations and tickets, they are somewhere
on my device, and typically the way I find them is I go
to the app or the website,
or dig through the e-mail to find them.

But with Search APIs, this is what my experience looks like.
The apps that I've used to make these reservations,
have added salient pieces of information
to Spotlight's index using CoreSpotlight.
So I can see my flight ticket, I can see the hotel that I booked
through Hotel Tonight,
and a couple dinner reservations I made on Open Table.
I can find more information by tapping on these results,
like what is my check-in time
or address of one of these restaurants.
And you'll also notice
that I have some results here from Pinterest.

I've been using the Pinterest app
to explore the things I can do when I'm in Maui.
Now, Pinterest uses NSUserActivity
to index everything that I have looked at inside of the app,
and I can tap on one of these results.
And continue my exploration.

So this is a really fantastic experience
for doing travel search and to find all the information
that I know exists on my device.

And what we've seen is, in general, indexing content
that your users want to get back to works really, really well.
Another example I want to share is around contacts.

Contacts is one of the most popular search use cases
in Spotlight.
Here I'm searching for buddy Allen.

I see his contacts, which is a native iPhone contact,
but I also see his Skype profile and a couple of conversations
that I'm having with him in Yahoo Mail and WhatsApp,
and I can easily get back to these conversations.
And this is so powerful because it allows me to get this sort
of centralized view of people that I know, and allows me
to have conversations with them in the channel of their choice.
Now, what we have seen is that users are naturally going
to Spotlight for all the things
that Spotlight supports natively,
and if you have content in these categories, it's a great way
to broaden that search experience,
as well as drive engagements to your app.
Now, finally, let's look at an example
of Universal Links index.

wikiHow is a high quality how-to resource that has hundreds
of thousands of articles on a variety of subjects.
Here I'm searching for a critical first aid question,
how to perform CPR, and Spotlight presents a bevy
of results from web, YouTube, as well as a section from wikiHow.
Now, wikiHow uses Universal Links
and they have a popular app.
So we have indexed their articles on the server
and we present them for appropriate queries.

It's pretty comprehensive results that by clicking on one
of these results, I go to this very clearly
illustrated article.

The index of wikiHow has really added a whole new capability
to Spotlight, which is what makes Search APIs so powerful.
With that, let's start looking at what's new
in Search APIs in iOS 10.
The first feature we are adding is called Continue Search
in App.

And for a lot of queries users want to look
at a larger result set, or you may not have been able
to add the right result to Spotlight for various reasons.

So for now apps have an option to present a search in App Punch
Out on the top right corner
of their section, as you can see here.

And tapping on this takes the user into your app
with the search query.
So you can continue the search query inside of the app.

It's super cool.
The second feature we are introducing is CoreSpotlight
Search API.

It's a slightly overloaded name, but there what's happening is
that you're adding all these items
to Spotlight's search index by CoreSpotlight API.

Wouldn't it be awesome if you could use this index
to power search inside your own app?
I think it would be.

And this is exactly what CoreSpotlight Search API allows
you to do.
The third feature we are introducing is a way
to estimate popularity of deep links using differential
privacy, the technology that was mentioned in the keynote.
We will discuss this more later when we talk about ranking.

And finally, by popular request we've added a feature
to our web markup preview tool
that lets you visually inspect your schematized results before
you put them in a Universal Links index.
And in addition to these we made many, many enhancements
to Search APIs based on your feedback.

And now I will invite John to do an introduction of these APIs
and do deeper dives into these new features.
Let's start talking about how to leverage the Search APIs.
In the next half hour I'm going to cover some things
that you have to do for your application to work great
with Spotlight, some things you'd want to do
to give users a great experience, and some new APIs
that we've added to make it easier for you
to accomplish what you need.
So roughly in the order that you need to implement this,
I'm going to talk about getting your content
into the index available to Spotlight
and keeping it up to date.
Presentation and user experience,
launching into your app, whether for restoring content
or for search continuation, and then I'll talk
about the new Search API before Vipul comes back
to give you an overview ranking.
So we have three technologies
that together cover most use cases.

CoreSpotlight, for all that you have on the device.
NSUserActivity for app history.
And Universal Links with web markup
for public online content.
Of course, you can use all these three together.
As an example, consider a recipe application.

It provides an interface to a vast collection of recipes.
There are hosts on the website, so deep links are a great fit.
The app may also have a favorites feature,
and for that you would use CoreSpotlight.
Your users will want to get back to content
that they've looked at,
so for the best experience you use app history
through NSUserActivity as well.
All right.

For those of you who haven't gone off to lunch,
instead of conducting Search APIs, let's dive in and talk
about indexing content, and we'll start with CoreSpotlight.

CoreSpotlight is an API for indexing on iOS.
It's on device and supports file protection.
So you can index the user's private content.

Your app is in charge and you decide what you want
to add to the index.
You can index all that your app has to offer, be it favorites
and bookmarks, messages and e-mails, documents, images,
music, videos, game levels, jump-off points
in your app, and much more.

So there are two basic operations.
First you need to add items to the index.
To do this you create a CSSearchableItemAttributeSet.

This contains the metadata and other content for your items.
Then you set at least one attribute.
In this case we're setting the display name.

You create a searchable item using the AttributeSet.
You use the uniqueIdentifier, which Spotlight will use
to identify this item for any future operations
and which is also used when we launch your application later.
And the domainIdentifier, which allows you to set
and shared property across many items
which can later use for deletion.
And then you ask CoreSpotlight to add the item to the index.
When your callback is called,
the item is then safely committed to storage
or an error will be passed back to the callback log.
For deleting content there are three APIs.

You can delete a specific item by its identifier.
For example, if the user deletes a document.
You can also delete groups of items by the domainIdentifier
that I mentioned earlier.
This is great if the user signs out of an account,
ends a subscription, or something of that sort.

Finally, you can delete all content for your application.
This is useful if you have something
like an incompatible version change and you need
to clear the index and start from scratch.
And this is also called by the system
if your app gets uninstalled.

Now let's go over some best practices
and some more advanced scenarios.
We'll cover registering as an index delegate,
using CoreSpotlight client state to handle progressive indexing,
some performance considerations
and creating a CoreSpotlight extension.

You want to register as an index delegate
because this lets Spotlight initiate indexing
when your app is first launched on the system,
perhaps after restoring a backup,
when the user installs your app, and sometimes
for disaster recovery.

It also lets Spotlight reach out and request
that you reindex a particular item.
This is commonly because you've set an expiration date
and Spotlight wants to check that the item has truly expired.
So to be an index delegate you need
to implement the CSSearchableIndexDelegate
protocol.
This has two required methods.
ReindexAllSearchableItems, which is called when you need
to add everything to the index.
If you track indexing of individual items
in your own database, you want to clear the indexing state
when this call is received,
unless you're using client state,
as we'll discuss in a moment.

The second call is reindex items with identifiers.
When this is called, you should look up items
that Spotlight is requesting and add them to the index
or delete them as appropriate.
For both methods you call the acknowledgmentHandler only
when you're completely done and receive the last callback
for any work you issued to CoreSpotlight.
This ensures that we know that your content is fully indexed
and that we don't have to call you in the future.

If you don't call this, we might call you again,
and if you call it early,
you might not get a chance to finish indexing.

For some applications we found that it's more convenient
to use client state than to try to manage the indexing callbacks
with your own database transactions.

The client state provides an asynchronous way
of keeping your content in Spotlight in sync.
Because it's asynchronous you essentially have an eventual
consistency model, but you can keep Spotlight up to date
with your own database without any redundant work.
The client state is an opaque token that is stored
in the Spotlight index that you update as you index
and then fetch back when your app is launched again.
Typically, the easiest way to do this is to put annotations
in your own database, for example, the sequence number
that you then as a client state with Spotlight.
After relaunch you would check this client state
and if it doesn't match with your expectations,
you would index any item in your database
with a sequence number higher
than the one you fetched back to Spotlight.
Another approach is to use the sequence number as a way
of knowing where to start the journal playback.

This can be really, really good for keeping your content
up to date, asynchronously, and for making sure
that you don't use too much power.

So to work with client state you need
to create a named index instance.
The name lets us know what client state you want.

You can only get the client state for your own application,
but you may have more than one database that you're indexing
and then you would use a separate name
for each of those databases.
With the index you begin an index batch.
You'd add searchable items as usual.

The completionHandler is not particularly important here
because you'll get a completion when you finish the batch.
You compute your picked state and you pass that to Spotlight
when you finish the batch.
And here you do need to pay attention
to the completionHandler.

When your app next launches, you fetch the client state,
and you figure out what operations you need to run
to bring Spotlight in sync with your state.

Because the client state is kept with the batch
into the index, it ensures integrity.
You can replay exactly operations that you need
to bring them both into sync.
So when your app next starts,
you create an index instance using the same name,
you fetch the client state.
And this is an asynchronous call.
So in the callback you deal with any errors
and check whether the state you got back is what you desired.
If not, you call the method to bring them up to date.
Now let's talk about some performance considerations.

Spotlight is really, really fast so you want to minimize overhead
on your side to make sure your app can keep up.
You want to optimize any access to files or databases,
and pay careful attention to your memory use.
Do notice that each call to CoreSpotlight has a cost,
so pass batches of items instead of single items when possible.

And just making batches as small
as ten items will still reduce the IPC overhead
by an order of magnitude.

Since your app will be performing indexing while the
user is using it, make sure that you don't block the main thread.
And finally, to avoid interfering with UI run
on a background thread.
All right.
Let us talk about CoreSpotlight extensions.

The extension can index when your app isn't running.
This lets you catch up after a disaster recovers in backup,
or when your icons expire.

Spotlight can reach out to your extension instead of reaching
out to your application, which is great
because your application might not be running.

The interface to the extension is the same
as for the index delegate.
So if you can factor your code
so that the index delegate is separate, it's really easy
to implement the extension as well.
To make content available
to your extension you can use share.groups.
To find out more about this,
take a look at last year's session
on App Extension Best Practices.
Next let's look at keeping content up to date.
As I mentioned, you can use expiration dates
to keep stale content from accumulating in the index.
CoreSpotlight will call your app around the expiration date
and you can update the expiration time
or update the data for the item if you need to.
If you need to get new content into Spotlight,
you can use background fetch.

This will allow your app to launch in the background,
letting you take care of adding content and getting it indexed.
Finally, if you have more of a push model with irregular
or infrequent updates, you can use silent remote notifications
to let your server tell you that updating is necessary.
To find out more about using background fetch
and silent remote notification take a look at the What's New
with Multitasking session from WWDC 2013.
That's it for indexing with CoreSpotlight.

Next let's talk about app history with NSUserActivity.
NSUserActivity was introduced for Handoff in iOS 8.
It lets you create a representation
of your application's current state that can be passed
to another device, and since iOS 9 stored
in the Spotlight index for app history.

You submit the activities as the user is browsing in your app
for content that the user may remember
and want to get back to.

So the question you want to ask yourself
about when the user activity should be indexed is simply will
the user want to get back to this.

If the same item might be indexed with CoreSpotlight,
then the answer is almost always yes.
The 2014 session on Adopting Handoff has great information
on how to use NSUserActivity.
To make NSUserActivity available for search you need to mark it
as searchable and add index
of the metadata using CSSearchableItemAttributeSet.
You mark it as searchable
by setting the eligibleForSearch property to true.

You can also mark NSUserActivity as eligible for public indexing
to make it a candidate for the online index,
as Vipul will get into a bit later.

This done, it will show up as a result in Spotlight Search
and your application can revisit the user activity
when the user selects it in Spotlight.

Your users just have to remember a single keyword
from what they saw to be able to get right back
to the content in your application.

All right.
So, as I said, you can use CoreSpotlight and NSUserActivity
for the very same content.

The difference is
that NSUserActivity reflects what the user has done
in your application.

CoreSpotlight is about what your app has.
So if you use both, you can relate NSUserActivity
to the CSSearchableItem for the same content to help ranking
and avoid duplication of the results.
This is done by setting the relatedUniqueIdentifier property
in the AttributeSet to the uniqueIdentifier
of the CSSearchableItem that you want to relate it to.
This also ties the lifetime of the NSUserActivity
to the CSSearchableItem, protecting you
against leaving data on the device
after the user has deleted private content.
However, not all data is private and managed by the user.

For example, for the recipe application that we talked
about earlier, you may want to relate NSUserActivity
to a possible CSSearchableItem, an item that doesn't exist yet,
something that the user might make a favorite in the future.
If you were to use the relatedUniqueIdentifier,
CoreSpotlight would immediately delete the NSUserActivity
as you try to add it because the related item doesn't exist yet.
To solve this, we're adding a new property,
weakRelatedUniqueIdentifier which lets you bind weakly
to the CSSearchableItem.
It can exist before the CSSearchableItem and will remain
when the CSSearchableItem is deleted.

As for the relatedUniqueIdentifier,
you simply have to set the property in the AttributeSet
for the NSUserActivity.

Now, the downside is that when the CSSearchableItem is deleted,
the NSUserActivity remains.
So if you have concerns about the lifetime
of the searchable item or the NSUserActivity,
you do need to delete NSUserActivity yourself.
Fortunately, in iOS 10 we're making this possible.

We're adding domainIdentifiers to NSUserActivities.
It's part of the CSSearchableItemAttributeSet
and it allows you to delete NSUserActivities
by the domainIdentifier,
just like you can for CSSearchableItem.
If you use both CSSearchableItems
and NSUserActivities, it's a good idea
to use the same domainIdentifier.
So that's indexing with NSUserActivity in iOS 10.

Next let's talk about Universal Links and web markup.
The content driving your app may live on the web,
not locally inside the app.

If this content is public, you can use web markup
to make a searchable
for Spotlight via the Universal Link index.

This is perfect for content hosted on the website
and available in your app, and a great solution
when your content is too large to fit on the device.

Because your content has a presence on the web,
results can be displayed to users that don't have your app,
which lets you reach new users and drive app installs.

Finally, these results can be shown in both Spotlight
and Safari, which makes your content available
to even more users.

So to implement indexing
with Universal Links you need to allow indexing.
Allow Applebot to call your website [inaudible] text,
and let Apple Note into the site.
Specify the call URL
when submitting the app to the App Store.

For deep links we strongly recommend Universal Links.
For this you need to implement dual authentication
for the website and the app.

Implement the continueUserActivity method
in your app delegate to ensure that your app handles deep links
when the user selects the results.

Markup your content with schema.org or Open Graph
to provide a rich display for attributes in your content.
And use the Search API validation tool
to test deep links, markup, title, description, and more.
Take a look at the Introducing Search APIs Session from 2015
and the Developer documentation on Universal Links.

So these are the schemas that we support today,
and we plan some more in the future.
Pay attention to interaction count and aggregate rating.

These are very useful for ranking results for your app.
Apple provides a test tool at search.developer.apple.com,
that you should consult
if you implement the deep links for your app.
It now displays a visual representation of your result,
including supported schema [inaudible] markup.

The information from the validation tool can help you
visualize pieces of information
that the Applebot web crawler has indexed,
including the title, description, markup and URL.
Now you've seen how to index content using our three APIs,
and there's a good chance that you'll want
to use them all together even for the same content.
So the most important thing to remember
when using multiple APIs is
to link items representing the same content together
across the APIs.
By setting NSUserActivity's relatedUniqueIdentifier
to CoreSpotlight's uniqueIdentifier,
CoreSpotlight's content URL, and NSUserActivity's webpage URL,
to the URL of the webpage, you tell Spotlight that all records
in the index represent the same item,
which allows search to de-duplicate.
It also provides strength to the ranking of items.

All right.
Now we've covered three ways of getting content into the index;
CoreSpotlight for content, NSUserActivity for app history,
and Universal Links with web markup
for public content available through the web.
Next let's talk about how
to present this information to the user.
To get a great presentation in Spotlight you want
to set a good thumbnail.

By default Spotlight will use your app icon
which makes it hard to distinguish results at a glance.
This doesn't matter much if you're just going
to get a single result, but if you get multiple,
it makes a big difference.
Just as important as the thumbnail,
and perhaps more, is the title.
A good title is not just great visually,
it's also what users most frequently search on.

After the thumbnail and the title you'll want
to set other fields that are suitable for your content.
A description is great when available, as is rating,
rating description, date attributes for items
that are time bound, such as travel reservations, dates,
reminders, events, and so forth.

For documents, general metadata, such as file size
and page count, are helpful as well.
If you set the right content type for your content,
Spotlight can also do a better job of displaying it.
Let's look at some examples.
The Hotel Tonight app makes good use of the thumbnail,
showing an easily identifiable landmark, as well as a title
and informative description.
You can get the same with web markup by setting the og:image,
og:title and og:description.
Open Table uses the title description, the rating,
with rating description attributes,
to let the user get great information
to choose the right result before jumping into the app.
In making the same attributes available in web markup,
you ensure that the user will get a consistent experience
whether they're getting the results from the web crawler
or from the local device index.

A great user experience is not just about the presentation,
but also about what data you make available for search.
Setting attributes that the user can understand
and remember makes your content quickly accessible.
Conversely, setting misleading attributes in metadata
or stuffing content and keywords
with dictionary words will cause your results to show
up frequently, but rarely be selected,
which will annoy the user
and have a strong negative affect on your ranking.
Another aspect of a great user experience is being able to get
to the salient part of the result
in as few steps as possible.
Enabling quick actions, like directions and calling,
has significant value for your users.

Finally, when a user selects an item,
you want to launch directly to it as quickly as you can
and without interstitials
or multistep builds that impede the user.
Let's look at some examples.
Redfin's app provides attractive looking results,
and by setting the latitude and longitude attributes
and enabling navigation, the app lets the user punch out directly
to Maps to go look at a property.

Similarly, here is a great use of the call action result.
You can get the same by setting the phone number's property
and support phone call properties for Spotlight.

On seeing the result, a user that already is familiar
with the location can immediately call
and reserve a table.

For web markup you can accomplish the same
by using the postal address and telephone schemas.
Next let's talk about launching your application.

For both CoreSpotlight and app history we use NSUserActivity
to restore the state.
Your app delegate will get called
with continueUserActivity.
You examine the NSUserActivity's activity type
and the user info dictionary if necessary.

If you're being launched because the user selected the
CSSearchableItem in Spotlight,
the activity type will be CSSearchableItemActionType,
and you retrieve the identifier from the user info dictionary
by using CSSearchable ItemActivityIdentifier.
The 2014 session Introducing Handoff goes into further detail
on how to launch NSUserActivity's review
and activity type.
For Universal Links, once again, we use NSUserActivity.

So your app delegate will get called
with continueUserActivity.
As usual, you examine the NSUserActivity's activity type,
which will be NSUserActivityTypeBrowsingWeb.
You parse the URL and take the user
to the part indicated by the URL.

That's launching.
Now let's look at a new feature.
In iOS 10 we've added a feature to allow the user
to continue search right in your app.
Conduct human results for Spotlight
to display will show an affordance, and you should opt
for Spotlight Search continuation
so the user can go directly to the app.
If you already support search, it's trivial to adopt.

It lets you leverage your customized search interface
and has been widely adopted by our internal apps.
It's another great way of getting increased engagement
with your application.
The user is taken directly from Spotlight
into the search experience that you already have.

Now
Thank you.

To support this in your own application you need
to add a key to your info.plist which declares
that you support the feature.

Your app delegate will get called with a new activity type,
CSQueryContinuationActionType,
and the new query string will be passed in the user info
and a CSSearchQueryString key.
At this point you can invoke your own search UI
with the same query string,
letting the user continue the search in your app.
To avoid confusion, it's usually a good idea to make sure
that your search results are somewhat consistent
with Spotlight.
Since Spotlight is based on prefix search, we recommend
that you use similar search rules.

And if you can't support prefix search, consider taking the user
to a completion interface.
Another way of ensuring consistent with Spotlight is
to use CoreSpotlight's own Search API,
which is the approach taken by many of our own internal apps.
CoreSpotlight Search API makes it easy for you
to implement search of your own data.
It uses the data that you already provided CoreSpotlight
for Spotlight Search.

It writes consistency with the rest of the US,
and is already used by many of our own applications,
including Mail, Messages and Notes.

It avoids overhead so you don't have
to maintain an additional search index,
as it's the same index used by Spotlight.

The index provides full metadata and content search.
If all your content is on the device,
CoreSpotlight can be a complete solution for search.

If you have a mixture of on-device and online content,
you can combine queries and merger cells,
getting responsiveness from CoreSpotlight on the device
and completeness from your online index.
Mail uses CoreSpotlight not just for search,
but also to create search suggestions
and to make complex queries.
This is a great way to take advantage of the power
of CoreSpotlight Search while keeping the user
interface simple.
A CoreSpotlight Search API has created data
that you've given Spotlight, but your data is protected
from other applications.
The query engine is fast and scalable
so you get excellent responsiveness in your app.

The query syntax allows complex, full inquiries,
as well as range searches, numerical and date searches,
and a set of powerful text message features.

For those of you that are familiar
with the metadata framework on Mac OS,
this syntax will feel very familiar.

Here's an overview of the most commonly used search operations.
As you can see, CoreSpotlight supports a full range
of search comparators, as well
as the Boolean operators AND, OR and NOT.
In addition, CoreSpotlight lets you customize string matching
to suit your needs.

If you want case insensitive search, you add the c flag.
If you want to ignore diacritics, such as umlauts,
if they are not important
in the current language, you use the d flag.
And if you want to match on words within a field instead
of anchoring your searches at the beginning of the field,
you specify the w flag.
This, by the way, is implied for text content.
And if you want multiple words to be dealt with individually,
then you pass the t flag and the query string will be tokenized.
So let's look at an example.
We're implementing a search function
that takes the user query as input.
We make sure to cancel any currently running query
so that we don't have multiple queries running concurrently,
as this will slow down the new query.
Because we're dealing with user input, we make sure
to escape the query string.

We use the double star syntax to create a query that will match
on either content or metadata.
The escape user input is tasked as a search string,
and we had the cdw and t operators,
which creates a case insensitive, locale aware,
diacritics insensitive, word matching, tokenized search.

With a query string we create a query object.
We request the display name to be fetched,
and this will be available in the AttributeSet
of the searchable items returned.
We set the foundItemsHandler, which will receive batches
of searchable items if there are no results for your query.

And a completionHandler will get called once,
either with an error or when the query has finished successfully.
In our completionHandler we can opt it
to display finish any processing, and so forth.
And all that remains is to start the query.
CoreSpotlight will call the handlers with results
and then call the completionHandler.
So let's put this into practice.
We've built an application.

There is a simple picture gathering.
It already supports indexing in CoreSpotlight
and it already supports Search.

So the first thing we're going to do is to add support
for Continue Search in App.
This is very simple.

We go to the app's info.plist
and we enable CoreSpotlightContinuation.
That done, we go to the app delegate
and in our user activity continuationHandler we add
support for Continue Search in App.
As you can see, the activity type is a
CSQueryContinuationActionType and we get the search query
by inspecting the user info for the CSSearchQueryString.
We can then activate our view controller
with the search query.
So let's see what this looks like.
All right.

Here we have our picture gathering, and we can pull
down Spotlight and search for the word "snow," which happens
to be popular in this gallery.

So we get two results and a search in app continuation.
So click search in app and I'm taken into my application.
Now you might observe that I had two results in Spotlight,
but only one result in my app,
and this is because I'm not using CoreSpotlight Search.

The search is a simple prefix search.
So let's fix that.
In our view controller we have a search method.

I'm going to remove the simple search implementation
that we already had and start implementing CoreSpotlight.
First we want to add a variable.

A query object that will keep the current search query.
We want to cancel the currently running query,
escape the query string and create --
create a query object and, of course, create a query string.
Oops.
All right.

So now we have a query string, a query, and then we need
to set the foundItemsHandler.
The foundItemsHandler creates displayables and appends those
to our list of results.
Now, since the query's getting CSSearchableItems back,
I need to implement an adapter for taking my CSSearchableItems
and creating something that I can actually display.
So let's look at what that would look like.
So I have what I call a lazy picture object.

What this does is it gets the data
that our query made available to us and uses
that whenever possible.

When that's not enough, it goes to our database
and gets the picture object for this identifier,
stores that away for further use, and then returns that,
and that lets it return all the other properties
that are not available from the database.
By doing this lazily we ensure that we can display our results
without having to go back to our database,
which is great for performance.
Let's implement our completionHandler.

In the completionHandler we sort the results,
jump over to the main queue to make the results displayable,
and then we call our table view update.

With that, all that remains is to call the start.
And let's see what that looks like.
All right.

Now since this is using CoreSpotlight Search
and I've made the query very forgiving, I should be able
to type any word that I see here, for example, river,
and I find anything that has river either
in the metadata or in the content.
All right.

That is how easy it is
to implement search with CoreSpotlight.
And with that, I'd like to invite Vipul back
to talk about ranking.
Thanks, John.

That was a fantastic overview of Search APIs.
As you are thinking about implementing these APIs
in your app, it helps to know how Spotlight ranks result
and how you can positively influence ranking, so let's look
at ranking in Spotlight.
Now, Spotlight's goal's prime directive is
to present the best results for your query in order,
and this order is determined with a set of factors.
The two most important factors are engagement ratio
and content popularity.
Let's talk about engagement ratio first.
Engagement ratio really is a measure of how often results
from your apps are being selected by the user
when they are presented,
and Spotlight maintains three flavors
of these engagement ratios.
One is maintained on the device and two on the server.
On the device the engagement ratio measures essentially
users' personalized interactions with your app.
The server will maintain an engagement ratio per query,
which is based on every query
for which your results have been shown in the past,
and using this Spotlight can uprank or downrank results
for a particular query.

And then finally there's a global engagement ratio that's
based on all interaction of all users that have happened in past
with your app, and this is used
when there's no query level engagement ratio available.
A couple important things to remember here.
One, you don't get penalized if the results
for a particular query have not been shown to the user
and they're sort of under the fold, under the keyboard.
The best practice here really is to use keywords, titles,
descriptions that really clearly describe the content
that you're indexing and will take the user
to the same content.

The second important thing is content popularity.
In general, items that are more popular,
tend to be ranked higher.

And Spotlight can understand the popularity
of items in three ways.
If you use Universal Links, they will automatically go and look
at the structure of the web and pull
out a reputation for your link.
You don't have to do anything there.

All you have to do is implement Universal Links.
If an item has an associated NSUserActivity,
Spotlight can track how often the user is viewing this item
on the device and then use that in ranking.
Now, if you use both Universal Links
and public NSUserActivities, which are eligible
for public indexing, iOS 10 can now estimate how often the
entire iOS population has viewed a link in your app,
and this is used in ranking.

And this is done for the new provision
of differential privacy.
Let me show you how that works.

So in this example Yelp has adopted NSUserActivities
that are eligible for public indexing,
as well as Universal Links.

When a user encounters the deep link from Yelp,
iOS computer hash adds some noise to it and takes a fragment
and then sends this fragment to Apple servers.

And by itself this fragment is useless.
It doesn't contain any information.
But once thousands of users have reported lots of fragments,
Apple servers are able to recover hashes
that have been viewed thousands of times without knowing
which users reported which hash fragments.

So this really provides a way to determine something
like aggregate behavior of users without doing anything
about individual behavior of any user.

Once popular deep links are discovered in this way,
they start getting ranked higher in Spotlight.
So this is very cool.

So let's have a quick look at best practices for ranking.
John showed many best practices
for indexing your app's content well with CoreSpotlight.

You should follow these because they will provide a consistent
search experience for your app's content, and then users will go
to Spotlight to search for stuff in your app,
which will then impact engagement ratios.
If you have app content, allow Apple to index them
by using Universal Links and this will expose your app
to users that don't yet have it.
So you know content popularity is important,
so link both Universal Links
and CoreSpotlight items to NSUserActivities.
We introduced weakRelatedUniqueIdentifier this
year to help you do that more easily.

And also, I'll repeat this because absolutely important
that the title, description and keywords
of items you're indexing are related to the deep link
that that item would go to.
Pay attention to your presentation.
We know that well-presented links have higher
engagement rates.
And finally, when appropriate, implement Continue Search in App
as it allows the user
to complete a search task inside of your app.
So in conclusion, key takeaways,
I think Spotlight is the new universal search for iOS,
and in iOS 10 we made it more functional and more accessible.
And users are increasingly expecting well behaved apps
to be searchable via Spotlight.

So if you haven't adopted these APIs, we would recommend
that you consider doing so for the general release of iOS 10.
And if you've already implemented Search APIs,
then we would recommend adopting new provisions
that have been introduced today.
Some related sessions, an excellent session
on Proactive Suggestions tomorrow that a lot
of you should go to because what you're doing
in search applies pretty much directly
to Proactive Suggestions.
We also had a fantastic session yesterday on SiriKit APIs.
You can find this on the conference website.

And we recommend the session on privacy from Wednesday
that covers differential privacy in more detail.
And like all other talks, this will be posted online
at the first URL, and we also post all Search API
documentation on search.developer.apple.com.
And that's all I have.

Enjoy the rest of the WWDC.

Looking for something specific? Enter a topic above and jump straight to the good stuff.