npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

expo-icloud-kv

v0.1.2

Published

Async key/value storage for React Native / Expo backed by iCloud (NSUbiquitousKeyValueStore) — sync small pieces of app state across a user's Apple devices with a Promise-based API.

Readme

expo-icloud-kv

npm downloads CI License Platform Expo

Features

  • Promise API:
    • getItem, setItem, removeItem, getAllKeys, synchronize — same shape as AsyncStorage, so migration is a search-and-replace.
  • Cross-device sync:
    • Values written on iPhone show up on iPad and Mac within seconds (typically) or minutes (worst case) — no server, no accounts, no code.
  • External change events:
    • Subscribe to onExternalChange and react when another device writes, when the initial sync completes, when the user switches iCloud accounts, or when you hit the quota.
  • Config plugin included:
    • Add one line to app.json and the com.apple.developer.ubiquity-kvstore-identifier entitlement is wired up for you — no manual Xcode surgery.
  • Cross-platform safe:
    • Every call is a no-op on Android (getItem resolves null, getAllKeys resolves [], etc.) so you can call it unconditionally from shared code.
  • Expo Modules API:
    • Ships as a modern Expo Module — no legacy .m bridge, no manual linking. Just npx expo install and go.

Installation

npx expo install expo-icloud-kv

Then rebuild your dev client (this package uses native code, so it is not available in Expo Go):

npx expo prebuild
npx expo run:ios

Setup

Add the config plugin to your app.json:

{
  "expo": {
    "plugins": [
      [
        "expo-icloud-kv",
        {
          "identifier": "$(TeamIdentifierPrefix)com.example.myapp"
        }
      ]
    ]
  }
}

identifierrequired. The value of the com.apple.developer.ubiquity-kvstore-identifier entitlement. The $(TeamIdentifierPrefix) prefix is a build-time variable that Xcode expands to your Apple Team ID plus a dot. In most projects you'll want the identifier to end with your app's bundle ID, e.g. "$(TeamIdentifierPrefix)com.example.myapp".

containersoptional string[]. Set this only if your app also uses CloudKit / iCloud Documents. Leaving it empty ([]) is the correct default for pure key/value sync; the plugin writes an empty array automatically.

servicesoptional string[]. Set to e.g. ["CloudKit"] if you want the plugin to also enable full iCloud services. Omit for pure KV sync.

You also need to enable the iCloud → Key-value storage capability on your App ID in the Apple Developer portal. Expo cannot do this for you — do it once in the Apple Developer console when you register the bundle ID.

Usage

import * as ICloudKV from 'expo-icloud-kv';

// Write
await ICloudKV.setItem('pinnedGroups', JSON.stringify(['410101', '410102']));

// Read
const raw = await ICloudKV.getItem('pinnedGroups');
const pinned: string[] = raw ? JSON.parse(raw) : [];

// Force a sync (optional — sets/removes already sync automatically)
await ICloudKV.synchronize();

// Listen for changes made on other devices
const sub = ICloudKV.addExternalChangeListener(({ keys, reason }) => {
  console.log('iCloud pushed changes:', keys, 'reason:', reason);
});

// Later:
sub.remove();

API reference

| Function | Signature | Description | | -------- | --------- | ----------- | | getItem | (key: string) => Promise<string \| null> | Read a string. Resolves null when missing or on non-iOS. | | setItem | (key: string, value: string) => Promise<void> | Write a string and request a sync. | | removeItem | (key: string) => Promise<void> | Remove a key and request a sync. | | getAllKeys | () => Promise<string[]> | List every key currently in the store. | | synchronize | () => Promise<boolean> | Force a sync. false = entitlement missing / user signed out of iCloud. | | addExternalChangeListener | (cb: (event) => void) => EventSubscription | Subscribe to remote changes. Call .remove() to unsubscribe. | | isAvailable | boolean | true on iOS, false elsewhere — useful for gating UI. |

type ExternalChangeReason =
  | 'server'          // A change on another device propagated to this one.
  | 'initialSync'     // First sync after launch or account change.
  | 'quotaViolation'  // Exceeded 1 MB total / 1024 keys / 1 MB per value.
  | 'accountChange'   // User signed in/out of iCloud, or switched accounts.
  | 'unknown';

interface ExternalChangeEvent {
  keys: string[];
  reason: ExternalChangeReason;
}

Requirements

  • iOS 14.0+.
  • Expo SDK 54+ (uses Expo Modules API).
  • Xcode 15+.
  • Apple Developer account with the iCloud → Key-value storage capability enabled on the App ID.

Non-iOS calls are silent no-ops.

Limits

iCloud KV is designed for small user preferences, not app data:

  • 1 MB total storage per app.
  • 1024 keys maximum.
  • 1 MB per value (large blobs should live in CloudKit or iCloud Documents).
  • Sync is best-effort. Expect seconds to minutes of propagation delay — do not treat it as a strong-consistency store.

If you exceed the quota, you'll receive an onExternalChange event with reason: 'quotaViolation' and the newest writes will silently drop.

How it works

The module is a thin Swift wrapper around NSUbiquitousKeyValueStore.default using the Expo Modules API — no legacy RCTBridgeModule or Objective-C shim.

The config plugin adds two entries to <AppName>.entitlements:

  • com.apple.developer.ubiquity-kvstore-identifier — the identifier you pass in app.json.
  • com.apple.developer.icloud-container-identifiers — an empty array (the OS requires the key even if you're not using CloudKit).

External change notifications are delivered via NSUbiquitousKeyValueStore.didChangeExternallyNotification; the module subscribes automatically as soon as JS registers its first listener and unsubscribes on the last remove().

Contributing

The example/ directory contains a minimal Expo app that exercises every API.

git clone https://github.com/kostyabet/expo-icloud-kv
cd expo-icloud-kv
npm install
npm run build
cd example
npm install
npx expo run:ios

Pull requests welcome — please add a CHANGELOG.md entry.

Source code

GitHub repo

License

MIT

Support

Press star on our GitHub repo please!