commit 290c374647d13a74d09cbcb61942bcf2722b99e1
parent d75fd2e1405a12158d541fdc156c429fd3539372
Author: Michael Camilleri <[email protected]>
Date: Thu, 26 Mar 2026 19:04:40 +0900
Sync theme across platforms
This commit syncs the theme used across platforms. This is most
important for the Apple Watch which does not have a way to independently
adjust the theme.
Co-Authored-By: Claude 4.6 Opus <[email protected]>
Diffstat:
6 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/Listless/Sync/KeyValueSyncBridge.swift b/Listless/Sync/KeyValueSyncBridge.swift
@@ -13,7 +13,7 @@ final class KeyValueSyncBridge {
cloud.synchronize()
for key in keys {
- if let cloudValue = cloud.string(forKey: key) {
+ if let cloudValue = cloud.object(forKey: key) {
isSyncing = true
UserDefaults.standard.set(cloudValue, forKey: key)
isSyncing = false
@@ -43,7 +43,7 @@ final class KeyValueSyncBridge {
let cloud = NSUbiquitousKeyValueStore.default
isSyncing = true
for key in changedKeys where keys.contains(key) {
- UserDefaults.standard.set(cloud.string(forKey: key), forKey: key)
+ UserDefaults.standard.set(cloud.object(forKey: key), forKey: key)
}
isSyncing = false
}
@@ -54,8 +54,8 @@ final class KeyValueSyncBridge {
let cloud = NSUbiquitousKeyValueStore.default
isSyncing = true
for key in keys {
- let localValue = defaults.string(forKey: key)
- let cloudValue = cloud.string(forKey: key)
+ let localValue = defaults.object(forKey: key) as? NSObject
+ let cloudValue = cloud.object(forKey: key) as? NSObject
if localValue != cloudValue {
cloud.set(localValue, forKey: key)
}
diff --git a/ListlessMac/ListlessMacApp.swift b/ListlessMac/ListlessMacApp.swift
@@ -16,6 +16,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuItemValidation {
private static let appearanceModeKey = "appearanceMode"
private static let colorThemeKey = "colorTheme"
+ private let keyValueSyncBridge = KeyValueSyncBridge(keys: ["headingText", "colorTheme"])
+
private var keyWindowCoordinator: WindowCoordinator? {
guard let window = NSApp.keyWindow else { return nil }
return coordinators.object(forKey: window)
@@ -34,6 +36,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuItemValidation {
func applicationDidFinishLaunching(_ notification: Notification) {
NSWindow.allowsAutomaticWindowTabbing = false
applyAppearanceMode(UserDefaults.standard.integer(forKey: Self.appearanceModeKey))
+ keyValueSyncBridge.start()
installMainMenu()
openNewWindow()
}
diff --git a/ListlessWatch/ListlessWatchApp.swift b/ListlessWatch/ListlessWatchApp.swift
@@ -3,7 +3,7 @@ import SwiftUI
@main
struct ListlessWatchApp: App {
private let persistenceController = PersistenceController.shared
- private let keyValueSyncBridge = KeyValueSyncBridge(keys: ["headingText"])
+ private let keyValueSyncBridge = KeyValueSyncBridge(keys: ["headingText", "colorTheme"])
init() {
keyValueSyncBridge.start()
diff --git a/ListlessWatch/Views/ItemListView.swift b/ListlessWatch/Views/ItemListView.swift
@@ -6,6 +6,8 @@ struct ItemListView: View {
let syncMonitor: CloudKitSyncMonitor
@AppStorage("headingText") private var headingText = "Items"
+ @AppStorage("colorTheme") private var colorThemeRaw = 0
+ private var colorTheme: ColorTheme { ColorTheme(rawValue: colorThemeRaw) ?? .pilbara }
@FetchRequest(
sortDescriptors: [
@@ -35,6 +37,7 @@ struct ItemListView: View {
item: item,
index: index,
totalActive: activeItems.count,
+ colorTheme: colorTheme,
onToggle: { toggleItem($0) }
)
}
@@ -46,6 +49,7 @@ struct ItemListView: View {
item: item,
index: 0,
totalActive: 0,
+ colorTheme: colorTheme,
onToggle: { toggleItem($0) }
)
}
diff --git a/ListlessWatch/Views/ItemRowView.swift b/ListlessWatch/Views/ItemRowView.swift
@@ -4,6 +4,7 @@ struct ItemRowView: View {
let item: ItemEntity
let index: Int
let totalActive: Int
+ let colorTheme: ColorTheme
let onToggle: (ItemEntity) -> Void
var body: some View {
@@ -15,7 +16,7 @@ struct ItemRowView: View {
.foregroundColor(
item.isCompleted
? .secondary
- : cachedItemColor(forIndex: index, total: totalActive)
+ : cachedItemColor(forIndex: index, total: totalActive, theme: colorTheme)
)
.font(.system(size: 17))
@@ -33,7 +34,7 @@ struct ItemRowView: View {
: AnyView(
ZStack(alignment: .top) {
Color(white: 0.15)
- cachedItemColor(forIndex: index, total: totalActive)
+ cachedItemColor(forIndex: index, total: totalActive, theme: colorTheme)
.frame(height: 3)
}
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
diff --git a/ListlessiOS/ListlessiOSApp.swift b/ListlessiOS/ListlessiOSApp.swift
@@ -63,7 +63,7 @@ struct ListlessiOSApp: App {
@UIApplicationDelegateAdaptor(IOSAppDelegate.self) var appDelegate
@AppStorage("appearanceMode") private var appearanceMode = 0
private let persistenceController: PersistenceController
- private let keyValueSyncBridge = KeyValueSyncBridge(keys: ["headingText"])
+ private let keyValueSyncBridge = KeyValueSyncBridge(keys: ["headingText", "colorTheme"])
init() {
let isUITesting = ProcessInfo.processInfo.arguments.contains("UI_TESTING")