iOS 9 Swift Programming Cookbook (2015)
Chapter 6. Web and Search
6.2 Making User Activities Searchable
Problem
You want to allow user activities inside your app to be searchable. User activities are of type NSUserActivity.
Solution
Use the eligibleForSearch and eligibleForPublicIndexing properties of the NSUserActivity class to mark your activities as searchable.
Discussion
Let’s say that the user is inside your app and is editing the text inside a text field. You start a user activity and want the user to be able to search for this activity in her home screen, then continue with that activity later. Start with the UI. Drop a text field and a text view on your view controller to make it look like Figure 6-3.
Figure 6-3. Put a text field and a text view on your UI
The text field will allow the user to enter whatever text she wants and we will use the text view to write log messages so that we know what is going on under the hood of our app and so will the user. Hook these up to your code. I’ve named the text field textField and the text viewstatus. Also set the delegate of your text field to your view controller, because you are going to want to know when the text field becomes active and inactive. That let’s you update the user activity accordingly.
Make your view controller conform to UITextFieldDelegate and NSUserActivityDelegate protocols and implement the user activity delegate methods:
func userActivityWasContinued(userActivity: NSUserActivity) {
log("Activity was continued")
}
func userActivityWillSave(userActivity: NSUserActivity) {
log("Activity will save")
}
Let’s also write a handy method that allows us to log messages into our text view:
func log(t: String){
dispatch_async(dispatch_get_main_queue()) {
self.status.text = t + "\n" + self.status.text
}
}
We need another method that can read the contents of our text field and, if it’s nil, give us an empty string:
func textFieldText() -> String{
if let txt = self.textField.text{
return txt
} else {
return ""
}
}
Then create your user activity as a lazy variable and mark it as searchable:
//TODO: change this ID to something relevant to your app
let activityType = "se.pixolity.Making-User-Activities-Searchable.editText"
let activityTxtKey = "se.pixolity.Making-User-Activities-Searchable.txt"
lazy var activity: NSUserActivity = {
let a = NSUserActivity(activityType: self.activityType)
a.title = "Text Editing"
a.eligibleForHandoff = true
a.eligibleForSearch = true
//do this only if it makes sense
//a.eligibleForPublicIndexing = true
a.delegate = self
a.keywords = ["txt", "text", "edit", "update"]
let att = CSSearchableItemAttributeSet(
itemContentType: kUTTypeText as String)
att.title = a.title
att.contentDescription = "Editing text right in the app"
att.keywords = Array(a.keywords)
if let u = NSBundle.mainBundle().URLForResource("Icon",
withExtension: "png"){
att.thumbnailData = NSData(contentsOfURL: u)
}
a.contentAttributeSet = att
return a
}()
NOTE
Make sure that you import the CoreSpotlight and MobileCoreServices frameworks.
Once your text field becomes active, mark the activity as the current one:
func textFieldDidBeginEditing(textField: UITextField) {
log("Activity is current")
userActivity = activity
activity.becomeCurrent()
}
func textFieldDidEndEditing(textField: UITextField) {
log("Activity resigns being current")
activity.resignCurrent()
userActivity = nil
}
When the text field’s content changes, mark that the user activity needs to be updated:
func textField(textField: UITextField,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String) -> Bool {
activity.needsSave = true
return true
}
A method in your view controller named updateUserActivityState(_:) gets called periodically when the current activity needs to be updated. Here you get the chance to update the user info dictionary of the activity:
override func updateUserActivityState(a: NSUserActivity) {
log("We are asked to update the activity state")
a.addUserInfoEntriesFromDictionary(
[self.activityTxtKey : self.textFieldText()])
super.updateUserActivityState(a)
}
That’s it, really. Now when the user starts writing text in the text field, and then sends the app to background, she will be able to search for the activity that she had started right on her home screen and then continue where she left off. I leave the details where we handle the request to continue the user activity up to you, as they are not new APIs.
See Also