Packages/MoonriseKit/Sources/Answers/Place.swift
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}