LocalData/Sources/LocalData/Services/SyncHelper.swift
Matt Bruce 4228fbc849 Update Models, Services + docs
Summary:
- Sources: Models, Services
- Docs: Proposal, README
- Added symbols: extension StorageKeys, struct UserTokenKey, typealias Value, enum KeychainAccessControl, enum KeychainAccessibility, actor EncryptionHelper (+38 more)
- Removed symbols: enum KeychainAccessControl, enum KeychainAccessibility, enum EncryptionConstants, func serialize, func deserialize, func applySecurity (+17 more)

Stats:
- 10 files changed, 1089 insertions(+), 348 deletions(-)
2026-01-18 14:53:25 -06:00

112 lines
3.4 KiB
Swift

import Foundation
#if os(iOS) || os(watchOS)
import WatchConnectivity
#endif
/// Actor that handles WatchConnectivity sync operations.
/// Manages data synchronization between iPhone and Apple Watch.
public actor SyncHelper {
public static let shared = SyncHelper()
/// Maximum data size for automatic sync (100KB).
public static let maxAutoSyncSize = 100_000
private init() {}
// MARK: - Public Interface
/// Syncs data to the paired device if appropriate.
/// - Parameters:
/// - data: The data to sync.
/// - keyName: The key name for the application context.
/// - availability: The platform availability setting.
/// - syncPolicy: The sync policy setting.
/// - Throws: `StorageError.dataTooLargeForSync` if data exceeds size limit for automatic sync.
public func syncIfNeeded(
data: Data,
keyName: String,
availability: PlatformAvailability,
syncPolicy: SyncPolicy
) throws {
#if os(iOS) || os(watchOS)
// Only sync for appropriate availability settings
guard availability == .all || availability == .phoneWithWatchSync else {
return
}
switch syncPolicy {
case .never:
return
case .automaticSmall:
guard data.count <= Self.maxAutoSyncSize else {
throw StorageError.dataTooLargeForSync
}
try performSync(data: data, keyName: keyName)
case .manual:
try performSync(data: data, keyName: keyName)
}
#endif
}
/// Manually triggers a sync for the given data.
/// - Parameters:
/// - data: The data to sync.
/// - keyName: The key name for the application context.
/// - Throws: Various errors if sync fails.
public func manualSync(data: Data, keyName: String) throws {
#if os(iOS) || os(watchOS)
try performSync(data: data, keyName: keyName)
#endif
}
/// Checks if sync is available.
/// - Returns: True if WatchConnectivity is supported and active.
public func isSyncAvailable() -> Bool {
#if os(iOS) || os(watchOS)
guard WCSession.isSupported() else { return false }
let session = WCSession.default
guard session.activationState == .activated else { return false }
#if os(iOS)
return session.isPaired && session.isWatchAppInstalled
#else
return true
#endif
#else
return false
#endif
}
/// Gets the current application context.
/// - Returns: The current application context dictionary.
public func currentContext() -> [String: Any] {
#if os(iOS) || os(watchOS)
guard WCSession.isSupported() else { return [:] }
return WCSession.default.applicationContext
#else
return [:]
#endif
}
// MARK: - Private Helpers
#if os(iOS) || os(watchOS)
private func performSync(data: Data, keyName: String) throws {
guard WCSession.isSupported() else { return }
let session = WCSession.default
guard session.activationState == .activated else { return }
#if os(iOS)
guard session.isPaired, session.isWatchAppInstalled else { return }
#endif
try session.updateApplicationContext([keyName: data])
}
#endif
}