React Native Cloud Sync
✨ Features
- ☁️ Four providers behind one API:
NSUbiquitousKeyValueStore(Apple's small key-value iCloud store), CloudKit records, iCloud Drive documents and Google DriveappDataFolder. Use them directly or through one facade. - 🔄 Auto-sync between iOS and Android in both directions:
mirrorwrites to iCloud and Drive together,resolvereads back whichever copy is newest, so either platform can be where the user started. - 🍏 CloudKit reaches Android and web too, via CloudKit Web Services, against the same private database your iOS app uses.
- 📂
icloudDocumentswrites into the user's own iCloud Drive, so the files show up in Files.app. - 🚨 Every failure is a typed rejection (
ERR_NOT_SIGNED_IN,ERR_QUOTA_EXCEEDED,ERR_RATE_LIMITEDwithretryAfterMs, ...);nullmeans only "key doesn't exist". - 👤 All five
CKAccountStatusvalues surface as-is, plusonAccountChangewithidentityChanged, which drops the previous account's caches and queued writes. - 🔔
onRemoteChangefires on every provider and the facade, including Google Drive via its change cursor. - 📦 Small values go to the key-value store, larger ones to a
CKRecordfield, binary to aCKAssetor a resumable Drive upload. The size check picks the target. - 🔁 Retryable failures queue into a durable outbox: backoff honours retry hints, auto-drains on foreground, bounded, never overwrites a newer write.
- 🧺
multiGet/multiSet/multiRemove/clearbatch for real, one request per provider. - 🪝 React hooks from
/hooks:useCloudItem,useCloudItems,useCloudCollection,useAccountStatus,usePendingWrites. They drop stale responses and neversetStateafter unmount. - 🔐
cloudKitEncrypteduses CloudKit's ownencryptedValues, so only ciphertext leaves the device; every other provider has acodecseam for your own cipher. - 🧪 An in-memory provider with fault injection, plus the native mock, both exported (
/testing,/jest-mock), so every failure path is testable in Jest. - ⚙️ React Native 0.71 through 0.86+, old and new architecture, with the
#ifdefbridge for the legacy one.
💡 Why?
Cloud storage in React Native is fragmented into single-provider wrappers repeating the same defects: two ship a setItem that reports a failed write as a success (one checks the wrong error variable, the other discards the result entirely), and a third flattens five iCloud account states into one boolean.
A catch { return null } makes "not signed in", "offline", "out of storage" and "no such key" indistinguishable. The app can't tell the user anything useful, or decide whether to retry.
This library was built error contract first, providers second.
📚 Upstream documentation
This package is a wrapper. When something behaves unexpectedly, the answer is usually in Apple's or Google's docs.
Apple
NSUbiquitousKeyValueStore | The iCloud key-value store; 1 MB / 1024-key limits |
CloudKit · CKDatabase · CKRecord | Records in the user's private database |
CKAsset · CKRecordZone | Binary assets and custom zones |
CKAccountStatus | The five account states this package surfaces verbatim |
| CloudKit Web Services | The REST API behind the Android and web paths |
| Authentication · Data size limits · Error codes | Worth reading before shipping CloudKit on Android |
| iCloud entitlements | The keys the config plugin writes |
| CloudKit Console | Where containers, schemas, API tokens and the sign-in callback live |
The appDataFolder | The hidden per-app folder this package stores into |
Drive files resource | The REST endpoints behind the provider |
| Drive API scopes | Why the scope is drive.appdata |