Maintaining Your App’s Indexed Content - Extensions - iOS 9 Swift Programming Cookbook (2015)

iOS 9 Swift Programming Cookbook (2015)

Chapter 5. Extensions

5.3 Maintaining Your App’s Indexed Content

Problem

You want to know when iOS is about to delete your indexed items and you would like to be able to provide new content to the search index.

Solution

NOTE

This is an extension to the search capability explained in Recipe 6.1.

Add a Spotlight Index Extension to your app and update the index right in your extension (see Figure 5-8).

Figure 5-8. Adding a Spotlight Index Extension will allow us to re-index our app’s searchable content

Discussion

Every now and then, iOS has to clean up the search index on a device. When this happens, apps that have provided searchable content will be given a chance to reindex their items. To get started, create a spotlight index extension as shown in Figure 5-8. I’ve given mine the name of Reindex. It’s up to you what you want to name your extension. Now you will get a class called IndexRequestHandler in your extension. It offers two methods:

§ searchableIndex(_:reindexAllSearchableItemsWithAcknowledgementHandler:)

§ searchableIndex(_:reindexSearchableItemsWithIdentifiers:acknowledgementHandler:)

The first method gets called when you are asked to reindex all your previously indexed items. This can happen if the index is corrupted on the device and you are asked to reindex all of your content. The second method will be called on your extension if you have to index specific items with the given identifiers. You will be given a function called an acknowledgement handler to call when you are done indexing again.

NOTE

In both of these methods, the first parameter that you are given is an index into which you have to index your items. Use that index instead of the default index.

Here is an example. Let’s define a protocol that dictates how indexable items have to look like:

protocol Indexable{

var id: String {get set}

var title: String {get set}

var description: String {get set}

var url: NSURL? {get set}

var thumbnail: UIImage? {get set}

}

And then a structure that conforms to our protocol:

struct Indexed : Indexable{

//Indexable conformance

var id: String

var title: String

var description: String

var url: NSURL?

var thumbnail: UIImage?

}

Later on we are going to go through an array of Indexed instances, grab all the IDs, and put those in an array. Then, when we are asked by iOS to index certain items with given IDs, we can just find that ID in our array, and then find the associated indexed item using the ID. For this, we can use protocol extensions on sequence types. I wrote about this in Recipe 1.12.

extension SequenceType where Generator.Element : Indexable{

func allIds() -> [String]{

var ids = [String]()

for (_, v) in self.enumerate(){

ids.append(v.id)

}

return ids

}

}

And now the juicy part: our extension. We construct an array of indexed items:

lazy var indexedItems: [Indexed] = {

var items = [Indexed]()

for n in 1...10{

items.append(Indexed(id: "id \(n)", title: "Item \(n)",

description: "Description \(n)", url: nil, thumbnail: nil))

}

return items

}()

When we are asked to reindex all our items, we just go through this array and reindex them (see Recipe 6.1):

override func searchableIndex(searchableIndex: CSSearchableIndex,

reindexAllSearchableItemsWithAcknowledgementHandler

acknowledgementHandler: () -> Void) {

for _ in indexedItems{

//TODO: you can index the item here.

}

//call this handler once you are done

acknowledgementHandler()

}

When we are asked to reindex only specific items with given identifiers, we use our sequence type extension to find all the IDs of our indexed items. Then we search through these IDs for the IDs that iOS gave us. Should we find a match, we will reindex that item. Code for reindexing is not shown here, but Recipe 6.1) shows you how to do it.

override func searchableIndex(searchableIndex: CSSearchableIndex,

reindexSearchableItemsWithIdentifiers identifiers: [String],

acknowledgementHandler: () -> Void) {

//get all the identifiers strings that we have

let ourIds = indexedItems.allIds()

//go through the items that we have and look for the given id

var n = 0

for i in identifiers{

if let index = ourIds.indexOf(i){

let _ = indexedItems[index]

//TODO: reindex this item.

}

n++

}

acknowledgementHandler()

}

See Also