react-native-esim-compatibility-checker
v0.1.0
Published
Check whether a device supports eSIM, plus a browsable catalog of eSIM compatible devices.
Maintainers
Readme
react-native-esim-compatibility-checker
Check whether a device can run an eSIM, without making the user dial a code and interpret the result themselves.
Two independent features:
- Detection. Ask the device directly whether it supports eSIM.
- Catalog. A browsable, searchable list of eSIM compatible devices, for building a "supported devices" screen.
The two never mix. Detection reads the hardware. The catalog is display data.
Install
npm install react-native-esim-compatibility-checkerNo permissions and no entitlements are required on either platform.
Detection
import { checkEsimSupport } from 'react-native-esim-compatibility-checker';
const result = await checkEsimSupport();
if (result.state === 'supported') {
// Offer the eSIM flow.
} else if (result.state === 'unsupported') {
// Offer a physical SIM instead.
} else {
// Could not tell. Ask the user rather than guessing.
}result carries six fields:
| Field | Meaning |
| --- | --- |
| state | supported, unsupported or unknown |
| source | which signal decided, so a surprising verdict is debuggable |
| deviceIdentifier | raw hardware identifier, iPhone17,1 or SM-S928B |
| deviceName | human readable name, or null if not in the bundled map |
| deviceFamily | iphone, ipad, android or unknown |
| warning | set only when something is worth telling you, null otherwise |
unknown is a real answer
It means no signal could settle the question. Reporting unsupported there
would be a confident wrong answer, which is the exact failure this package
exists to avoid. Treat unknown as "ask the user", not as "no".
How each platform decides
iPhone. Apple's generation numbering splits exactly on eSIM. Generation 10 is iPhone 8, 8 Plus and X, none with eSIM. Generation 11 is XS, XS Max and XR, the first with it. So the rule is a comparison, not a list, and it stays correct for iPhones that do not exist yet.
iPad. There is no clean cutoff, so eSIM capable models come from a small
table. Family 8 and above qualify, plus the 7th generation iPad, whose family
siblings do not. A qualifying model then needs a detected cellular radio,
because the Wi-Fi only build of the same model has no eSIM and the identifier
does not distinguish them. No radio found means unknown, never unsupported.
Android. Two signals, either positive wins. EuiccManager.isEnabled() from
Android 9, and the android.hardware.telephony.euicc hardware feature from
Android 13. The second rescues devices where a carrier build disables the eSIM
profile assistant while the hardware is present. Below Android 9 there is no
eSIM API at all, so the answer is unsupported.
CoreTelephony is only ever a positive. Apple documents
supportsCellularPlan() as whether the device and your app meet provisioning
requirements, and it is entangled with the carrier entitlement, so it returns
false on plenty of eSIM capable hardware. A true from it can promote a result.
A false from it never overrides a model rule. When the model says supported and
CoreTelephony disagrees, you get a warning instead, because that is how a
regional variant without eSIM hardware shows up.
Simulators and emulators always report unknown. They cannot answer this,
and pretending otherwise teaches you to trust a result that means nothing.
Catalog
import {
getDevices,
getBrands,
getCategories,
searchDevices,
checkDeviceByName,
} from 'react-native-esim-compatibility-checker';
getCategories(); // ['phones', 'watches', 'laptops', 'tablets']
getDevices({ category: 'phones' }); // every listed phone
getDevices({ brand: 'Samsung' }); // one brand
getBrands('phones'); // brands within a category
searchDevices('pixel'); // free text over name and brand
checkDeviceByName('galaxy s25 plus'); // 'supported' | 'unsupported' | 'unknown'Name matching normalises case, spacing and punctuation, so galaxy s25 plus
finds Galaxy S25+.
checkDeviceByName returns unknown for anything not listed. The catalog holds
no negative entries, so absence proves nothing.
Keeping it current
The catalog ships inside the package, so the library works offline and needs no hosting. To push new devices without a release, refresh it from a URL you own:
import { refreshCatalog, restoreCatalog } from 'react-native-esim-compatibility-checker';
await refreshCatalog('https://example.com/esim-catalog.json');A failed or malformed refresh leaves the bundled catalog in place, so the
package never degrades below what it shipped with. A payload whose
schemaVersion does not match is rejected rather than half applied.
Persistence is yours to choose. The package ships no storage dependency:
await restoreCatalog({ get: readFromYourStorage }); // at startup
await refreshCatalog(url, { set: writeToYourStorage }); // laterWhat this package does not do
It answers whether the hardware can run an eSIM. It does not report whether a profile is already installed, and it does not expose the EID. Both need carrier privileges a third party library cannot have.
Regenerating the data
node scripts/generate-apple-device-names.mjs # identifier to name map
node scripts/convert-catalog.mjs <source.txt> # catalog from the source listThe name map is cosmetic. A missing entry shows the raw identifier and can never change a verdict.
License
MIT
