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

Packages/MoonriseKit/Sources/Answers/Place.swift

6c4b28384d65e09486e0ad62f2ac9d10401cc80c
moonrise/Packages/MoonriseKit/Sources/Answers/Place.swift history · blame · raw

61 lines · 2113 bytes

 1import Foundation
 2import SkyEngine
 3
 4/// Where answers are worked out for. The Sky tab saves its observer here on every fix or city change,
 5/// so intents launched in the background can answer without a location fix.
 6public struct Place: Codable, Sendable, Equatable {
 7    public enum Kind: String, Codable, Sendable {
 8        /// The device's own position.
 9        case located
10        /// A city chosen in Settings.
11        case city
12        /// No fix and no city yet.
13        case fallback
14    }
15
16    public var latitude: Double
17    public var longitude: Double
18    public var name: String
19    public var kind: Kind
20
21    /// Rounds to 0.1°, about 10 km, which is all any answer needs.
22    public init(latitude: Double, longitude: Double, name: String, kind: Kind) {
23        self.latitude = (latitude * 10).rounded() / 10
24        self.longitude = (longitude * 10).rounded() / 10
25        self.name = name
26        self.kind = kind
27    }
28
29    public static let fallback = Place(latitude: 41.8781, longitude: -87.6298, name: "Chicago", kind: .fallback)
30
31    public var observer: Observer { Observer(latitude: latitude, longitude: longitude) }
32
33    /// "Your location" or the place's name.
34    public var label: String { kind == .located ? "Your location" : name }
35
36    /// Sentence that ends an answer which depends on the place; nil for the device's own position.
37    public var note: String? {
38        switch kind {
39        case .located: nil
40        case .city: "For \(name)."
41        case .fallback: "For \(name); open Moonrise to set your location."
42        }
43    }
44
45    static let key = "answersPlace"
46
47    public static func saved(in defaults: UserDefaults) -> Place? {
48        defaults.data(forKey: key).flatMap { try? JSONDecoder().decode(Place.self, from: $0) }
49    }
50
51    public func save(to defaults: UserDefaults) {
52        defaults.set(try? JSONEncoder().encode(self), forKey: Self.key)
53    }
54}
55
56extension [String] {
57    /// Joins sentences, with the place's note, if any, last.
58    func sentences(_ note: String?) -> String {
59        (self + [note].compactMap { $0 }).joined(separator: " ")
60    }
61}