How to handle serviceSubscriberCellularProviders to get only data from physical SIM card instead of the eSIM?

All right, so I have an app that requires reading the data from the SIM card. More precisely I need the Carrier name, MCC, MNC, ISO Country code and current Radio Access Technology (Okay this does not directly come from the SIM card but it is linked). It works perfectly on every phone, but I realized (after testing) that I'm in trouble with the newest iPhones that have an eSIM set up. I have no idea how to tell my app to read the data of the physical SIM card instead of the eSIM.

I tried to search a bit on the Web about that but I found absolutely nothing...

The code I use:


let telephonyInfo: CTTelephonyNetworkInfo = CTTelephonyNetworkInfo()

carrierNetwork = telephonyInfo.serviceCurrentRadioAccessTechnology?.first?.value ?? "null"
carrierNetwork = carrierNetwork.replacingOccurrences(of: "CTRadioAccessTechnology", with: "", options: NSString.CompareOptions.literal, range: nil)

let carrier = telephonyInfo.serviceSubscriberCellularProviders?.first?.value // actually here originally I tried to use ?.values.first, but the result is the same

let countryCode = carrier?.mobileCountryCode ?? "null"
let mobileNetworkName = carrier?.mobileNetworkCode ?? "null"
let carrierName = carrier?.carrierName ?? "null"
let isoCountrycode = carrier?.isoCountryCode?.uppercased() ?? "null"

/* so the problem is that the values I get are the ones from the eSIM when it is set up. If it is not, then I get the values I want. Apparently, when the physical SIM card line is set as primary, it works. But I need that code to systematically use the physical SIM data. */


Example:

I expect [physical SIM card]: Carrier name: Free, MCC: 208, MNC: 15, ISO Country Code: fr, CTRadioAccessTechnology: WCDMA.

But instead, I get [eSIM]: Carrier name: Swisscom, MCC: 228, MNC: 01, ISO Country Code: ch, CTRadioAccessTechnology: LTE.


EDIT: If you know how I can read both, that's also fine to me, my goal is to detect if the user uses a Free Mobile [208 15] SIM card.


Thank you in advance,

Answered by DTS Engineer in 349565022

Your code is always getting the first entry from

serviceSubscriberCellularProviders
and
serviceCurrentRadioAccessTechnology
dictionaries, but dictionaries don’t have a defined order. Thus, you’ve no idea which info you’re getting; there’s not even any guarantee that you’ll get the same info between different executions of your app.

If you know how I can read both, that's also fine

For that you can enumerate the

serviceSubscriberCellularProviders
dictionary and then the ‘key’ value to access the
serviceCurrentRadioAccessTechnology
dictionary. For example:
let info = CTTelephonyNetworkInfo()
for (service, carrier) in info.serviceSubscriberCellularProviders ?? [:] {
    let radio = info.serviceCurrentRadioAccessTechnology?[service] ?? ""
    … `radio` is now a value that matches `carrier` …
}

There’s still no guarantee about the order though. However, it seems like you plan to search through this list for the value you want, and that’s quite feasible.

Also, with regards this:

carrierNetwork.replacingOccurrences(of: "CTRadioAccessTechnology", with: "", options: NSString.CompareOptions.literal, range: nil)

I’m not sure what you’re trying to do here but this code is not valid. The values returned by

currentRadioAccessTechnology
and
serviceCurrentRadioAccessTechnology
are only intended to be compared to the various
CTRadioAccessTechnologyXXX
constants; you can’t parse the string and expect to get back meaningful results.

If you want to show the radio technology to the user, you’ll need your own mapping from these constants to an appropriate display name.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
Accepted Answer

Your code is always getting the first entry from

serviceSubscriberCellularProviders
and
serviceCurrentRadioAccessTechnology
dictionaries, but dictionaries don’t have a defined order. Thus, you’ve no idea which info you’re getting; there’s not even any guarantee that you’ll get the same info between different executions of your app.

If you know how I can read both, that's also fine

For that you can enumerate the

serviceSubscriberCellularProviders
dictionary and then the ‘key’ value to access the
serviceCurrentRadioAccessTechnology
dictionary. For example:
let info = CTTelephonyNetworkInfo()
for (service, carrier) in info.serviceSubscriberCellularProviders ?? [:] {
    let radio = info.serviceCurrentRadioAccessTechnology?[service] ?? ""
    … `radio` is now a value that matches `carrier` …
}

There’s still no guarantee about the order though. However, it seems like you plan to search through this list for the value you want, and that’s quite feasible.

Also, with regards this:

carrierNetwork.replacingOccurrences(of: "CTRadioAccessTechnology", with: "", options: NSString.CompareOptions.literal, range: nil)

I’m not sure what you’re trying to do here but this code is not valid. The values returned by

currentRadioAccessTechnology
and
serviceCurrentRadioAccessTechnology
are only intended to be compared to the various
CTRadioAccessTechnologyXXX
constants; you can’t parse the string and expect to get back meaningful results.

If you want to show the radio technology to the user, you’ll need your own mapping from these constants to an appropriate display name.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thank you.

I will try this. However I need both serviceSubscriberCellularProviders and serviceCurrentRadioAccessTechnology to have the same order at the same time, to know which signal comes from which card. Will it be the case?

Because once I found it, I need to get the serviceCurrentRadioAccessTechnology from that particular physical SIM card.


Thank you,


P.S.: I use the replacingOccurences only on the UI once to make it easier for me to set custom display names below (so I don't have to type that "CTRadioAccessTechnology" every time). That's just a lazy move to code faster 😁

Will it be the case?

No, because they’re both dictionaries and dictionaries don’t define an iteration order.

However the dictionary keys are the same, so you can iterate one dictionary to get both its keys and values (line 2 of my code), and thus use the keys to look up in the other dictionary (line 3).

I use the replacingOccurences only on the UI

That’s fine during development but in production you should map these to your own strings and then run those strings through localisation lookup. These constants are intended to be used symbolically, meaning that the actual string values could change from release to release of the OS.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Will serviceSubscriberCellularProviders data change if the device uses roaming?

No. This is explicitly covered by the documentation:

A CTCarrier object that contains information about the user’s home cellular service provider.

And to head off the inevitable follow-up question…

No, there not supported API to determine the carrier that the device is currently using.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

How to handle serviceSubscriberCellularProviders to get only data from physical SIM card instead of the eSIM?
 
 
Q