Compatibility
How the fingerprint of the native contract and binary version ranges keep a release away from the store builds it would break.
A release runs only on the store builds whose native layer it was built for. HotCodePush decides that with a fingerprint: a hash of the project's native dependencies, stamped into every build and computed for every bundle at upload.
A release carries its bundle's fingerprint as a condition and is offered only to devices whose binary carries the same one. A binary range narrows a release further, to the store versions you name.
What the fingerprint hashes
The fingerprint hashes the native contract, not the native project. It reads the committed lockfile, package-lock.json, pnpm-lock.yaml or yarn.lock, and takes from it only:
- the packages that ship native code: a React Native module's podspec or Gradle file, a Capacitor plugin's
iosorandroidentry, a Cordova plugin'splugin.xml. - the runtime packages,
react-native,@capacitor/core,@capacitor/ios,@capacitor/android,cordova-iosandcordova-android. - the files under the paths
hotcodepush.jsonlists asextraFingerprintPaths, for native code of your own, hashed file by file.
Each package enters as its name, version and lockfile integrity. Everything else in the lockfile is skipped: a pure JavaScript dependency travels inside the bundle. The ios/ and android/ trees are never hashed.
hotcodepush fingerprint prints the contributing packages and files, and hotcodepush fingerprint diff shows which of them moved between two of its outputs.
When it changes
The fingerprint moves exactly when a store release would be due anyway:
- a plugin or native module added, removed or bumped, a patch release included.
- a framework major.
- native code of your own under
extraFingerprintPathschanged.
Between those events a team ships under one fingerprint and types nothing per release. It is the same on every machine, since the lockfile is committed.
The binary range
release create --binary-version <range> adds a binary condition beside the fingerprint. A device must satisfy every condition, so the range narrows the release to the versions it names and never widens it past its fingerprint.
The range covers the store build's version with the build number as a fourth component: 2.4.1 matches every build of 2.4.1 and 2.4.1.57 names build 57. A version of fewer than three components reads with zeros. The syntax is a subset of npm's ranges, the same in the CLI, the API and every SDK:
| Form | Reads as | Example |
|---|---|---|
>=, >, <=, < or = before a version | Compares over the components the version names. | >=2.3.0, and >2.4.1 excludes every build of 2.4.1 |
| a bare version of three or more components | Requires equality. | 2.4.1, 2.4.1.57 |
a partial version, or trailing x or * components | Names an interval. | 2.4 and 2.4.x are >=2.4 <2.5, and x matches everything |
| comparators separated by whitespace | Requires all of them. | >=2.3.0 <3.0.0 |
alternatives joined by || | Requires any of them. | <2.4.1 || >2.4.1 |
^, ~, hyphen ranges, prerelease tags, an operator on a wildcard and a numeric component after a wildcard do not parse. The API refuses such a range when the release is created, with the validation rule range_syntax, and the CLI checks before the upload. The os condition, release create --os-version <range>, uses the same syntax over the OS version.
What happens on a mismatch
A device whose binary carries another fingerprint, or a version outside the range, skips the release and keeps running what it has. It reports the skip once. The console shows it per release, and Troubleshooting explains it.
The devices that do not match are not left behind: the channel's index keeps the newest active release for every native layer released to it. So an old store build keeps receiving the releases built for it. A layer no device has reported for the retention period on Limits and fair use drops out of the index.
At upload the CLI compares the bundle's fingerprint with the app's binaries and warns when it matches none. A release that would reach nobody is caught before it ships. The audience preview on release create --dry-run and in the console counts the devices a release reaches, as Target devices with attributes shows.
The store build and the binary
The build step init wires into the native build writes the resource file the SDK reads at start. It holds the app id and channel, the fingerprint, the manifest of the embedded bundle and the build time. A release created before the build is never applied on it.
A store build, an Xcode archive or a Gradle release variant, also creates the binary in HotCodePush. The binary records the platform, version and build number, the fingerprint and the embedded bundle. The first update after a store release can then be a delta.
A developer's run or a debug build creates no binary and still updates, its first update as the full pack. The console's Binaries page and binary list show every store build with its fingerprint and the devices running it.
Last updated on
How an update reaches a device
How a device finds, checks, verifies and applies an update, and what keeps your app running when something fails.
Rollouts and auto-pause
How a rollout percentage picks devices, how automatic progression raises it and how auto-pause stops a release when its failure rate crosses a threshold.