You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Never adopt another device's store URI when importing legacy events. Object URIs are device-local, but importing a legacy (Ensembles 2 binary format) event copied the exporting device's URI into the local global-identifier table for identifiers not seen before. A foreign URI normally fails to resolve and self-heals, but on a device restored from another device's backup the store UUID matches, and the URI could resolve to an unrelated local object, sending integrated changes to the wrong object. First-seen identifiers now import without a URI; integration assigns the local one when it creates the object. Production JSON exports never carried URIs, so cloud data is unaffected. Documentation examples that showed object URIs as an identifier fallback were also corrected: they never match across devices, and a store migration or restore changes them locally. Prompted by a field report on identifier stability.
Dropbox: implement remote-change long-polling. The dropboxCloudFileSystemDidDetectRemoteFileChanges delegate callback shipped with documentation promising long-poll detection, but the engine behind it was never ported from Ensembles 2 — nothing called it. The backend now long-polls Dropbox (60-second polls against a recursive folder cursor, honoring the server's backoff hints) and fires the callback whenever the monitored folder changes, advancing the cursor first so each notification represents genuinely new activity. Monitoring starts automatically on connect(); startMonitoringRemoteChanges() / stopMonitoringRemoteChanges() give manual control. Note that this device's own uploads also trigger the callback; responding with a sync() is harmless. Verified against the SwiftyDropbox API and by compilation — live-service testing is welcome and feedback appreciated. Reported by Pascal (issue #1).
New: CoreDataEnsemble.syncableGlobalIdentifiers(forManagedObjects:). Apps that use CoreDataEnsemble directly (no container) had to hand-roll the Syncable lookup in their mandatory globalIdentifiersForManagedObjects delegate method — the containers' automatic discovery was private. The new helper resolves identifiers from the objects' Syncable conformances using the same shared implementation as the containers, so the delegate method becomes a one-line forward. The method stays required by design: attaching without any identifier source must fail loudly (missingGlobalIdentifierSource, 220) rather than silently identify nothing, so a defaulted implementation was not an option. The helper fails closed for a whole batch when any object's entity lacks a conformance. Requested by Pascal (issue #3).
Cloud backends now log the context of every failed operation. The Google Drive, pCloud, OneDrive, Supabase, WebDAV, Box, S3, Zip, and Memory backends reported failed transfers as a bare error code — a 401 from an expired token, a 403 quota block, a 429 rate limit, and a genuinely missing file were all indistinguishable "A file operation failed" (107) or similar. Every such site now logs, at the default .error level, the operation, the path, the HTTP status, and a truncated server response (or the underlying SDK error) immediately before throwing. The thrown errors themselves are unchanged: an NSError with a matching domain and code does not cast back to EnsembleError, so enriching the error value would silently break error as? EnsembleError classification in the framework and in apps — the context goes in the log instead, where it lands in the console without any configuration. Routine existence probes (checking whether a file exists before uploading a new one) are exempted so the error log stays signal. The core event store also names the offending path when its directory layout is corrupted. Prompted by a customer report where a bare 107 gave nothing to go on.
README rewritten around the questions evaluators actually ask. It now explains what a merge does (attribute-level last-writer-wins with causal ordering, add-wins for to-many relationships), how event history is compacted into baselines so sync data stays bounded, and what happens on a device's first sync when local data already exists (seed policy, deduplication, CloudKit capability setup). Stale facts were corrected along the way: the free-backend list now matches the license check (iCloud Drive is free; SwiftData is an integration, not a backend), and the test count and Swift version requirements are current. All distribution READMEs are now generated from a single template, so they can no longer drift apart.