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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Set Up Core Spotlight with Core Data: Getting Started
30 mins
- Getting Started
- Adding Spotlight to Your Core Data Model
- Configuring Spotlight in Your Persistent Store
- Creating a Spotlight Delegate
- Connecting a Spotlight Delegate
- Describing Searchable Items
- Trying It Out
- Heavy Lifting
- Building a Bridge
- Importing From an API
- Running the Fetch
- Opening a Search Result in Your App
- Including Image Thumbnails with Spotlight
- Deleting Spotlight Data
- Searching Inside the App
- Searching Core Spotlight
- Searchable SwiftUI
- Where to Go From Here?
Searching Inside the App
You’ve done all this work to provide search results for when people are searching outside your app. Now, you’ll learn how to leverage Spotlight inside your app. In this section, you’ll add a search feature that looks up results in the Spotlight index.
The following sections will show you how to do just that.
Searching Core Spotlight
First, you’ll add the machinery to search the Spotlight index. In the Project navigator, within the group Controller, open BugController.swift. Then, add this import to the top of the file:
import CoreSpotlight
Next, add these properties to BugController
:
private var searchQuery: CSSearchQuery? private var spotlightFoundItems: [CSSearchableItem] = []
Then, add the following extension to the end of the file:
extension BugController { private func fetchSearchResults(_ items: [CSSearchableItem]) { let foundBugs = items.compactMap { (item: CSSearchableItem) -> CDBug? in guard let bugURI = URL(string: item.uniqueIdentifier) else { return nil } return dataStack.bugWithURI(bugURI) } bugs = foundBugs } }
Spotlight returns instances of CSSearchableItem
. In this method, you map the uniqueIdentifier
of CSSearchableItem
— which is a Core Data object URI — to the corresponding CDBug
instance. At the end, you update bugs
with the result to update the list in the UI.
The next step is to create a CSSearchQuery
that will perform the search for you. Add this method to the same extension:
private func searchCoreSpotlight(_ term: String) { // 1 let escapedTerm = term .replacingOccurrences(of: "\\", with: "\\\\") .replacingOccurrences(of: "\"", with: "\\\"") let queryString = "(textContent == \"\(escapedTerm)*\"cd)" // 2 searchQuery = CSSearchQuery( queryString: queryString, attributes: ["textContent"]) // 3 searchQuery?.foundItemsHandler = { items in DispatchQueue.main.async { self.spotlightFoundItems += items } } // 4 searchQuery?.completionHandler = { error in guard error == nil else { print(error?.localizedDescription ?? "oh no!") return } DispatchQueue.main.async { self.fetchSearchResults(self.spotlightFoundItems) self.spotlightFoundItems.removeAll() } } // 5 searchQuery?.start() }
In this method you set up and start a CSSearchQuery
based on your search term:
- Sanitize your search term, and then construct a query string. The syntax for this string is documented here. The query used here says “Search the textContent attribute with a case and diacritic insensitive search”.
- Instantiate
CSSearchQuery
with that query string and a list of the attributes fromCSSearchableItemAttributeSet
that you want to search. - Append found results to an array, as the
foundItemsHandler
ofCSSearchQuery
may be called many times during a search. - The
completionHandler
ofCSSearchQuery
is only called once. For a happy path, usefetchSearchResults()
to convert the results to records in the PointyBug database and clean up. - Call
start()
to trigger the query.
The last step is to provide access to the search function. Add this final method to the same extension:
func setSearchText(_ term: String) { guard !term.isEmpty else { searchQuery?.cancel() bugs = fetchedResults.fetchedObjects ?? [] return } searchCoreSpotlight(term) }
This method either resets the bug list to an unfiltered state or runs a search. You’re all done with the machinery. Now, all you need to do is add some UI.
Searchable SwiftUI
SwiftUI search is very simple. You declare an element as searchable, and SwiftUI interprets that as needed.
In the Project navigator, in the group Views, open BugListView.swift. Add this property to the top of BugListView
:
@State var searchText = ""
With this, you declare a bindable @State
property to act as storage for the search term.
Next, within the body
of BugListView
, add the following modifiers to the List
at the marker // add searchable here
:
.searchable(text: $searchText) .onChange(of: searchText) { newValue in bugController.setSearchText(newValue) }
You can’t help but love SwiftUI for syntax like this. You declare the List
as searchable
and bind to searchText
. The final modifier, onChange
, relays the search text to the method you created on BugController
.
Build and run. Then, pull down on the main list to reveal the search bar, and search for QA:
You can see all the bugs that are in a QA state.
Congratulations! You’ve created a fully featured search experience for your app. Quit your simulator to give the fans a rest.
Where to Go From Here?
Download the completed project files by clicking the Download Materials button at the top or bottom of the tutorial.
In this tutorial, you learned all the steps necessary to use Core Spotlight with a Core Data database, including how to:
- Upgrade your Core Data model to support Core Spotlight.
- Create a custom
NSCoreDataCoreSpotlightDelegate
to connect your database toCoreSpotlight
. - Start and stop the indexer when needed.
- Use
CoreSpotlight
for searching within your app.
Hopefully, this tutorial has demonstrated the simplicity and the power of the new API included with iOS 15 and macOS 12, and perhaps it’ll inspire you to both use Core Data and expose your data to Spotlight in clever ways.
We hope you enjoyed this tutorial. If you have any questions or comments, please join the forum discussion below!