SecureStorageSample/SecureStorageSample Watch App/Services/WatchConnectivityService.swift

110 lines
3.8 KiB
Swift

import Foundation
import SharedKit
import WatchConnectivity
@MainActor
final class WatchConnectivityService: NSObject, WCSessionDelegate {
static let shared = WatchConnectivityService()
private let store: WatchProfileStore
private var handlers: [String: WatchDataHandling] = [:]
private override init() {
self.store = .shared
super.init()
registerDefaultHandlers()
activateIfSupported()
loadCurrentContext()
}
private func registerDefaultHandlers() {
let profileHandler = UserProfileWatchHandler(store: store)
registerHandler(profileHandler)
let syncableSettingHandler = SyncableSettingWatchHandler(store: store)
registerHandler(syncableSettingHandler)
}
func registerHandler(_ handler: WatchDataHandling) {
handlers[handler.key] = handler
}
private func activateIfSupported() {
guard WCSession.isSupported() else {
store.setStatus("WatchConnectivity not supported")
return
}
let session = WCSession.default
session.delegate = self
session.activate()
}
private func loadCurrentContext() {
guard WCSession.isSupported() else { return }
let context = WCSession.default.applicationContext
let keys = context.keys.sorted().joined(separator: ", ")
Logger.debug("Watch loaded current context keys: [\(keys)]")
handleContext(context)
}
private func handleContext(_ context: [String: Any]) {
let keys = context.keys.sorted().joined(separator: ", ")
Logger.debug("Watch handling context keys: [\(keys)]")
for (key, handler) in handlers {
guard let data = context[key] as? Data else { continue }
handler.handle(data: data)
}
}
func session(
_ session: WCSession,
activationDidCompleteWith activationState: WCSessionActivationState,
error: Error?
) {
if let error {
Logger.error("Watch WCSession activation failed", error: error)
} else {
Logger.debug("Watch WCSession activated with state: \(activationState.rawValue)")
}
updateReachability(using: session)
loadCurrentContext()
requestSyncIfNeeded()
}
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String: Any]) {
Logger.debug("Watch received application context with \(applicationContext.count) keys")
handleContext(applicationContext)
}
func sessionReachabilityDidChange(_ session: WCSession) {
Logger.debug("Watch reachability changed: reachable=\(session.isReachable)")
updateReachability(using: session)
requestSyncIfNeeded()
}
private func updateReachability(using session: WCSession) {
store.setPhoneReachable(session.isReachable)
}
private func requestSyncIfNeeded() {
guard WCSession.isSupported() else { return }
let session = WCSession.default
if session.isReachable {
store.setPhoneReachable(true)
store.setStatus("Requesting sync from iPhone...")
session.sendMessage([WatchSyncMessageKeys.requestSync: true]) { reply in
Logger.debug("Watch received sync reply with \(reply.count) keys")
self.handleContext(reply)
} errorHandler: { error in
Logger.error("Watch sync request failed", error: error)
}
Logger.debug("Watch requested sync from iPhone (reachable)")
} else {
store.setPhoneReachable(false)
store.setStatus("Open the iPhone app to sync.")
session.transferUserInfo([WatchSyncMessageKeys.requestSync: true])
Logger.debug("Watch queued sync request for iPhone launch")
}
}
}