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

Commit 906cb6df4f

906cb6df4f93a45c7d9b546cb7db7152eb12d11d

parent: 014330d266

Verified · cmc

cmc <hello@cleberg.net> · 2026-10-18 05:53 UTC

Tint the tab bar red while the Sky tab is in night mode

The Sky tab publishes its night state (nightActive); the root TabView
tints the selected tab red when Sky is selected and night mode is on.

Layout: unified · split

App/Sources/Info.plist −16
@@ -2,22 +2,6 @@
2<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 2<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3<plist version="1.0"> 3<plist version="1.0">
4<dict> 4<dict>
5 <key>CFBundleDevelopmentRegion</key>
6 <string>$(DEVELOPMENT_LANGUAGE)</string>
7 <key>CFBundleExecutable</key>
8 <string>$(EXECUTABLE_NAME)</string>
9 <key>CFBundleIdentifier</key>
10 <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11 <key>CFBundleInfoDictionaryVersion</key>
12 <string>6.0</string>
13 <key>CFBundleName</key>
14 <string>$(PRODUCT_NAME)</string>
15 <key>CFBundlePackageType</key>
16 <string>APPL</string>
17 <key>CFBundleShortVersionString</key>
18 <string>1.0</string>
19 <key>CFBundleVersion</key>
20 <string>1</string>
21 <key>MoonriseAPIURL</key> 5 <key>MoonriseAPIURL</key>
22 <string>$(MOONRISE_API_URL)</string> 6 <string>$(MOONRISE_API_URL)</string>
23 <key>NSAppTransportSecurity</key> 7 <key>NSAppTransportSecurity</key>
App/Sources/MoonriseApp.swift +10 −6
@@ -8,16 +8,20 @@ let log = Logger(subsystem: "sh.krz.moonrise", category: "app")
8struct MoonriseApp: App { 8struct MoonriseApp: App {
9 @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate 9 @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
10 @AppStorage(SettingsKey.onboarded) private var onboarded = false 10 @AppStorage(SettingsKey.onboarded) private var onboarded = false
11 @AppStorage(SettingsKey.nightActive) private var nightActive = false
12 @State private var tab = "sky"
11 13
12 var body: some Scene { 14 var body: some Scene {
13 WindowGroup { 15 WindowGroup {
14 TabView { 16 TabView(selection: $tab) {
15 Tab("Sky", systemImage: "sparkles") { SkyScreen() } 17 Tab("Sky", systemImage: "sparkles", value: "sky") { SkyScreen() }
16 Tab("System", systemImage: "circle.circle") { SystemScreen() } 18 Tab("System", systemImage: "circle.circle", value: "system") { SystemScreen() }
17 Tab("Sun", systemImage: "sun.max") { SunScreen() } 19 Tab("Sun", systemImage: "sun.max", value: "sun") { SunScreen() }
18 Tab("Launches", systemImage: "airplane.departure") { LaunchesScreen() } 20 Tab("Launches", systemImage: "airplane.departure", value: "launches") { LaunchesScreen() }
19 Tab("Library", systemImage: "books.vertical") { LibraryScreen() } 21 Tab("Library", systemImage: "books.vertical", value: "library") { LibraryScreen() }
20 } 22 }
23 // Red tab highlight while the Sky tab is in night mode.
24 .tint(tab == "sky" && nightActive ? Color(red: 0.85, green: 0.12, blue: 0.08) : nil)
21 .preferredColorScheme(.dark) 25 .preferredColorScheme(.dark)
22 .fullScreenCover(isPresented: .constant(!onboarded)) { 26 .fullScreenCover(isPresented: .constant(!onboarded)) {
23 OnboardingScreen().preferredColorScheme(.dark) 27 OnboardingScreen().preferredColorScheme(.dark)
App/Sources/SettingsScreen.swift +2
@@ -9,6 +9,8 @@ enum SettingsKey {
9 static let cityLongitude = "cityLongitude" 9 static let cityLongitude = "cityLongitude"
10 static let nightMode = "nightMode" // "off", "on" or "auto" 10 static let nightMode = "nightMode" // "off", "on" or "auto"
11 static let onboarded = "onboarded" 11 static let onboarded = "onboarded"
12 /// Whether the Sky tab is currently drawn in night mode (written by the Sky tab).
13 static let nightActive = "nightActive"
12} 14}
13 15
14struct City: Hashable, Identifiable { 16struct City: Hashable, Identifiable {
App/Sources/SkyScreen.swift +2
@@ -12,6 +12,7 @@ struct SkyScreen: View {
12 @AppStorage(SettingsKey.nightMode) private var nightMode = "auto" 12 @AppStorage(SettingsKey.nightMode) private var nightMode = "auto"
13 @State private var showSettings = false 13 @State private var showSettings = false
14 @AppStorage(SettingsKey.onboarded) private var onboarded = false 14 @AppStorage(SettingsKey.onboarded) private var onboarded = false
15 @AppStorage(SettingsKey.nightActive) private var nightActive = false
15 @State private var showToday = false 16 @State private var showToday = false
16 @State private var libraryPage: LibraryPage? 17 @State private var libraryPage: LibraryPage?
17 @State private var selection: SkyObject? 18 @State private var selection: SkyObject?
@@ -68,6 +69,7 @@ struct SkyScreen: View {
68 } 69 }
69 .onChange(of: model.offsetHours) { select(selection) } 70 .onChange(of: model.offsetHours) { select(selection) }
70 .onChange(of: onboarded) { if onboarded { model.start() } } 71 .onChange(of: onboarded) { if onboarded { model.start() } }
72 .onChange(of: night, initial: true) { nightActive = night }
71 .task { 73 .task {
72 if onboarded { model.start() } 74 if onboarded { model.start() }
73 while !Task.isCancelled { 75 while !Task.isCancelled {