110 lines
3.8 KiB
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")
|
|
}
|
|
}
|
|
}
|