This page describes how developers can integrate eSIM provisioning into their React Native apps using platform-native APIs to setup OTR as a mobile network on a mobile phone.
Overview & Use Cases
eSIM feature lets users add a cellular plan without a physical SIM. In your app, you:
- Generate or fetch an SM‑DP+ activation payload (LPA URI).
- Display it as a QR code or in a text field.
- Invoke the system’s Add eSIM flow (Android or iOS APIs).
- User confirms, OS downloads and installs the profile.
Use cases:
- Provide connectivity fallback (5G) for offline-first messaging.
- Manage M2M/eMBB profiles remotely in enterprise apps.
Activation Payload Format
The payload is a GSMA LPA URI:
LPA:<version>$<sm_dp_address>$<iccid>[,<confirmation_code>]
version
: LPA spec version (usually1
).sm_dp_address
: Hostname of the carrier’s SM‑DP+ server.iccid
: The new profile’s identifier.confirmation_code
(optional): One-time PIN.
Example:
LPA:1$subprofiles.example.com$89011234567890123456,ABCD
JavaScript API (React Native)
ESIMHandler
// engine/ESIMHandler.js
import { NativeModules } from 'react-native';
const { ESIMModule } = NativeModules;
export default {
// Fetches LPA URI for display
async getActivationCode() {
return ESIMModule.getActivationCode();
},
// Adds subscription given the same LPA URI or user-entered code
async addSubscription(code) {
return ESIMModule.addSubscription(code);
}
};
UI Screen
// components/ManageESIMScreen.js
import ESIMHandler from '../engine/ESIMHandler';
import QRCode from 'react-native-qrcode-svg';
function ManageESIMScreen() {
const [code, setCode] = useState('');
useEffect(() => {
ESIMHandler.getActivationCode().then(setCode);
}, []);
const onAdd = () => ESIMHandler.addSubscription(inputCode);
return (
<View>
<QRCode value={code} />
<TextInput onChangeText={setInputCode} />
<Button title="Add eSIM" onPress={onAdd} />
</View>
);
}
Native Module APIs
Android (Java)
public class ESIMModule extends ReactContextBaseJavaModule {
@ReactMethod
public void getActivationCode(Promise p) {
// Fetch from backend or generate
p.resolve("LPA:1$...$...");
}
@ReactMethod
public void addSubscription(String code, Promise p) {
// Use EuiccManager.downloadSubscription(...) behind system UI
p.resolve(null);
}
}
Register Package
packages.add(new ESIMPackage());
iOS (Objective-C)
RCT_REMAP_METHOD(getActivationCode, resolver:(RCTPromiseResolveBlock)r rejecter:(RCTPromiseRejectBlock)j) {
NSString *code = @"LPA:1$...$...";
r(code);
}
RCT_REMAP_METHOD(addSubscription, code:(NSString*)c resolver:(RCTPromiseResolveBlock)r rejecter:(RCTPromiseRejectBlock)j) {
CTCellularPlanProvisioning *prov = [[CTCellularPlanProvisioning alloc] init];
CTCellularPlanProvisioningRequest *req = [[CTCellularPlanProvisioningRequest alloc] initWithActivationCode:c];
[prov addPlanWithRequest:req completionHandler:^(NSError *err) {
if(err) j(@"error", err.localizedDescription, err);
else r(nil);
}];
}
Prerequisites & Permissions
- AndroidManifest.xml:
<uses-feature android:name="android.hardware.telephony.euicc" android:required="true"/> <uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/>
- iOS: No extra entitlements; uses public CTCellularPlanProvisioning class.
- Carrier partnership: You must operate or integrate with an SM‑DP+ server.
Security & Compliance
- Always display the standard system UI; never bypass or suppress it.
- Validate codes server-side (optional) to avoid expired or fraudulent profiles.
- Adhere to GSMA RSP specifications.
This information is for educational purposes only.