Add Apple Watch companion app with complications and WCSession sync
- Add watchOS app target with mood voting UI (5 mood buttons) - Add WidgetKit complications (circular, corner, inline, rectangular) - Add WatchConnectivityManager for bidirectional sync between iOS and watch - iOS app acts as central coordinator - all mood logging flows through MoodLogger - Watch votes send to iPhone via WCSession, iPhone logs and notifies watch back - Widget votes use openAppWhenRun=true to run MoodLogger in main app process - Add #if !os(watchOS) guards to Mood.swift and Random.swift for compatibility - Update SKStoreReviewController to AppStore.requestReview (iOS 18 deprecation fix) - Watch reads user's moodImages preference from GroupUserDefaults for emoji style 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
88
Feels Watch App/WatchConnectivityManager.swift
Normal file
88
Feels Watch App/WatchConnectivityManager.swift
Normal file
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// WatchConnectivityManager.swift
|
||||
// Feels Watch App
|
||||
//
|
||||
// Watch-side connectivity - sends mood to iPhone for centralized logging.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import WatchConnectivity
|
||||
import WidgetKit
|
||||
import os.log
|
||||
|
||||
/// Watch-side connectivity manager
|
||||
/// Sends mood votes to iPhone for centralized logging
|
||||
final class WatchConnectivityManager: NSObject, ObservableObject {
|
||||
|
||||
static let shared = WatchConnectivityManager()
|
||||
|
||||
private static let logger = Logger(subsystem: "com.tt.ifeel.watchkitapp", category: "WatchConnectivity")
|
||||
|
||||
private var session: WCSession?
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
|
||||
if WCSession.isSupported() {
|
||||
session = WCSession.default
|
||||
session?.delegate = self
|
||||
session?.activate()
|
||||
Self.logger.info("WCSession activated")
|
||||
} else {
|
||||
Self.logger.warning("WCSession not supported")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Watch → iOS
|
||||
|
||||
/// Send mood to iOS app for centralized logging
|
||||
func sendMoodToPhone(mood: Int, date: Date) -> Bool {
|
||||
guard let session = session,
|
||||
session.activationState == .activated else {
|
||||
Self.logger.warning("WCSession not ready")
|
||||
return false
|
||||
}
|
||||
|
||||
let message: [String: Any] = [
|
||||
"action": "logMood",
|
||||
"mood": mood,
|
||||
"date": date.timeIntervalSince1970
|
||||
]
|
||||
|
||||
// Use transferUserInfo for guaranteed delivery
|
||||
session.transferUserInfo(message)
|
||||
Self.logger.info("Sent mood \(mood) to iPhone")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - WCSessionDelegate
|
||||
|
||||
extension WatchConnectivityManager: WCSessionDelegate {
|
||||
|
||||
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
|
||||
if let error = error {
|
||||
Self.logger.error("WCSession activation failed: \(error.localizedDescription)")
|
||||
} else {
|
||||
Self.logger.info("WCSession activated: \(activationState.rawValue)")
|
||||
}
|
||||
}
|
||||
|
||||
// Receive reload notification from iOS
|
||||
func session(_ session: WCSession, didReceiveUserInfo userInfo: [String: Any] = [:]) {
|
||||
if userInfo["action"] as? String == "reloadWidgets" {
|
||||
Self.logger.info("Received reload notification from iPhone")
|
||||
Task { @MainActor in
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func session(_ session: WCSession, didReceiveMessage message: [String: Any]) {
|
||||
if message["action"] as? String == "reloadWidgets" {
|
||||
Task { @MainActor in
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user