mcpField
A
axiom-core-spotlight-ref

axiom-core-spotlight-ref

github.com/charleswiltgen開発1.2k163 回インストール今日更新
リポジトリ

axiom-core-spotlight-ref について

AppleアプリでCore SpotlightとNSUserActivityを実装する開発者向けのリファレンスです。コンテンツのインデックスと現在のアクティビティ記録を区別し、Spotlight検索、Siriの予測、Handoff、App Intentsを解説します。

主な機能

  1. 01CSSearchableItemによるコンテンツのインデックス
  2. 02NSUserActivityによるアクティビティの記録
  3. 03インデックスAPIとアクティビティAPIの使い分け
  4. 04一括インデックスと登録項目の削除
  5. 05アクティビティとApp Intentsの連携

活用シーン

  1. 01アプリの文書やメッセージをSpotlight検索可能にする
  2. 02表示中の画面からHandoffを利用する
  3. 03アプリのアクティビティにSiriの予測を有効化する
  4. 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ユーザーが注文の詳細を開いた
アプリのすべてのコンテンツをインデックス化するCSSearchableItem500 件すべての注文を検索可能にする
App Intents のエンティティ検索IndexedEntity「注文を検索して、条件は…」
デバイス間の HandoffNSUserActivityMac でメモの編集を続ける
バックグラウンドでコンテンツをインデックス化する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)

クイックメモとのリンクには、アクティビティが次の条件を満たす必要があります。

  1. アプリの現在のアクティビティであること(becomeCurrent() を使用)
  2. 明確で簡潔な title があること(動詞ではなく名詞)
  3. 安定した一貫性のある識別子を提供すること
  4. リンク先のコンテンツへの移動を常にサポートすること
  5. コンテンツが見つからない場合にも適切に対処すること
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 の比較

項目NSUserActivityCSSearchableItem
目的現在のユーザーアクティビティすべてのコンテンツのインデックス作成
使用する場面ユーザーが画面を表示しているときバックグラウンドでコンテンツのインデックスを作成するとき
範囲一度に 1 項目一括処理
Handoff対応非対応
予測対応非対応
検索限定的Spotlight と完全に連携
例ユーザーが注文の詳細を表示500 件すべての注文をインデックスに登録

推奨: 両方を使用します。

  • 現在表示されている画面には NSUserActivity を使用します
  • コンテンツの包括的なインデックス作成には CSSearchableItem を使用します

テストとデバッグ

インデックス登録済みの項目を確認する
Spotlight を使用する
  1. Spotlight を開きます(ホーム画面で下にスワイプ)
  2. インデックス登録したコンテンツのキーワードを検索します
  3. アプリの検索結果が表示されることを確認します
  4. 検索結果をタップし、画面遷移が機能することを確認します
コンソールログを使用する
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 に接続することで、包括的な検出可能性を実現します。

コメント