krz/moonrise

Night-sky companion for iPhone, Apple Watch and Vision Pro: what's up tonight, the Moon, planets, launches and missions, and a Library of the solar system. SwiftUI app with a Cloudflare Workers backend. astronomy cloudflare-workers ios night-sky space swiftui visionos watchos

App/Sources/Intents/LibraryPageEntity.swift

main
moonrise/App/Sources/Intents/LibraryPageEntity.swift history · blame · raw

75 lines · 2802 bytes

7 symbols in this file
 1import Answers
 2import AppIntents
 3import CoreSpotlight
 4import Library
 5
 6/// A Library page for Siri, Shortcuts and Spotlight.
 7struct LibraryPageEntity: IndexedEntity {
 8    static let typeDisplayRepresentation: TypeDisplayRepresentation = "Library Page"
 9    static let defaultQuery = LibraryPageQuery()
10
11    let page: LibraryPage
12    var id: String { page.id }
13
14    var displayRepresentation: DisplayRepresentation {
15        DisplayRepresentation(title: "\(page.title)", subtitle: "\(page.subtitle)", image: .init(systemName: page.section.symbol))
16    }
17
18    var attributeSet: CSSearchableItemAttributeSet {
19        let a = defaultAttributeSet
20        a.contentDescription = page.paragraphs.first
21        a.keywords = [page.section.title] + page.facts.map(\.value)
22        return a
23    }
24}
25
26struct LibraryPageQuery: EntityStringQuery {
27    func entities(for identifiers: [String]) async throws -> [LibraryPageEntity] {
28        identifiers.compactMap { Library.page(id: $0).map(LibraryPageEntity.init) }
29    }
30
31    func entities(matching string: String) async throws -> [LibraryPageEntity] {
32        Library.search(string).prefix(20).map(LibraryPageEntity.init)
33    }
34
35    /// The pages the Sky tab can turn to: the Moon, planets, constellations and named stars.
36    /// These are also the names Siri listens for in "Look up …" and "Show … in the sky".
37    func suggestedEntities() async throws -> [LibraryPageEntity] {
38        Library.pages.filter { $0.skyTarget != nil }.map(LibraryPageEntity.init)
39    }
40}
41
42extension LibrarySection {
43    var symbol: String {
44        switch self {
45        case .sun: "sun.max"
46        case .planets: "circle.circle"
47        case .dwarfPlanets: "circle.dotted"
48        case .moons: "moon"
49        case .constellations: "star.circle"
50        case .stars: "star"
51        case .deepSky: "sparkles"
52        case .exoplanets: "globe"
53        case .missions: "antenna.radiowaves.left.and.right"
54        case .rockets: "airplane.departure"
55        }
56    }
57}
58
59/// Indexes every Library page in Spotlight once per build; pages only change with an app release.
60enum SpotlightIndexer {
61    static let key = "spotlightIndexedBuild"
62
63    static func indexIfNeeded() async {
64        let build = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? ""
65        guard UserDefaults.standard.string(forKey: key) != build else { return }
66        let index = CSSearchableIndex(name: "library")
67        do {
68            try await index.deleteAppEntities(ofType: LibraryPageEntity.self)
69            try await index.indexAppEntities(Library.pages.map(LibraryPageEntity.init))
70            UserDefaults.standard.set(build, forKey: key)
71        } catch {
72            log.error("Spotlight indexing failed: \(error.localizedDescription, privacy: .public)")
73        }
74    }
75}