Making User Activities Searchable - Web and Search - iOS 9 Swift Programming Cookbook (2015)

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