From 75f95a92b829f41e459a280d66e47cc60dab6f6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantale=C3=A3o=20Gon=C3=A7alves?= <5808343+bgoncal@users.noreply.github.com> Date: Sun, 6 Sep 2026 02:09:57 +0200 Subject: [PATCH 1/2] Let the system refresh the Spotlight index Today the index only updates when the database changes or the app returns to the foreground, so entries can sit stale until something else triggers a pass. --- ...IntentEntityQuery+IndexedEntityQuery.swift | 20 +++++++++++++++++++ .../Spotlight/SpotlightEntityIndexer.swift | 19 ++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 Sources/App/Utilities/Spotlight/HAAppEntityAppIntentEntityQuery+IndexedEntityQuery.swift diff --git a/Sources/App/Utilities/Spotlight/HAAppEntityAppIntentEntityQuery+IndexedEntityQuery.swift b/Sources/App/Utilities/Spotlight/HAAppEntityAppIntentEntityQuery+IndexedEntityQuery.swift new file mode 100644 index 0000000000..8853f53f6c --- /dev/null +++ b/Sources/App/Utilities/Spotlight/HAAppEntityAppIntentEntityQuery+IndexedEntityQuery.swift @@ -0,0 +1,20 @@ +import AppIntents +import CoreSpotlight +import Foundation +import Shared + +/// Lets the system ask for a Spotlight refresh instead of waiting for the app's own pass, which only +/// runs when the database changes or the app comes to the foreground. +@available(iOS 27.0, *) +extension HAAppEntityAppIntentEntityQuery: IndexedEntityQuery { + func reindexEntities( + for identifiers: [String], + indexDescription: CSSearchableIndexDescription + ) async throws { + try await SpotlightEntityIndexer.shared.reindex(entityIds: identifiers) + } + + func reindexAllEntities(indexDescription: CSSearchableIndexDescription) async throws { + await SpotlightEntityIndexer.shared.reindexEverything() + } +} diff --git a/Sources/App/Utilities/Spotlight/SpotlightEntityIndexer.swift b/Sources/App/Utilities/Spotlight/SpotlightEntityIndexer.swift index 56a8b51360..998db8ed34 100644 --- a/Sources/App/Utilities/Spotlight/SpotlightEntityIndexer.swift +++ b/Sources/App/Utilities/Spotlight/SpotlightEntityIndexer.swift @@ -104,6 +104,25 @@ final class SpotlightEntityIndexer: ServerObserver { needsReindexOnForeground = true } + /// Reindexes the named entities on request from the system, which asks when it notices the index + /// may be stale rather than waiting for the next database change. + @available(iOS 27.0, *) + func reindex(entityIds: [String]) async throws { + let wanted = Set(entityIds) + guard let snapshot = await Task.detached(priority: .utility) { Self.makeSnapshot() }.value else { + return + } + let entities = snapshot.entities.filter { wanted.contains($0.id) } + guard !entities.isEmpty else { return } + try await index.indexAppEntities(entities) + } + + /// Rebuilds the whole index on request from the system. + @available(iOS 27.0, *) + func reindexEverything() async { + await reindex(reason: "system asked for a reindex") + } + private func reindexAfterForegroundIfNeeded() { guard needsReindexOnForeground else { return } needsReindexOnForeground = false From 96d6c59b889d9cc83d80f26a33df8dd4bd3d8baf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantale=C3=A3o=20Gon=C3=A7alves?= <5808343+bgoncal@users.noreply.github.com> Date: Sun, 6 Sep 2026 13:24:31 +0200 Subject: [PATCH 2/2] Run tests on the Xcode 27 preview runner The App Intent schema types need the iOS 27 SDK, which the macos-26 image does not carry: it tops out at Xcode 26.6. GitHub's xcode-27 preview image has Xcode 27 beta 6 as its default, along with iOS 27 and watchOS 27 simulators, so both test destinations still resolve. The DerivedData and gem cache keys include DEVELOPER_DIR, so the toolchain change invalidates them on its own. --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2caba2e8c..e301c45bb8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ on: - main env: - DEVELOPER_DIR: /Applications/Xcode_26.4.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_27.0.app/Contents/Developer FASTLANE_SKIP_UPDATE_CHECK: true FASTLANE_XCODE_LIST_TIMEOUT: 80 FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT: 80 @@ -146,7 +146,7 @@ jobs: ``` test: - runs-on: macos-26 + runs-on: xcode-27 timeout-minutes: 60 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1