@rific/updater
v0.1.1
Published
OTA update hook for Expo apps — silent background fetch on foreground, manual check with confirmation dialog
Maintainers
Readme
@rific/updater
OTA update hook for Expo apps. Silently stages updates in the background when the app is foregrounded, and exposes a manual check() for settings screens. No surprise restarts — the user always confirms before the app reloads.
Install
npm install @rific/updaterPeer dependencies: expo-updates, react, react-native
Usage
Basic
import { useUpdater } from '@rific/updater'
const { check, checking, updateReady } = useUpdater()Call check() from a "Check for Updates" button. The updateReady flag goes true after a silent background fetch — use it to show a badge on your settings icon.
Settings screen
const { check, checking, updateReady } = useUpdater({
onError: (msg) => toast(msg),
})
<MenuItem
title="Check for Update"
caption={`v${release.otaVersion}${updateReady ? ' — update ready' : ''}`}
loading={checking}
onPress={check}
/>With a custom confirm dialog
const { check, checking } = useUpdater({
onConfirm: async (manifest) => {
// return true to proceed with reload, false to cancel
return myCustomDialog(manifest)
},
})Disable automatic foreground check
const { check, checking } = useUpdater({ autoCheck: false })API
useUpdater(options?)
interface UseUpdaterOptions {
autoCheck?: boolean // default: true
onConfirm?: (manifest: UpdateManifest) => Promise<boolean>
onError?: (message: string) => void
}
interface UseUpdaterReturn {
check: () => Promise<void>
checking: boolean
updateReady: boolean
}| Option | Default | Description |
|--------|---------|-------------|
| autoCheck | true | Registers an AppState listener that silently fetches available updates whenever the app comes to the foreground. Disable for games or apps that want full manual control. |
| onConfirm | — | Custom confirmation dialog. Receives the update manifest, must return Promise<boolean> — true to reload, false to cancel. Defaults to a native Alert showing the release date and metadata message. |
| onError | — | Called with an error message string if check() throws. Defaults to Alert.alert. |
| Return | Description |
|--------|-------------|
| check() | Manual update check. Shows a dev/web guard alert if unsupported. If a background fetch already staged an update, uses that manifest directly (no extra network call). Clears updateReady on completion regardless of whether the user confirmed. |
| checking | true while check() is in flight. Safe to drive a loading spinner. Concurrent calls are ignored via a ref guard. |
| updateReady | true after the background fetch successfully staged an update. Cleared when check() completes. Use to show a badge on a settings button. |
How updates work
Automatic (foreground): When autoCheck is true, the hook registers an AppState listener. Each time the app returns from background/inactive to active, it calls checkForUpdateAsync() + fetchUpdateAsync() silently. The downloaded bundle sits on disk — no prompt, no restart. The next cold launch automatically runs it.
Manual (check()): Runs the full flow — check (or reuse staged manifest) → confirmation dialog → reloadAsync(). The user sees what was released and chooses whether to restart now.
Web / DEV: Both are no-ops. check() shows an informational alert explaining why. The foreground listener is never registered.
OTA version constant
Each app maintains a local integer version displayed to users (separate from the semver app version). Bump it before pushing an OTA:
# from your app's root
npx rific-bump-ota src/constants/release.tsOr add to your app's package.json:
"scripts": {
"update:bump": "rific-bump-ota src/constants/release.ts"
}The script:
- Verifies git working directory is clean
- Increments
otaVersionin the target file - Auto-commits
"otaVersion N -> N+1"
File format expected (TypeScript or JS object literal):
export const release = {
otaVersion: 1
}The path argument defaults to src/constants/release.ts if omitted.
Context / design notes
- Named
@rific/updater(notexpo-updater) to avoid confusion with theexpo-updatespeer dependency check()uses a ref guard (checkingRef) rather than thecheckingstate to prevent concurrent calls — state batching means a second call could see stalefalsebefore the first render commitsupdateReadyand the staged manifest ref are cleared infinallyso they reset on both confirm and cancelonConfirmreplaces the defaultAlertentirely — useful in apps that have their own dialog primitive (e.g. aselect()utility or bottom sheet)- No Provider or context required — the hook is self-contained
Consuming apps
- Lumber (
../Lumber) — account screen, shows version + update badge - CashierFu-Utility (
../CashierFu-Utility) — settings modal, uses@rific/toasterforonError - Games (Setter, Hangman, Crumby, HexFleet, etc.) — use
autoCheck: true, no manual check needed
Local development (yalc)
# in this repo
yalc publish
# in the consuming app
yalc add @rific/updaterUse yalc not npm link — Metro doesn't resolve symlinks reliably.
