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:

  1. Generate or fetch an SM‑DP+ activation payload (LPA URI).
  2. Display it as a QR code or in a text field.
  3. Invoke the system’s Add eSIM flow (Android or iOS APIs).
  4. 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 (usually 1).
  • 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.