axiom-core-spotlight-ref について
AppleアプリでCore SpotlightとNSUserActivityを実装する開発者向けのリファレンスです。コンテンツのインデックスと現在のアクティビティ記録を区別し、Spotlight検索、Siriの予測、Handoff、App Intentsを解説します。
主な機能
- 01CSSearchableItemによるコンテンツのインデックス
- 02NSUserActivityによるアクティビティの記録
- 03インデックスAPIとアクティビティAPIの使い分け
- 04一括インデックスと登録項目の削除
- 05アクティビティとApp Intentsの連携
活用シーン
- 01アプリの文書やメッセージをSpotlight検索可能にする
- 02表示中の画面からHandoffを利用する
- 03アプリのアクティビティにSiriの予測を有効化する
- 04Spotlight検索結果への対応とインデックス項目の削除
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-core-spotlight-refインストール後、Claude Code で/axiom-core-spotlight-refを入力して読み込まれたか確認します。
詳細
Core Spotlight と NSUserActivity のリファレンス
概要
アプリのコンテンツを Spotlight 検索で見つけられるようにし、Siri の予測機能を有効にして、Handoff をサポートするための Core Spotlight フレームワークと NSUserActivity の包括的なガイドです。Core Spotlight はアプリのコンテンツを直接インデックス化し、NSUserActivity は予測用にユーザーのエンゲージメントを記録します。
主な違い Core Spotlight はアプリのすべてのコンテンツをインデックス化し、NSUserActivity は予測や Handoff のために現在のユーザーアクティビティを記録します。
このスキルを使う場面
次の場合にこのスキルを使用してください:
- アプリのコンテンツ(ドキュメント、メモ、注文、メッセージ)を Spotlight 用にインデックス化する
- Handoff や Siri の予測機能に NSUserActivity を使用する
- CSSearchableItem、IndexedEntity、NSUserActivity のどれを使うか選択する
- Spotlight の検索結果からのアクティビティの継続を実装する
- パフォーマンス向上のために一括インデックス化する
- インデックス化したコンテンツを削除する
- Spotlight 検索でアプリのコンテンツが見つからない問題をデバッグする
- NSUserActivity を App Intents(appEntityIdentifier)と統合する
次の場合はこのスキルを使用しないでください:
- App Shortcuts の実装(app-shortcuts-ref を使用してください)
- App Intents の基本(app-intents-ref を使用してください)
- アプリの全般的な見つけやすさの戦略(app-discoverability を使用してください)
関連スキル
- app-intents-ref — IndexedEntity を含む App Intents フレームワーク
- app-discoverability — アプリを見つけやすくするための戦略ガイド
- app-shortcuts-ref — すぐに利用できる App Shortcuts
API の使い分け
| ユースケース | アプローチ | 例 |
|---|---|---|
| ユーザーが特定の画面を表示している | NSUserActivity | ユーザーが注文の詳細を開いた |
| アプリのすべてのコンテンツをインデックス化する | CSSearchableItem | 500 件すべての注文を検索可能にする |
| App Intents のエンティティ検索 | IndexedEntity | 「注文を検索して、条件は…」 |
| デバイス間の Handoff | NSUserActivity | Mac でメモの編集を続ける |
| バックグラウンドでコンテンツをインデックス化する | CSSearchableItem の一括処理 | 起動時にドキュメントをインデックス化する |
Apple のガイダンス NSUserActivity は、ユーザーが開始したアクティビティ(現在表示されている画面)に使用し、一般的なインデックス化の手段としては使用しないでください。コンテンツを包括的にインデックス化するには、Core Spotlight の CSSearchableItem を使用してください。
Core Spotlight(CSSearchableItem)
検索可能なアイテムの作成
import CoreSpotlight
import UniformTypeIdentifiers
func indexOrder(_ order: Order) {
// 1. Create attribute set with metadata
let attributes = CSSearchableItemAttributeSet(contentType: .item)
attributes.title = order.coffeeName
attributes.contentDescription = "Ordered on \(order.date.formatted())"
attributes.keywords = ["coffee", "order", order.coffeeName.lowercased()]
attributes.thumbnailData = order.imageData
// Optional: Add location
attributes.latitude = order.location.coordinate.latitude
attributes.longitude = order.location.coordinate.longitude
// Optional: Add rating
attributes.rating = NSNumber(value: order.rating)
// 2. Create searchable item
let item = CSSearchableItem(
uniqueIdentifier: order.id.uuidString, // Stable ID
domainIdentifier: "orders", // Grouping
attributeSet: attributes
)
// Optional: Set expiration
item.expirationDate = Date().addingTimeInterval(60 * 60 * 24 * 365) // 1 year
// 3. Index the item
CSSearchableIndex.default().indexSearchableItems([item]) { error in
if let error = error {
print("Indexing error: \(error.localizedDescription)")
}
}
}
主なプロパティ
uniqueIdentifier
用途 アプリ内でこのアイテムを一意に識別する、安定した永続 ID です。
uniqueIdentifier: order.id.uuidString
要件:
- 安定している必要があります(同じアイテムには同じ識別子を使用します)
- 更新と削除に使用されます
- アプリ内でのみ有効です
domainIdentifier
用途 関連するアイテムをグループ化し、一括操作できるようにします。
domainIdentifier: "orders"
ユースケース:
- ドメイン内のすべてのアイテムを削除する
- 種類別に整理する(注文、ドキュメント、メッセージ)
- 一括操作
パターン:
// Index with domains
item1.domainIdentifier = "orders"
item2.domainIdentifier = "documents"
// Delete entire domain
CSSearchableIndex.default().deleteSearchableItems(
withDomainIdentifiers: ["orders"]
) { error in }
CSSearchableItemAttributeSet
検索可能なコンテンツを説明するメタデータです。
let attributes = CSSearchableItemAttributeSet(contentType: .item)
// Required
attributes.title = "Order #1234"
attributes.displayName = "Coffee Order"
// Highly recommended
attributes.contentDescription = "Medium latte with oat milk"
attributes.keywords = ["coffee", "latte", "order"]
attributes.thumbnailData = imageData
// Optional but valuable
attributes.contentCreationDate = Date()
attributes.contentModificationDate = Date()
attributes.rating = NSNumber(value: 5)
attributes.comment = "My favorite order"
よく使用する属性
| 属性 | 用途 | 例 |
|---|---|---|
title | 主なタイトル | "Coffee Order #1234" |
displayName | ユーザーに表示する名前 | "Morning Latte" |
contentDescription | 説明文 | "Medium latte with oat milk" |
keywords | 検索語 | ["coffee", "latte"] |
thumbnailData | プレビュー画像 | JPEG/PNG データ |
contentCreationDate | 作成日時 | Date() |
contentModificationDate | 最終変更日時 | Date() |
rating | 星による評価 | NSNumber(value: 5) |
latitude / longitude | 位置情報 | 37.7749, -122.4194 |
ドキュメント固有の属性
// For document types
attributes.contentType = UTType.pdf
attributes.author = "John Doe"
attributes.pageCount = 10
attributes.fileSize = 1024000
attributes.path = "/path/to/document.pdf"
メッセージ固有の属性
// For messages
attributes.recipients = ["[email protected]"]
attributes.recipientNames = ["Jane Doe"]
attributes.authorNames = ["John Doe"]
attributes.subject = "Meeting notes"
パフォーマンス向上のための一括インデックス化
❌ 避ける:アイテムを一つずつインデックス化する
// Bad: 100 index operations
for order in orders {
CSSearchableIndex.default().indexSearchableItems([order.asSearchableItem()]) { _ in }
}
✅ 推奨:インデックス化操作を一括で行う
// Good: 1 index operation
let items = orders.map { $0.asSearchableItem() }
CSSearchableIndex.default().indexSearchableItems(items) { error in
if let error = error {
print("Batch indexing error: \(error)")
} else {
print("Indexed \(items.count) items")
}
}
推奨されるバッチサイズ 1 回の呼び出しにつき 100~500 件です。件数がさらに多い場合は、複数のバッチに分割してください。
削除パターン
識別子で削除する
let identifiers = ["order-1", "order-2", "order-3"]
CSSearchableIndex.default().deleteSearchableItems(
withIdentifiers: identifiers
) { error in
if let error = error {
print("Deletion error: \(error)")
}
}
ドメインで削除する
// Delete all items in "orders" domain
CSSearchableIndex.default().deleteSearchableItems(
withDomainIdentifiers: ["orders"]
) { error in }
すべて削除する
// Nuclear option: delete everything
CSSearchableIndex.default().deleteAllSearchableItems { error in
if let error = error {
print("Failed to delete all: \(error)")
}
}
削除するタイミング:
- ユーザーがコンテンツを削除したとき
- コンテンツが期限切れになったとき
- ユーザーがログアウトしたとき
- アプリをリセット/再インストールしたとき
App Entity の統合(App Intents)
App Entity から作成する
import AppIntents
struct OrderEntity: AppEntity, IndexedEntity {
var id: UUID
@Property(title: "Coffee", indexingKey: \.title)
var coffeeName: String
@Property(title: "Date", indexingKey: \.contentCreationDate)
var orderDate: Date
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Order"
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(coffeeName)", subtitle: "Order from \(orderDate.formatted())")
}
}
// Create searchable item from entity
let order = OrderEntity(id: UUID(), coffeeName: "Latte", orderDate: Date())
let item = CSSearchableItem(appEntity: order)
CSSearchableIndex.default().indexSearchableItems([item])
既存の項目にエンティティを関連付ける
let attributes = CSSearchableItemAttributeSet(contentType: .item)
attributes.title = "Order #1234"
let item = CSSearchableItem(
uniqueIdentifier: "order-1234",
domainIdentifier: "orders",
attributeSet: attributes
)
// Associate with App Intent entity
item.associateAppEntity(orderEntity, priority: .default)
メリット:
- ショートカットで「検索」アクションが自動的に利用可能になります
- Spotlight 検索でエンティティが直接表示されます
- App Intents と連携します
NSUserActivity
概要
NSUserActivity は、以下のユーザーエンゲージメントを記録します。
- Handoff — 別のデバイスでアクティビティを続行します
- Spotlight 検索 — 現在表示しているコンテンツをインデックスに登録します
- Siri の予測 — この画面に戻ることを提案します
- クイックメモ — メモをアプリのコンテンツにリンクします
対応プラットフォーム: iOS 8.0 以降、iPadOS 8.0 以降、macOS 10.10 以降、tvOS 9.0 以降、watchOS 2.0 以降、axiom-visionOS 1.0 以降
適格性プロパティ
let activity = NSUserActivity(activityType: "com.app.viewOrder")
// Enable Spotlight search
activity.isEligibleForSearch = true
// Enable Siri predictions
activity.isEligibleForPrediction = true
// Enable Handoff to other devices
activity.isEligibleForHandoff = true
// Contribute URL to global search (public content only)
activity.isEligibleForPublicIndexing = false
プライバシーに関する注意: isEligibleForPublicIndexing = true を設定するのは、公開コンテンツ(公開 URL のあるブログ記事など)のみにしてください。
基本的なパターン
func viewOrder(_ order: Order) {
// 1. Create activity
let activity = NSUserActivity(activityType: "com.coffeeapp.viewOrder")
activity.title = order.coffeeName
// 2. Set eligibility
activity.isEligibleForSearch = true
activity.isEligibleForPrediction = true
// 3. Provide identifier for updates/deletion
activity.persistentIdentifier = order.id.uuidString
// 4. Provide rich metadata
let attributes = CSSearchableItemAttributeSet(contentType: .item)
attributes.title = order.coffeeName
attributes.contentDescription = "Your \(order.coffeeName) order"
attributes.thumbnailData = order.imageData
activity.contentAttributeSet = attributes
// 5. Mark as current
activity.becomeCurrent()
// 6. Store reference (important!)
self.userActivity = activity
}
重要: アクティビティへの強い参照を保持してください。強い参照がないと、検索結果に表示されません。
becomeCurrent() と resignCurrent()
// UIKit pattern
class OrderDetailViewController: UIViewController {
var currentActivity: NSUserActivity?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let activity = NSUserActivity(activityType: "com.app.viewOrder")
activity.title = order.coffeeName
activity.isEligibleForSearch = true
activity.becomeCurrent() // Mark as active
self.currentActivity = activity
self.userActivity = activity // UIKit integration
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
currentActivity?.resignCurrent() // Mark as inactive
}
}
// SwiftUI pattern
struct OrderDetailView: View {
let order: Order
var body: some View {
VStack {
Text(order.coffeeName)
}
.onAppear {
let activity = NSUserActivity(activityType: "com.app.viewOrder")
activity.title = order.coffeeName
activity.isEligibleForSearch = true
activity.becomeCurrent()
// SwiftUI automatically manages userActivity
self.userActivity = activity
}
}
}
App Intents との連携(appEntityIdentifier)
NSUserActivity を App Intent エンティティに接続します。
func viewOrder(_ order: Order) {
let activity = NSUserActivity(activityType: "com.app.viewOrder")
activity.title = order.coffeeName
activity.isEligibleForSearch = true
activity.isEligibleForPrediction = true
// Connect to App Intent entity
activity.appEntityIdentifier = order.id.uuidString
// Now Spotlight can surface this as an entity suggestion
activity.becomeCurrent()
self.userActivity = activity
}
メリット:
- 関連する状況で、Siri がこの注文を提案します
- App Intents からこのアクティビティを参照できます
- ショートカットと連携します
画面上のコンテンツのタグ付け
WWDC で紹介されたパターン: 現在表示されているコンテンツにタグを付け、Spotlight のパラメーター候補として表示します。
func showEvent(_ event: Event) {
let activity = NSUserActivity(activityType: "com.app.viewEvent")
activity.persistentIdentifier = event.id.uuidString
// Spotlight suggests this event for intent parameters
activity.appEntityIdentifier = event.id.uuidString
activity.becomeCurrent()
userActivity = activity
}
結果: イベントパラメーターが必要なインテントをユーザーが呼び出すと、Spotlight は現在表示されているイベントを提案します。
クイックメモとの連携(macOS/iPadOS)
クイックメモとのリンクには、アクティビティが次の条件を満たす必要があります。
- アプリの現在のアクティビティであること(
becomeCurrent()を使用) - 明確で簡潔な
titleがあること(動詞ではなく名詞) - 安定した一貫性のある識別子を提供すること
- リンク先のコンテンツへの移動を常にサポートすること
- コンテンツが見つからない場合にも適切に対処すること
let activity = NSUserActivity(activityType: "com.app.viewNote")
activity.title = note.title // ✅ "Project Ideas" not ❌ "View Note"
activity.persistentIdentifier = note.id.uuidString
activity.targetContentIdentifier = note.id.uuidString
activity.becomeCurrent()
アクティビティの続行(Spotlight のタップへの対応)
ユーザーが Spotlight の検索結果をタップした場合、続行処理を行います。
UIKit
// AppDelegate or SceneDelegate
func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
guard userActivity.activityType == "com.app.viewOrder" else {
return false
}
// Extract identifier
if let identifier = userActivity.persistentIdentifier,
let orderID = UUID(uuidString: identifier) {
// Navigate to order
navigateToOrder(orderID)
return true
}
return false
}
SwiftUI
@main
struct CoffeeApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onContinueUserActivity("com.app.viewOrder") { userActivity in
if let identifier = userActivity.persistentIdentifier,
let orderID = UUID(uuidString: identifier) {
// Navigate to order
navigateToOrder(orderID)
}
}
}
}
}
検索可能な項目の続行
// When continuing from CSSearchableItem
func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
if userActivity.activityType == CSSearchableItemActionType {
// Get identifier from Core Spotlight item
if let identifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String {
// Navigate based on identifier
navigateToItem(identifier)
return true
}
}
return false
}
削除 API
保存済みアクティビティをすべて削除する
NSUserActivity.deleteAllSavedUserActivities { }
特定のアクティビティを削除する
let identifiers = ["order-1", "order-2"]
NSUserActivity.deleteSavedUserActivities(
withPersistentIdentifiers: identifiers
) { }
削除するタイミング:
- ユーザーがコンテンツを削除したとき
- ユーザーがログアウトしたとき
- コンテンツにアクセスできなくなったとき
NSUserActivity と CSSearchableItem の比較
| 項目 | NSUserActivity | CSSearchableItem |
|---|---|---|
| 目的 | 現在のユーザーアクティビティ | すべてのコンテンツのインデックス作成 |
| 使用する場面 | ユーザーが画面を表示しているとき | バックグラウンドでコンテンツのインデックスを作成するとき |
| 範囲 | 一度に 1 項目 | 一括処理 |
| Handoff | 対応 | 非対応 |
| 予測 | 対応 | 非対応 |
| 検索 | 限定的 | Spotlight と完全に連携 |
| 例 | ユーザーが注文の詳細を表示 | 500 件すべての注文をインデックスに登録 |
推奨: 両方を使用します。
- 現在表示されている画面には NSUserActivity を使用します
- コンテンツの包括的なインデックス作成には CSSearchableItem を使用します
テストとデバッグ
インデックス登録済みの項目を確認する
Spotlight を使用する
- Spotlight を開きます(ホーム画面で下にスワイプ)
- インデックス登録したコンテンツのキーワードを検索します
- アプリの検索結果が表示されることを確認します
- 検索結果をタップし、画面遷移が機能することを確認します
コンソールログを使用する
CSSearchableIndex.default().fetchLastClientState { clientState, error in
if let error = error {
print("Error fetching client state: \(error)")
} else {
print("Client state: \(clientState?.base64EncodedString() ?? "none")")
}
}
よくある問題
Spotlight に項目が表示されない
- インデックス作成に 1~2 分待ちます
isEligibleForSearch = trueであることを確認します- 「システム設定」→「Siri と検索」→「[App]」→「検索でアプリを表示」を確認します
- デバイスを再起動します
- コンソールでインデックス作成のエラーを確認します
アクティビティによって Handoff が開始されない
isEligibleForHandoff = trueであることを確認します- 両方のデバイスで同じ iCloud アカウントにサインインしていることを確認します
- 両方のデバイスで Bluetooth と Wi-Fi が有効になっていることを確認します
- activityType が逆 DNS 形式(com.company.app.action)であることを確認します
続行処理が機能しない
application(_:continue:restorationHandler:)が実装されていることを確認します- activityType が完全に一致していることを確認します
- persistentIdentifier が設定されていることを確認します
- デバッガーでテストし、メソッドが呼び出されることを確認します
ベストプラクティス
1. 選択的なインデックス作成
❌ やってはいけないこと:すべてをインデックスに登録する
// Bad: Index all 10,000 items
let allItems = try await ItemService.shared.all()
✅ すべき: 選択的にインデックスする
// Good: Index recent/important items
let recentItems = try await ItemService.shared.recent(limit: 100)
let favoriteItems = try await ItemService.shared.favorites()
理由 パフォーマンス、クォータ制限、ユーザーエクスペリエンス。
2. ドメイン識別子を使用する
❌ すべきでない: 一意の識別子だけに頼る
// Hard to delete all orders
CSSearchableIndex.default().deleteSearchableItems(withIdentifiers: allOrderIDs)
✅ すべき: ドメインでグループ化する
// Easy to delete all orders
CSSearchableIndex.default().deleteSearchableItems(withDomainIdentifiers: ["orders"])
3. 有効期限を設定する
❌ すべきでない: アイテムを永久にインデックスする
// Bad: Items never expire
let item = CSSearchableItem(/* ... */)
✅ すべき: 適切な有効期限を設定する
// Good: Expire after 1 year
item.expirationDate = Date().addingTimeInterval(60 * 60 * 24 * 365)
4. 豊富なメタデータを提供する
❌ すべきでない: 最小限のメタデータ
attributes.title = "Item"
✅ すべき: 豊富で検索可能なメタデータ
attributes.title = "Medium Latte Order"
attributes.contentDescription = "Ordered on December 12, 2025"
attributes.keywords = ["coffee", "latte", "order", "medium"]
attributes.thumbnailData = imageData
5. 欠落しているコンテンツを適切に処理する
func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
guard let identifier = userActivity.persistentIdentifier else {
return false
}
// Attempt to load content
if let item = try? await ItemService.shared.fetch(id: identifier) {
navigate(to: item)
return true
} else {
// Content deleted or unavailable
showAlert("This content is no longer available")
// Delete activity from search
NSUserActivity.deleteSavedUserActivities(
withPersistentIdentifiers: [identifier]
)
return true // Still handled
}
}
完全な例
包括的な統合
import CoreSpotlight
import UniformTypeIdentifiers
class OrderManager {
// MARK: - Core Spotlight Indexing
func indexOrder(_ order: Order) {
let attributes = CSSearchableItemAttributeSet(contentType: .item)
attributes.title = order.coffeeName
attributes.contentDescription = "Order from \(order.date.formatted())"
attributes.keywords = ["coffee", "order", order.coffeeName.lowercased()]
attributes.thumbnailData = order.thumbnailImageData
attributes.contentCreationDate = order.date
attributes.rating = NSNumber(value: order.rating)
let item = CSSearchableItem(
uniqueIdentifier: order.id.uuidString,
domainIdentifier: "orders",
attributeSet: attributes
)
item.expirationDate = Date().addingTimeInterval(60 * 60 * 24 * 365)
CSSearchableIndex.default().indexSearchableItems([item]) { error in
if let error = error {
print("Indexing error: \(error)")
}
}
}
func deleteOrder(_ orderID: UUID) {
// Delete from Core Spotlight
CSSearchableIndex.default().deleteSearchableItems(
withIdentifiers: [orderID.uuidString]
)
// Delete NSUserActivity
NSUserActivity.deleteSavedUserActivities(
withPersistentIdentifiers: [orderID.uuidString]
)
}
func deleteAllOrders() {
CSSearchableIndex.default().deleteSearchableItems(
withDomainIdentifiers: ["orders"]
)
}
// MARK: - NSUserActivity for Current Screen
func createActivityForOrder(_ order: Order) -> NSUserActivity {
let activity = NSUserActivity(activityType: "com.coffeeapp.viewOrder")
activity.title = order.coffeeName
activity.isEligibleForSearch = true
activity.isEligibleForPrediction = true
activity.persistentIdentifier = order.id.uuidString
// Connect to App Intents
activity.appEntityIdentifier = order.id.uuidString
// Rich metadata
let attributes = CSSearchableItemAttributeSet(contentType: .item)
attributes.title = order.coffeeName
attributes.contentDescription = "Your \(order.coffeeName) order"
attributes.thumbnailData = order.thumbnailImageData
activity.contentAttributeSet = attributes
return activity
}
}
// UIKit view controller
class OrderDetailViewController: UIViewController {
var order: Order!
var currentActivity: NSUserActivity?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
currentActivity = OrderManager.shared.createActivityForOrder(order)
currentActivity?.becomeCurrent()
self.userActivity = currentActivity
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
currentActivity?.resignCurrent()
}
}
// SwiftUI view
struct OrderDetailView: View {
let order: Order
var body: some View {
VStack {
Text(order.coffeeName)
.font(.largeTitle)
Text("Ordered on \(order.date.formatted())")
.foregroundColor(.secondary)
}
.userActivity("com.coffeeapp.viewOrder") { activity in
activity.title = order.coffeeName
activity.isEligibleForSearch = true
activity.isEligibleForPrediction = true
activity.persistentIdentifier = order.id.uuidString
activity.appEntityIdentifier = order.id.uuidString
let attributes = CSSearchableItemAttributeSet(contentType: .item)
attributes.title = order.coffeeName
attributes.contentDescription = "Your \(order.coffeeName) order"
activity.contentAttributeSet = attributes
}
}
}
リソース
WWDC: 260, 2015-709
Docs: /corespotlight, /corespotlight/cssearchableitem, /foundation/nsuseractivity
Skills: axiom-app-intents-ref, axiom-app-discoverability, axiom-app-shortcuts-ref
覚えておいてください Core Spotlight はアプリのすべてのコンテンツをインデックスし、NSUserActivity はユーザーが現在何をしているかを示します。バッチインデックスには CSSearchableItem を、アクティブな画面には NSUserActivity を使用し、それらを appEntityIdentifier を介して App Intents に接続することで、包括的な検出可能性を実現します。