Update Helpers, Services

Summary:
- Sources: Helpers, Services
- Added symbols: func maxAutoSyncSize, func syncRegisteredKeysIfNeeded, func syncSnapshot

Stats:
- 2 files changed, 90 insertions(+), 2 deletions(-)
This commit is contained in:
Matt Bruce 2026-01-16 15:51:34 -06:00
parent c2b17a4b7f
commit 358e7a0ffc
2 changed files with 90 additions and 2 deletions

View File

@ -17,6 +17,11 @@ actor SyncHelper {
public func updateConfiguration(_ configuration: SyncConfiguration) {
self.configuration = configuration
}
/// Exposes the current max auto-sync size for filtering outbound payloads.
public func maxAutoSyncSize() -> Int {
configuration.maxAutoSyncSize
}
// MARK: - Public Interface
@ -68,10 +73,15 @@ actor SyncHelper {
guard WCSession.isSupported() else { return false }
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)
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
return true
#endif
@ -100,7 +110,10 @@ actor SyncHelper {
guard session.isPaired, session.isWatchAppInstalled else { return }
#endif
Logger.info(">>> [SYNC] Sending application context for key: \(keyName) (\(data.count) bytes)")
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() {

View File

@ -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
/// Internal method to update storage from received sync data.