import Foundation import WatchConnectivity /// Manages WatchConnectivity session and receives card data from iPhone @MainActor final class WatchConnectivityService: NSObject { static let shared = WatchConnectivityService() /// Callback when cards are received from iPhone var onCardsReceived: (([WatchCard]) -> Void)? private var session: WCSession? private override init() { super.init() if WCSession.isSupported() { session = WCSession.default session?.delegate = self session?.activate() WatchDesign.debugLog("WatchConnectivity: Session activating...") } else { WatchDesign.debugLog("WatchConnectivity: Not supported") } } /// Check for any existing application context on launch func checkForExistingContext() { guard let session = session else { return } let context = session.receivedApplicationContext if !context.isEmpty { WatchDesign.debugLog("WatchConnectivity: Found existing context on launch") processReceivedContext(context) } else { WatchDesign.debugLog("WatchConnectivity: No existing context found") } } private func processReceivedContext(_ context: [String: Any]) { guard let cardsData = context["cards"] as? Data else { WatchDesign.debugLog("WatchConnectivity: No cards data in context") return } WatchDesign.debugLog("WatchConnectivity: Received \(cardsData.count) bytes") do { let syncableCards = try JSONDecoder().decode([SyncableCard].self, from: cardsData) let watchCards = syncableCards.map { syncable in WatchCard( id: syncable.id, fullName: syncable.fullName, role: syncable.role, company: syncable.company, email: syncable.email, phone: syncable.phone, website: syncable.website, location: syncable.location, isDefault: syncable.isDefault, qrCodeImageData: syncable.qrCodeImageData, appClipQRCodeImageData: syncable.appClipQRCodeImageData ) } WatchDesign.debugLog("WatchConnectivity: Decoded \(watchCards.count) cards") onCardsReceived?(watchCards) } catch { WatchDesign.debugLog("WatchConnectivity: ERROR - Failed to decode: \(error)") } } } // MARK: - WCSessionDelegate extension WatchConnectivityService: WCSessionDelegate { nonisolated func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { Task { @MainActor in if let error = error { WatchDesign.debugLog("WatchConnectivity: Activation failed: \(error)") } else { WatchDesign.debugLog("WatchConnectivity: Activated with state: \(activationState.rawValue)") // Check for existing context after activation checkForExistingContext() } } } nonisolated func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String: Any]) { Task { @MainActor in WatchDesign.debugLog("WatchConnectivity: Received application context update") processReceivedContext(applicationContext) } } nonisolated func session(_ session: WCSession, didReceiveUserInfo userInfo: [String: Any] = [:]) { Task { @MainActor in WatchDesign.debugLog("WatchConnectivity: Received userInfo transfer") processReceivedContext(userInfo) } } nonisolated func session(_ session: WCSession, didReceiveMessage message: [String: Any]) { Task { @MainActor in WatchDesign.debugLog("WatchConnectivity: Received message") processReceivedContext(message) } } } /// Syncable card structure matching iOS side (for decoding) private struct SyncableCard: Codable, Identifiable { let id: UUID var fullName: String var role: String var company: String var email: String var phone: String var website: String var location: String var isDefault: Bool var pronouns: String var bio: String var linkedIn: String var twitter: String var instagram: String var qrCodeImageData: Data? var appClipQRCodeImageData: Data? }