Remote config, update walls and maintenance mode for React Native. Decided at the edge, verified on device.
@ripstop/react-native is not on npm yet. Until it is, install straight from
GitHub. The package builds itself on install:
npm install github:ripstop-dev/ripstop-react-nativeNo native modules, no pod install, no config plugin. It is JavaScript.
import AsyncStorage from '@react-native-async-storage/async-storage';
import DeviceInfo from 'react-native-device-info';
import { RipstopProvider, asyncStorageAdapter } from '@ripstop/react-native';
export default function App() {
return (
<RipstopProvider
apiKey="rs_pub_your_key"
appVersion={DeviceInfo.getVersion()}
storage={asyncStorageAdapter(AsyncStorage)}
>
<MyApp />
</RipstopProvider>
);
}That's the whole integration. The provider renders your app until a decision says otherwise, then shows the right wall.
The other Ripstop SDKs read the installed version themselves, Swift from
CFBundleShortVersionString and Flutter from package_info_plus. React Native
cannot. Nothing in the framework exposes the host app's version: Platform.OS
and Platform.Version describe the operating system, and
Platform.constants.reactNativeVersion describes React Native. Reading the
bundle means a native module, and this package has none. See the installation
note above; that promise is worth more than saving you one argument.
So pass it, from whichever of these you already have:
import DeviceInfo from 'react-native-device-info'; // DeviceInfo.getVersion()
import Constants from 'expo-constants'; // Constants.expoConfig?.versionPass the version only: 1.4.0, not 1.4.0 (312). Build numbers are not part
of a semantic version, and the rules are compared as semantic versions.
const { decision, values, snooze, loading } = useRipstop();
if (decision.type === 'force') return <MyUpdateScreen {...decision} />;Pass walls={false} to the provider and nothing is rendered for you.
The cache is in-memory unless you give it somewhere to live. AsyncStorage is a
separate package and the SDK will not add it to your app on your behalf. Pass
your existing instance through asyncStorageAdapter and the cache survives
restarts.
| Situation | What your app does |
|---|---|
| No network | Uses the last signed payload |
| No network, no cache | none, so your app runs unrestricted |
| Server returns 500, or times out | Cache, then normal |
| Signature doesn't verify | Discarded. A forged payload can never wall your app |
| Cache tampered with | Re-verified on read, so it grants nothing |
| Maintenance on, then network lost | The wall stays, until a fresh signed payload clears it |
React Native doesn't have it. @noble/ed25519 backs its SHA-512 with WebCrypto
by default, so a naive integration verifies fine in every test and throws on the
first real device. This package wires SHA-512 explicitly from @noble/hashes,
and has a test that deletes crypto.subtle before importing anything, because
a test that runs on Node's fallback would pass whether or not the fix was there.
Every Ripstop SDK runs the same vectors.json: version ordering, evaluation
order, snooze accounting, the fail-open state machine. npm test runs it here.
Full docs: ripstop.dev/docs/react-native
MIT
