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/Feeds/Launches.swift

b327e0d6e7ab67511c5bade3f3c3a93834f6e9ca
moonrise/Packages/MoonriseKit/Sources/Feeds/Launches.swift history · blame · raw

56 lines · 2216 bytes

 1import Foundation
 2import SkyEngine
 3
 4/// An upcoming launch as served by the Moonrise backend (from Launch Library 2).
 5public struct Launch: Codable, Sendable, Identifiable, Hashable {
 6    public var id: String
 7    public var name: String
 8    public var net: Date
 9    public var windowStart: Date?
10    public var windowEnd: Date?
11    /// Launch Library 2 status abbreviation: Go, TBC, TBD, Hold, Success, Failure, …
12    public var status: String
13    public var statusName: String
14    public var provider: String
15    public var rocket: String
16    public var mission: String?
17    public var missionType: String?
18    public var missionDescription: String?
19    public var orbit: String?
20    public var pad: String
21    public var location: String
22    public var padLatitude: Double?
23    public var padLongitude: Double?
24    /// Element sets for this launch's objects (a Starlink train, rocket bodies, payloads), once known.
25    public var elements: [OrbitalElements]?
26    /// "supgp" (SpaceX data via CelesTrak, possibly predicted before launch) or "gp" (CelesTrak catalogue).
27    public var elementsSource: String?
28}
29
30public struct LaunchList: Codable, Sendable {
31    public var fetchedAt: Date?
32    public var launches: [Launch]
33    /// Launched in the last 14 days, latest first.
34    public var recent: [Launch]?
35}
36
37public enum LaunchFeed {
38    static let decoder: JSONDecoder = {
39        let d = JSONDecoder()
40        d.dateDecodingStrategy = .custom { decoder in
41            let s = try decoder.singleValueContainer().decode(String.self)
42            if let date = try? Date(s, strategy: .iso8601) { return date }
43            if let date = try? Date(s, strategy: Date.ISO8601FormatStyle(includingFractionalSeconds: true)) { return date }
44            throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, debugDescription: "bad date \(s)"))
45        }
46        return d
47    }()
48
49    public static func decode(_ data: Data) throws -> LaunchList {
50        try decoder.decode(LaunchList.self, from: data)
51    }
52
53    public static func fetch(base: URL, session: URLSession = .shared) async throws -> LaunchList {
54        try decode(await session.data(from: base.appending(path: "v1/launches")).0)
55    }
56}