Set Up Core Spotlight with Core Data: Getting Started

Learn how to connect Core Data with Core Spotlight and add search capability to your app using Spotlight. By Warren Burton.

4.7 (6) · 2 Reviews

Download materials
Save for later
Share

Many apps you build are containers for your customers’ information, and it’s up to you to provide great ways to let your customer search for that information. However, if the only way to find that information is to open your app and search for it, it makes it difficult for your users. You could instead expose that information to Spotlight and have your app results appear there.

When you have a lot of information that has a complex structure, Core Data is the preferred way of storing your app data. It’s super fast, and as long as you follow a few simple rules, it’s easy to use. The framework has been around for more than 15 years, and Apple engineers have been improving it this whole time.

In this tutorial, you’ll learn how to:

  • Upgrade your Core Data model to support Core Spotlight.
  • Create a custom NSCoreDataCoreSpotlightDelegate to connect your database to CoreSpotlight.
  • Start and stop the indexer when needed.
  • Use CoreSpotlight for searching within your app.
Note: This tutorial requires knowledge of Core Data. You can review the basics of Core Data with the Core Data with SwiftUI Tutorial.

Getting Started

Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial.

You’ll be using a bug tracking utility app, PointyBug. If you’ve already read Drag and Drop Tutorial for SwiftUI, you’ll be familiar with this app.

The persistent storage of PointyBug uses Core Data, and you’ll be adding extra code to expose your data to Spotlight.

Open the Xcode project in the starter directory. First, ensure that the simulator is set to iPad Pro (11-inch) (3rd generation):

Choose a simulator

Then, build and run. Rotate the simulator to landscape mode to see both the bug list and the details:

Starting point for the app

Tap ADD BUG, then tap the image picker button at top right of the detail view to add an image. Nothing happens when you tap the button, which seems like a bug. Fortunately, you can use drag and drop to import an image. Trigger a screenshot from the simulator by using the menu Device ▸ Trigger Screenshot.

The screenshot will appear at the bottom-left corner of the screen. Drag and drop that small image to the detail view:

Drag and drop the thumbnail

Drag the arrow image at the bottom to add a pointer to show where the problem is. Next, add a description of Image picker button in navigation bar doesn’t show image picker. Then, press Return. Finally, set the status to Open by tapping the gray “No Tag” icon. Now, you have your first bug report:

First bug report

Press Command-Shift-H to return to the Home Screen and trigger a save.

What you have is a shoe box app: a place to put bits of useful information. But without a system search, it has limited long-term value for your users. Your next task is to add the code to expose your data to the system.

Adding Spotlight to Your Core Data Model

Your first task is to change your Core Data model to let Spotlight know what parts of your model to index.

In the Project navigator, locate the group PointyBug ▸ Model ▸ Core Data. Select PointyBug.xcdatamodeld:

Project navigator view

Select CDBug and then text. In the Data Model inspector, check the box for Index in Spotlight:

Edit the data model

You took the first step of connecting your data to Spotlight. The next step is to configure NSPersistentContainer to allow indexing.

Configuring Spotlight in Your Persistent Store

In Core Data, open CoreDataStack.swift. This class wraps all the code needed to create an NSPersistentContainer that has all the machinery to work with a Core Data store.

Illustration describing the parts of a Core Data stack

Find makeStore(at:). This method creates an NSPersistentStoreDescription that describes how you want your store to work. Next, locate this line:

storeDescription.type = NSSQLiteStoreType

Below it, add the following:

storeDescription.setOption(
  true as NSNumber,
  forKey: NSPersistentHistoryTrackingKey)

This tells the store to use persistent history tracking. Setting this option is necessary for a Spotlight indexed store. The store will now record the changes you make and use those change sets to work out what to index.

In the same file, find isPreviewContext. This switch tells the persistent store to be memory resident when used in SwiftUI previews. Using NSInMemoryStoreType means there’s no file mess to clean up. For the same reason, this is also a useful configuration when doing unit testing.

Your next task is to create an instance of NSCoreDataCoreSpotlightDelegate for the NSPersistentStoreCoordinator.

Creating a Spotlight Delegate

NSCoreDataCoreSpotlightDelegate describes a set of methods that enable integration of a Core Data store with Core Spotlight:

Core Spotlight structure

In the Project navigator, select Core Data and create a new Swift file named BugSpotlightDelegate.swift. Add the following code to the file:

import CoreData
import CoreSpotlight

class BugSpotlightDelegate: NSCoreDataCoreSpotlightDelegate {
  override func domainIdentifier() -> String {
    return "com.raywenderlich.pointybug.bugs"
  }

  override func indexName() -> String? {
    return "bugs-index"
  }
}

BugSpotlightDelegate is a subclass of NSCoreDataCoreSpotlightDelegate. In this subclass, you override a couple of attributes to define some names that establish a connection between your app and Core Spotlight. domainIdentifier should be a reverse-coded domain that builds on your app’s bundle identifier, com.raywenderlich.pointybug.

That’s all you need for a start. Next, you’ll plug BugSpotlightDelegate into your store.

Connecting a Spotlight Delegate

You created NSCoreDataCoreSpotlightDelegate, so now you can attach it to your store. Open CoreDataStack.swift.

Add this property to the top of CoreDataStack:

private(set) var spotlightIndexer: BugSpotlightDelegate?

Then, add this extension to the end of the file:

extension CoreDataStack {
  func toggleSpotlightIndexing(enabled: Bool) {
    guard let spotlightIndexer = spotlightIndexer else { return }

    if enabled {
      spotlightIndexer.startSpotlightIndexing()
    } else {
      spotlightIndexer.stopSpotlightIndexing()
    }
  }
}

This is a helper to keep start and stop logic for NSCoreDataCoreSpotlightDelegate in one place.

Now, locate configureContainer(). Inside the trailing closure of container.loadPersistentStores, add the code below at the marker // insert here:

if !self.isPreviewContext {
  let coordinator = self.container.persistentStoreCoordinator
  self.spotlightIndexer = BugSpotlightDelegate(
    forStoreWith: storeDescription,
    coordinator: coordinator)
  self.toggleSpotlightIndexing(enabled: true)
}

In this fragment, you create BugSpotlightDelegate with information about your store. Then, you tell Core Spotlight to start doing its work.

There’s one last step: You need to provide a package of information that describes a searchable item.