126 lines
4.4 KiB
Swift
126 lines
4.4 KiB
Swift
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,
|
|
displayName: syncable.displayName,
|
|
role: syncable.role,
|
|
company: syncable.company,
|
|
email: syncable.email,
|
|
phone: syncable.phone,
|
|
website: syncable.website,
|
|
location: syncable.location,
|
|
isDefault: syncable.isDefault,
|
|
qrCodeImageData: syncable.qrCodeImageData
|
|
)
|
|
}
|
|
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 displayName: 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?
|
|
}
|