SpotlightSearchTool is a new Foundation Models tool that lets a language model directly query your app's Core Spotlight index, grounding model responses in your app's own indexed content rather than general world knowledge.
• Enables conversational, context-aware search over your app's own data without building a custom RAG pipeline — just donate items to Spotlight and attach the tool to your LanguageModelSession.
• The model autonomously generates search queries, handles multi-step reasoning, and returns results grounded in your indexed content, including structured metadata like dates, locations, and custom attributes.
• Custom pipeline stages let you register computation steps (e.g. aggregations, scoring) that Spotlight executes efficiently over large result sets on behalf of the model.
Demonstrates attaching SpotlightSearchTool to a LanguageModelSession so users can ask natural-language questions about indexed hiking trails, with results streamed back to a SwiftUI chat interface.
import SwiftUI
import CoreSpotlight
import FoundationModels
// MARK: - Spotlight Index Delegate
final class TrailIndexDelegate: NSObject, CSSearchableIndexDelegate {
func searchableIndex(_ searchableIndex: CSSearchableIndex, reindexAllSearchableItemsWithAcknowledgementHandler acknowledgementHandler: @escaping () -> Void) {
acknowledgementHandler()
}
func searchableIndex(_ searchableIndex: CSSearchableIndex, reindexSearchableItemsWithIdentifiers identifiers: [String], acknowledgementHandler: @escaping () -> Void) {
acknowledgementHandler()
}
// New in iOS 27: called by SpotlightSearchTool to recover full item metadata
func searchableItems(forIdentifiers identifiers: [String]) async -> [CSSearchableItem] {
return identifiers.compactMap { id in
let attrs = CSSearchableItemAttributeSet(contentType: .text)
attrs.title = "Example Trail"
attrs.contentDescription = "A scenic trail with river views, completed on 2025-06-10."
attrs.namedLocation = "Marin County, CA"
return CSSearchableItem(uniqueIdentifier: id, domainIdentifier: "trails", attributeSet: attrs)
}
}
}
// MARK: - View Model
@Observable
final class TrailSearchViewModel {
var messages: [(role: String, text: String)] = []
var isLoading = false
private let indexDelegate = TrailIndexDelegate()
func ask(_ question: String) async {
messages.append((role: "user", text: question))
isLoading = true
defer { isLoading = false }
// 1. Configure SpotlightSearchTool for app bundle index
let searchTool = SpotlightSearchTool()
// 2. Set the delegate so the tool can recover full item metadata
let index = CSSearchableIndex.default()
index.indexDelegate = indexDelegate
// 3. Create a session with the on-device model + tool
let session = LanguageModelSession(
model: SystemLanguageModel.default,
tools: [searchTool]
)
do {
let response = try await session.respond(to: question)
messages.append((role: "assistant", text: response.content))
} catch {
messages.append((role: "assistant", text: "Error: \(error.localizedDescription)"))
}
}
}
// MARK: - SwiftUI View
struct TrailSearchView: View {
@State private var viewModel = TrailSearchViewModel()
@State private var input = ""
var body: some View {
NavigationStack {
VStack {
ScrollView {
LazyVStack(alignment: .leading, spacing: 12) {
ForEach(viewModel.messages.indices, id: \.self) { i in
let msg = viewModel.messages[i]
HStack {
if msg.role == "user" { Spacer() }
Text(msg.text)
.padding(10)
.background(msg.role == "user" ? Color.blue.opacity(0.2) : Color.gray.opacity(0.15))
.clipShape(RoundedRectangle(cornerRadius: 12))
if msg.role == "assistant" { Spacer() }
}
}
}
.padding()
}
if viewModel.isLoading {
ProgressView("Searching trails…").padding(.bottom, 4)
}
HStack {
TextField("Ask about your hikes…", text: $input)
.textFieldStyle(.roundedBorder)
Button("Send") {
let q = input
input = ""
Task { await viewModel.ask(q) }
}
.disabled(input.isEmpty || viewModel.isLoading)
}
.padding()
}
.navigationTitle("Trail Journal")
}
}
}Some Spotlight metadata (text content, HTML) is stored in a compact form not readable by the LLM — implement the new searchableItems(forIdentifiers:) delegate method to surface full item details. The model may call SpotlightSearchTool multiple times per response; use queryToken on SpotlightSearchReply to correctly sequence UI updates.
Requires Apple Intelligence-capable device for on-device SystemLanguageModel; SpotlightSearchTool itself is available on iOS, iPadOS, macOS, and visionOS.
More iOS 27 APIs land every week.
Get notified when new capabilities are published — no noise, just signal.