Quick nav

Old blog highlights (2003-2013)

iOS dev : on-device spotlight search for app contents

For the AdoreMe iOS app (the ecommmerce startup I’ve been working since last year), a feature I’ve personally wanted to add for a long time while is integration with Spotlight search of the product categories pages.

Lately I’ve started working on it, and would like to share with you some of the insights I discovered and practices I used:

To keep things as decoupled as possible, I created a separate class SpotlightHelper where I placed all search-related functionality.

attemptReindexCategories

Checks if spotlight indexing is allowed (by my homegrown feature flag and a/b test utility class), if we need reindexing (the current condition is to index categories only once / month, with the last indexed date saved on device in UserDefaults), then calls the async method that retrieves the categories list and eventually starts the indexing.

tryOpenActivity

This is a class method called from AppDelegate in the application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) method. It just checks if the current activity results from a tap on a spotlight search query result and opens the corresponding category screen (in my case, using my deeplink opening engine).

Now for the actual functionality

I wanted each index category to have a nice thumbnail image, which was loaded from our backend server.

The tricky part was to only add those categories to the index only after the thumbnail image was downloaded. To achieve this, I’m using a global array where, once my async download of the image is complete, I add the CSSearchableItem for the category. When I’m done downloading the thumbnails for all categories, I call the CSSearchableIndex.default().indexSearchableItems.

Issues handled

In order to allow concurrent access to the indexableItems array without getting into any concurrency problems, my indexableItems array is actually a SynchronizedArray. SynchronizedArray is a nifty array-like atomic access collection inspired by Basem Emara’s blog.

I keep a single SpotlightHelper instance, created /retained in my UserSession (a singleton). This prevents accidents with triggering multiple indexing requests at once. The indexing request only happens at most once per user session (and because of the checkAndSetIfNeedsReindexing method, won’t be called again until next month).

Possible issues (areas to improve)

I should probably first check/handle if Spotlight indexing has been disabled by the user on device, in order to prevent needless work.

In the future I’ll want to remove the spotlight indexed items when the feature flag becomes false (when we want to disable the feature completely)

In your own app you’ll probably handle errors when indexing, with CSSearchableIndex indexing batches of items instead of all at once, add error handling and retry.

The “last indexed date” flag should be set after you’ve actually succeeded indexing the searchable items, not before. This way, if any error has occurred in the meantime, you’re sure to retry on next user session.

I hope that my real-life example will give some of you you ideas on implementing Spotlight search for your apps as well.