iOS 9 Swift Programming Cookbook (2015)
Chapter 6. Web and Search
iOS 9 brings with it some really exciting functionality, such as indexing contents inside your app as searchable content on an iOS device. Even better, you can contribute to iOS’s public search index so that your searchable content appears on devices that don’t even have your app installed. That’s pretty cool, don’t you agree? In this chapter we’ll have a look at all these great features.
6.1 Making Your App’s Content Searchable
Problem
You want the user to be able to search within the contents inside your app, from iOS’s search functionality (see Figure 6-1).
Figure 6-1. iOS 9 has improved search functionality
Solution
First construct an object of type CSSearchableItemAttributeSet. This will represent the metadata for any one item that you want to index in the search. Having the metadata, construct an instance of the CSSearchableItem class with your metadata and expiration date, plus some other properties that you will see soon. Index an item using the CSSearchableIndex class. You’ll get a completion block that will let you know whether things went well or not.
Discussion
You have to keep quite a few things in mind when indexing items in the local device search functionality. I’ll walk you through them one by one. Always keep this index in a useful state. Don’t index stuff that you don’t need, and make sure you delete the old items. You can specify an expiration date for your content, so I suggest that you always do that.
Let’s look at an example. We will start off by including the two required frameworks that we are going to use and a handy extension:
import CoreSpotlight
import MobileCoreServices
extension String{
func toFoundationString() -> NSString{
return NSString(string: self)
}
}
Then we will proceed to deleting all existing indexed items using the deleteAllSearchableItemsWithCompletionHandler(_:) method of the CSSearchableIndex class. This method takes in a closure that gives you an optional error. Check this error if you want to find out whether something went wrong:
//delete the existing indexed items
CSSearchableIndex.defaultSearchableIndex()
.deleteAllSearchableItemsWithCompletionHandler {err in
if let err = err{
print("Error in deleting \(err)")
}
}
Now let’s instantiate our metadata of type CSSearchableItemAttributeSet and give it a title, description, path and URL, keywords, and a thumbnail:
let attr = CSSearchableItemAttributeSet(
itemContentType: kUTTypeText as String)
attr.title = "My item"
attr.contentDescription = "My description"
attr.path = "http://reddit.com"
attr.contentURL = NSURL(string: attr.path!)!
attr.keywords = ["reddit", "subreddit", "today", "i", "learned"]
if let url = NSBundle(forClass: self.dynamicType)
.URLForResource("Icon", withExtension: "png"){
attr.thumbnailData = NSData(contentsOfURL: url)
}
Then let’s create the actual searchable item of type CSSearchableItem and set its expiration data 20 seconds into the future.
//searchable item
let item = CSSearchableItem(
uniqueIdentifier: attr.contentURL!.absoluteString,
domainIdentifier: nil, attributeSet: attr)
let cal = NSCalendar.currentCalendar()
//our content expires in 20 seconds
item.expirationDate = cal.dateFromComponents(cal
.componentsInTimeZone(cal.timeZone, fromDate:
NSDate().dateByAddingTimeInterval(20)))
Last but not least, use the indexSearchableItems(_:) method of the CSSearchableIndex class to index the item that we just created. You can index an array of items, but we have just one item, so let’s index that for now:
//now index the item
CSSearchableIndex.defaultSearchableIndex()
.indexSearchableItems([item]) {err in
guard err == nil else{
print("Error occurred \(err!)")
return
}
print("We successfully indexed the item. Will expire in 20 seconds")
}
When the user taps on your item in the results list, your app will be opened and iOS will call the application(_:continueUserActivity:restorationHandler:) method on your app delegate. In this method, you have to do a few things:
1. Check the activity type that is given to you and make sure it is CSSearchableItemActionType. The aforementioned method gets called under various circumstances, for example, with HandOff, so we have to make sure we are responding only to activities that concern indexed items.
2. Check the userInfo property of the activity and read the value of the CSSearchableItemActivityIdentifier key from it. This should be the identifier for your indexed item.
func application(application: UIApplication,
continueUserActivity userActivity: NSUserActivity,
restorationHandler: ([AnyObject]?) -> Void) -> Bool {
guard userActivity.activityType == CSSearchableItemActionType,
let userInfo = userActivity.userInfo,
let id = userInfo[CSSearchableItemActivityIdentifier
.toFoundationString()] as? String
else{
return false
}
//now we have access to id of the activity. and that is the URL
print(id)
return true
}
Run your code and then send your app to the background. Open a search in your iPhone and do a search on the item that we just indexed (see Figure 6-2):
Figure 6-2. Our item is listed in the search results
See Also