Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
281c9d5e91
commit
9dd9b63f9c
@ -17,6 +17,11 @@ actor SyncHelper {
|
|||||||
public func updateConfiguration(_ configuration: SyncConfiguration) {
|
public func updateConfiguration(_ configuration: SyncConfiguration) {
|
||||||
self.configuration = configuration
|
self.configuration = configuration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Exposes the current max auto-sync size for filtering outbound payloads.
|
||||||
|
public func maxAutoSyncSize() -> Int {
|
||||||
|
configuration.maxAutoSyncSize
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Public Interface
|
// MARK: - Public Interface
|
||||||
|
|
||||||
@ -68,10 +73,15 @@ actor SyncHelper {
|
|||||||
guard WCSession.isSupported() else { return false }
|
guard WCSession.isSupported() else { return false }
|
||||||
|
|
||||||
let session = WCSession.default
|
let session = WCSession.default
|
||||||
guard session.activationState == .activated else { return false }
|
guard session.activationState == .activated else {
|
||||||
|
Logger.debug("<<< [SYNC] WCSession not activated")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
return session.isPaired && session.isWatchAppInstalled
|
let isAvailable = session.isPaired && session.isWatchAppInstalled
|
||||||
|
Logger.debug("<<< [SYNC] WCSession status paired=\(session.isPaired) installed=\(session.isWatchAppInstalled) reachable=\(session.isReachable)")
|
||||||
|
return isAvailable
|
||||||
#else
|
#else
|
||||||
return true
|
return true
|
||||||
#endif
|
#endif
|
||||||
@ -100,7 +110,10 @@ actor SyncHelper {
|
|||||||
guard session.isPaired, session.isWatchAppInstalled else { return }
|
guard session.isPaired, session.isWatchAppInstalled else { return }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Logger.info(">>> [SYNC] Sending application context for key: \(keyName) (\(data.count) bytes)")
|
||||||
try session.updateApplicationContext([keyName: data])
|
try session.updateApplicationContext([keyName: data])
|
||||||
|
let contextKeys = session.applicationContext.keys.sorted().joined(separator: ", ")
|
||||||
|
Logger.info("<<< [SYNC] Application context updated for key: \(keyName). Keys now: [\(contextKeys)]")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupSession() {
|
private func setupSession() {
|
||||||
|
|||||||
@ -553,6 +553,81 @@ public actor StorageRouter: StorageProviding {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempts to sync any registered keys that already have stored values.
|
||||||
|
/// This is useful for bootstrapping watch data after app launch or reconnection.
|
||||||
|
public func syncRegisteredKeysIfNeeded() async {
|
||||||
|
let isAvailable = await sync.isSyncAvailable()
|
||||||
|
guard isAvailable else {
|
||||||
|
Logger.debug("<<< [SYNC] Skipping bootstrap sync: WatchConnectivity unavailable")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.debug(">>> [SYNC] Starting bootstrap sync for registered keys")
|
||||||
|
for entry in registeredKeys.values {
|
||||||
|
let descriptor = entry.descriptor
|
||||||
|
guard descriptor.availability == .all || descriptor.availability == .phoneWithWatchSync else {
|
||||||
|
Logger.debug("<<< [SYNC] Skipping key \(descriptor.name): availability=\(descriptor.availability)")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
guard descriptor.syncPolicy != .never else {
|
||||||
|
Logger.debug("<<< [SYNC] Skipping key \(descriptor.name): syncPolicy=\(descriptor.syncPolicy)")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
guard let storedData = try await retrieve(for: descriptor) else {
|
||||||
|
Logger.debug("<<< [SYNC] No stored data for key \(descriptor.name)")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
try await sync.syncIfNeeded(
|
||||||
|
data: storedData,
|
||||||
|
keyName: descriptor.name,
|
||||||
|
availability: descriptor.availability,
|
||||||
|
syncPolicy: descriptor.syncPolicy
|
||||||
|
)
|
||||||
|
Logger.debug("<<< [SYNC] Bootstrapped context for key: \(descriptor.name) (\(storedData.count) bytes)")
|
||||||
|
} catch {
|
||||||
|
Logger.error("Failed to bootstrap sync for key: \(descriptor.name)", error: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds a snapshot of syncable key data for immediate watch requests.
|
||||||
|
public func syncSnapshot() async -> [String: Data] {
|
||||||
|
let isAvailable = await sync.isSyncAvailable()
|
||||||
|
guard isAvailable else {
|
||||||
|
Logger.debug("<<< [SYNC] Skipping snapshot: WatchConnectivity unavailable")
|
||||||
|
return [:]
|
||||||
|
}
|
||||||
|
|
||||||
|
let maxAutoSyncSize = await sync.maxAutoSyncSize()
|
||||||
|
var payload: [String: Data] = [:]
|
||||||
|
|
||||||
|
for entry in registeredKeys.values {
|
||||||
|
let descriptor = entry.descriptor
|
||||||
|
guard descriptor.availability == .all || descriptor.availability == .phoneWithWatchSync else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
guard descriptor.syncPolicy != .never else { continue }
|
||||||
|
|
||||||
|
do {
|
||||||
|
guard let storedData = try await retrieve(for: descriptor) else { continue }
|
||||||
|
|
||||||
|
if descriptor.syncPolicy == .automaticSmall, storedData.count > maxAutoSyncSize {
|
||||||
|
Logger.debug("<<< [SYNC] Snapshot skip \(descriptor.name): size exceeds maxAutoSyncSize")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
payload[descriptor.name] = storedData
|
||||||
|
} catch {
|
||||||
|
Logger.error("Failed to build sync snapshot for key: \(descriptor.name)", error: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.debug("<<< [SYNC] Snapshot built with \(payload.count) keys")
|
||||||
|
return payload
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Internal Sync Handling
|
// MARK: - Internal Sync Handling
|
||||||
|
|
||||||
/// Internal method to update storage from received sync data.
|
/// Internal method to update storage from received sync data.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user