import Foundation import CoreSpotlight import AppIntents /// Pushes Linkding bookmarks into the system Spotlight index via the /// `IndexedEntity` conformance on `BookmarkEntity`. /// /// This is the step that actually makes bookmarks discoverable: conforming to /// `IndexedEntity` only describes *how* an entity would be indexed (its /// `attributeSet`) — nothing reaches Spotlight until the entities are handed to /// `CSSearchableIndex`. Once indexed, bookmarks show up in Spotlight search and /// are reachable by Siri / Apple Intelligence. /// /// All calls are best-effort: failures are logged, never surfaced. If indexing /// is unavailable or fails, search degrades but nothing else breaks. enum SpotlightIndexer { /// Insert or update the given bookmarks in the Spotlight index. static func index(_ bookmarks: [Bookmark]) async { guard CSSearchableIndex.isIndexingAvailable(), !bookmarks.isEmpty else { return } do { try await CSSearchableIndex.default() .indexAppEntities(bookmarks.map(BookmarkEntity.init(from:))) Log.spotlight.info("Indexed \(bookmarks.count, privacy: .public) bookmarks") } catch { Log.spotlight.error("Index failed: \(error.localizedDescription, privacy: .public)") } } /// Remove bookmarks from the index by id (e.g. after delete / archive). static func remove(ids: [Int]) async { guard CSSearchableIndex.isIndexingAvailable(), !ids.isEmpty else { return } do { try await CSSearchableIndex.default() .deleteAppEntities(identifiedBy: ids, ofType: BookmarkEntity.self) Log.spotlight.info("Removed \(ids.count, privacy: .public) bookmarks") } catch { Log.spotlight.error("Delete failed: \(error.localizedDescription, privacy: .public)") } } }