Introduction
Imagine you're developing an app to manage eSIM profiles. A user wants to switch between work and personal numbers without manually digging into settings. On Android, EuiccManager.switchToSubscription() returns a bunch of error codes, and on iOS without carrier entitlement you only get a deep link into Settings. Users end up spending up to a minute switching manually, while programmatic switching on Android takes about 20 seconds on average—a 3x difference in time. Each switch saves up to 40 seconds. At 10 switches per day that's 6 minutes daily, or 2 hours per month. At an average hourly cost of $30, savings amount to $60 per month per user. For a company with 10 employees, annual savings reach $7,200. Typical development cost for a basic eSIM switching module ranges from $5,000 to $10,000 per platform, offering a rapid return on investment. We've built expertise across 20+ projects and are ready to share it.
For mobile operators and enterprise solutions, automating eSIM profile switching is a critical feature that boosts loyalty and reduces support load. This article covers the entire eSIM development process: integration, pitfalls, and how to avoid them.
Android Implementation
How to implement eSIM switching on Android?
Step 1: Set up permissions
Add WRITE_EMBEDDED_SUBSCRIPTIONS permission to your manifest.
Step 2: Get installed profiles
Use SubscriptionManager.getAvailableSubscriptionInfoList() filtered by isEmbedded. Example:
fun getInstalledEsimProfiles(): List<SubscriptionInfo> { val allSubs = subscriptionManager.availableSubscriptionInfoList ?: emptyList() return allSubs.filter { it.isEmbedded } .map { sub -> // sub.displayName — carrier name // sub.simSlotIndex: -1 if inactive, >=0 if active sub } } Step 3: Switch to a profile
Use EuiccManager.switchToSubscription(). Example:
fun switchToProfile(subscriptionId: Int) { val activeSubInfo = subscriptionManager.activeSubscriptionInfoList val currentEsimId = activeSubInfo?.firstOrNull { it.isEmbedded }?.subscriptionId if (currentEsimId == subscriptionId) { showMessage("This profile is already active") return } euiccManager.switchToSubscription( subscriptionId, PendingIntent.getBroadcast( context, REQUEST_CODE_SWITCH, Intent(ACTION_ESIM_SWITCH_COMPLETE), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) ) } // BroadcastReceiver to process the result private val switchReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val resultCode = intent.getIntExtra(EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0) if (resultCode == EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) { onSwitchSuccess() } else { onSwitchError(resultCode) } } } Step 4: Handle vendor-specifics
On Samsung, use forceDeactivateSim = true to suppress dialog:
if (Build.MANUFACTURER.equals("samsung", ignoreCase = true)) { euiccManager.switchToSubscription( subscriptionId, forceDeactivateSim = true, callbackIntent = pendingIntent ) } Important: on Qualcomm devices, after switchToSubscription the radio module reboots. We always warn the user: "Switching will cause signal loss for 15–30 seconds." In 95% of cases, switching succeeds on the first attempt, but 5% encounter a temporary error resolved by retrying. If you face integration challenges, contact us—our engineers will help.
iOS Limitations
What are the limitations of eSIM switching on iOS?
On iOS, programmatic switching is only available via Carrier entitlement. Regular apps can only open the cellular settings section. There is no direct API to select the active profile. This is a security measure—eSIM profiles are tied to the carrier. If you have the entitlement, use CTSubscriptionManager:
import CoreTelephony let subscriptionManager = CTSubscriptionManager() let subscriptions = subscriptionManager.subscriptions // No direct switch without entitlement For most apps, the only option remains a deep link to Settings. UX suffers, but it's the only path without complex Apple negotiations.
Platform Comparison
| Parameter | Android | iOS (no entitlement) |
|---|---|---|
| API | EuiccManager.switchToSubscription |
Only open App-Prefs:root=Cellular |
| Switch time | 10–30 s (avg 20 s) | Uncontrolled (user manually) |
| System dialog | Yes (can be suppressed on Samsung) | No (only Settings) |
| Get all profiles | SubscriptionManager.availableSubscriptionInfoList |
Only active via CTTelephonyNetworkInfo |
| First attempt success | ~95% | ~70% (due to human factor) |
Programmatic switching is 3 times faster than manual switching, and Android's success rate of 95% is nearly 40% higher than iOS's 70%.
Error Handling
Common eSIM errors
Common eSIM errors include code EMBEDDED_SUBSCRIPTION_RESULT_ERROR (general) and EMBEDDED_SUBSCRIPTION_RESULT_ERROR_INVALID_ACTIVATION_CODE. According to the GSMA eSIM specification, proper error handling is crucial for a smooth user experience.
Development Process
How does the eSIM development process work?
The eSIM development process involves analysis, design, implementation, testing, and deployment. We guarantee successful integration and provide 30 days of support. Our team holds certifications in mobile security and eSIM technologies.
Deliverables
- Source code of the eSIM switching module with comments.
- Documentation on API usage and error handling.
- Entitlement and signature configuration.
- Instructions for store publication.
- Support for 30 days after delivery.
Timeline and Cost
Timelines: Android (basic switching with UI) — 1–2 weeks; iOS (deep link + display) — 3–5 days. Full carrier-grade solution for both platforms — 1–2 months. Cost is calculated individually based on complexity and number of platforms. Get your project estimated in one business day—contact us.
Company Experience
We've been doing mobile development for 5+ years and have delivered 20+ projects with eSIM integration. We know all the pitfalls: from EuiccManager error codes to iOS bugs. If you want to automate eSIM switching, request a consultation—we'll estimate your project in one business day.
According to Wikipedia eSIM, the GSMA RSP specification defines the protocol for downloading and switching profiles.







