Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Your App in Spotlight: Harnessing the Power of the Search APIs

Your App in Spotlight: Harnessing the Power of the Search APIs

Tobi Adebisi

May 24, 2016
Tweet

Other Decks in Technology

Transcript

  1. NSUserActivity • NSUserActivity objects help save app state so it

    can be restored later • NSUserActivity API is used for Handoff • In iOS 9 and later, NSUserActivity objects can be indexed, and displayed in search results on Spotlight.
  2. NSUserActivity • On its own, NSUserActivity can be used as

    a Search API for viewed app content • For a more robust Search experience, NSUserActivity is used in combination with Core Spotlight API to index app content (including ones the user hasn’t viewed yet) • NSUserActivity is not intended for indexing arbitrary data within your app
  3. Public Indexing • isEligibleForPublicIndexing • Not for private or sensitive

    information • First added to private, on-device index • If there’s a lot of user engagement with the public activities in search results, then this will lead to higher rankings in search results, and expanded indexing by Apple (cloud index) • Once the user activity is in the cloud index, it is searchable by anyone who has installed your application, regardless of whether they have opened that particular content or not
  4. struct FoodBud { let objectId: String let name: String let

    email: String let phone: String let recipes: [Recipe] let specialties: [String] }
  5. extension FoodBud { public static let domainIdentifier = "com.kodeplus.foodbuds.foodbud" public

    var userActivityInfo: [NSObject: AnyObject] { return ["id": objectId] } public var userActivity: NSUserActivity { let activity = NSUserActivity(activityType: FoodBud.domainIdentifier) activity.title = name activity.userInfo = userActivityInfo activity.keywords = Set(specialties) return activity } }
  6. override func viewDidLoad() { let activity = foodBud.userActivity activity.eligibleForSearch =

    true userActivity = activity } // MARK: NSUserActivity override func updateUserActivityState(activity: NSUserActivity) { activity.addUserInfoEntriesFromDictionary(foodBud.userActivityInfo) }
  7. So far, so good • Added all viewed items (foodBuds)

    to on- device index • What happens when we click on our search results? • Opens the main view of the app. That’s not what we want -_-
  8. Handoff Tidbits • becomeCurrent() • resignCurrent() • A non-current activity

    is not eligible for handoff (continuation on another device)
  9. //AppDelegate func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) ->

    Void) -> Bool { guard let tabBarController = window?.rootViewController as? UITabBarController else { return false } let tabNumber = userActivity.activityType == FoodBud.domainIdentifier ? 0 : 1 guard let navigationController = tabBarController.viewControllers?[tabNumber] as? UINavigationController else { return false } navigationController.topViewController?.restoreUserActivityState(userActivity) return true }
  10. //FoodBudsViewController // MARK: NSUserActivity override func restoreUserActivityState(activity: NSUserActivity) { guard

    let selectedActivityId = activity.userInfo?["id"] as? String else { return } selectedFoodBud = foodBuds.filter { $0.objectId == selectedActivityId }.first performSegueWithIdentifier("foodBudDetail", sender: nil) }
  11. Core Spotlight • CSSearchableItemAttributeSet • Allow us to add more

    information to search results • CSSearchableItem • Defines an object than can be indexed and available for on-device searches
  12. public var attributeSet: CSSearchableItemAttributeSet { let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeContact

    as String) attributeSet.title = name attributeSet.contentDescription = "\(specialties.joinWithSeparator(", ")) \n \(phone)" let image = UIImage(named: "chef") let imageData = UIImagePNGRepresentation(image!) attributeSet.thumbnailData = imageData attributeSet.supportsPhoneCall = true attributeSet.phoneNumbers = [phone] attributeSet.emailAddresses = [email] attributeSet.keywords = specialties return attributeSet }
  13. public var userActivity: NSUserActivity { let activity = NSUserActivity(activityType: FoodBud.domainIdentifier)

    activity.title = name activity.userInfo = userActivityInfo activity.keywords = Set(specialties) activity.contentAttributeSet = attributeSet return activity }
  14. CSSearchableItem • Allows us to add items (viewed or not)

    to on- device index • attributeSet (our friend from previous slide) • domainIdentifier • expirationDate • uniqueIdentifier
  15. func addRecipesToSearchableIndex() { for recipe in recipes { let attributeSet

    = CSSearchableItemAttributeSet(itemContentType: kUTTypeItem as String) attributeSet.title = recipe.name attributeSet.contentDescription = recipe.ingredients.joinWithSeparator(", ") attributeSet.keywords = recipe.name.componentsSeparatedByString(" ") + recipe.ingredients let searchableRecipe = CSSearchableItem(uniqueIdentifier: recipe.name, domainIdentifier: Recipe.domainIdentifier, attributeSet: attributeSet) searchableRecipes.append(searchableRecipe) } CSSearchableIndex.defaultSearchableIndex().indexSearchableItems(searchableRecipes, completionHandler: nil) }
  16. CSSearchableItem • Selecting search results from CoreSpotlight API is implemented

    using the same methods as those of NSUserActivity • Whenever a CSSearchableItem is selected, a NSUserActivity is created • We’ll revisit our application(_:continueUserActivity:restorationHa ndler:) method in App Delegate
  17. //AppDelegate func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) ->

    Void) -> Bool { guard let tabBarController = window?.rootViewController as? UITabBarController else { return false } //if userActivity.activityType == CSSearchableItemActionType let tabNumber = userActivity.activityType == FoodBud.domainIdentifier ? 0 : 1 guard let navigationController = tabBarController.viewControllers?[tabNumber] as? UINavigationController else { return false } tabBarController.selectedIndex = tabNumber navigationController.topViewController?.restoreUserActivityState(userActivity) return true }
  18. //RecipesViewController override func restoreUserActivityState(activity: NSUserActivity) { fetchRecipes() guard let selectedActivityName

    = activity.userInfo?[CSSearchableItemActivityIdentifier] as? String else { return } selectedRecipe = recipes.filter { $0.name == selectedActivityName }.first performSegueWithIdentifier("recipeDetail", sender: nil) }
  19. Web Markup • Allow Apple to discover and index app’s

    website • App’s website should contain markup for deep links • Prepare app to handle deep links
  20. //Preparing your app for deep linking func application(application: UIApplication, openURL

    url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool { if let components = NSURLComponents(URL: url, resolvingAgainstBaseURL: true), let path = components.path, let query = components.query { if path == "/Recipes" { //Pass the specific recipe from the URL to the view controller return recipeDetailViewController.loadRecipe(query) } } return false }
  21. Best Practices • Include various keywords. Apple recommends 3 -

    5 keywords per item, including synonyms, abbreviations, and category items. • Provide detailed search results using CSSearchableItemAttributeSet • Take user directly to searched content, quickly • Keep search results updated
  22. What to Index • Any item viewed, created or curated

    by user • Navigation parts and features • New messages, content, or items arriving on device
  23. References Search APIs Documentation
 http://developer.apple.com/ios/search WWDC 2015: Introducing Search APIs


    https://developer.apple.com/videos/play/wwdc2015/709/ WWDC 2015: Seamless Linking into Your App
 https://developer.apple.com/videos/play/wwdc2015/509/ Ray Wenderlich: Introduction to App Search
 https://www.raywenderlich.com/116608/ios-9-app-search-tutorial-introduction-to-app-search