Retrieving Carrier Name from iPhone programmatically - ios

Is there a way to know the cell carrier on an iPhone programmatically?
I am looking for the carrier name which the iPhone is connected to.

In iOS 4, the CoreTelephony framework is useable, here's a snippet to get the carrier name:
CTTelephonyNetworkInfo *netinfo = [[CTTelephonyNetworkInfo alloc] init];
CTCarrier *carrier = [netinfo subscriberCellularProvider];
NSLog(#"Carrier Name: %#", [carrier carrierName]);
[netinfo release];
Link against CoreTelephony and include in your headers:
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <CoreTelephony/CTCarrier.h>

Just to make a note here.. I tested this API on different SIMs and it seems that the name of the operator the iPhone is locked to is returned with [carrer carrierName]!!
I tested this on 2 iphones, one locked and the other not, and for the locked one, regardless of the SIM provider, it returns the name of the operator it is locked to everytime i run my test app. Note however that the MNC does change!

For swift users you can try this:
import CoreTelephony
static var carrierName:String? {
let networkInfo = CTTelephonyNetworkInfo()
let carrier = networkInfo.subscriberCellularProvider
return carrier?.carrierName
}

There is no public API for getting the carrier name. If you don't need to publish on the App Store you could look at using private api's.
VVCarrierParameters.h in the VisualVoiceMail package seems to have a carrierServiceName class method that might be what you need. Drop that header in your project and call [VVCarrierParameters carrierServiceName].
Note your app will most likely be rejected if you do this.

While developing Alpha, I encountered the same problem. The project itself was not limited to use only public API, so first I tried #Jason Harwig's solution. Because I could not get it to work, I thought of another option.
My solution uses private API to access the _serviceString ivar of the label (UIStatusBarServiceItemView) that is displayed in status bar.
It relies on status bar having a carrier value and only needs UIKit to work.
- (NSString *)carrierName
{
UIView* statusBar = [self statusBar];
UIView* statusBarForegroundView = nil;
for (UIView* view in statusBar.subviews)
{
if ([view isKindOfClass:NSClassFromString(#"UIStatusBarForegroundView")])
{
statusBarForegroundView = view;
break;
}
}
UIView* statusBarServiceItem = nil;
for (UIView* view in statusBarForegroundView.subviews)
{
if ([view isKindOfClass:NSClassFromString(#"UIStatusBarServiceItemView")])
{
statusBarServiceItem = view;
break;
}
}
if (statusBarServiceItem)
{
id value = [statusBarServiceItem valueForKey:#"_serviceString"];
if ([value isKindOfClass:[NSString class]])
{
return (NSString *)value;
}
}
return #"Unavailable";
}
- (UIView *)statusBar
{
NSString *statusBarString = [NSString stringWithFormat:#"%#ar", #"_statusB"];
return [[UIApplication sharedApplication] valueForKey:statusBarString];
}
I only tested the method with applications that have status bar visible. It returns the same string as it is displayed in status bar, so it works correctly even when roaming.
This method is not App Store safe.

Get carrier name from status bar in case if Core Telephony returns "Carrier"
func getCarrierName() -> String? {
var carrierName: String?
let typeName: (Any) -> String = { String(describing: type(of: $0)) }
let statusBar = UIApplication.shared.value(forKey: "_statusBar") as! UIView
for statusBarForegroundView in statusBar.subviews {
if typeName(statusBarForegroundView) == "UIStatusBarForegroundView" {
for statusBarItem in statusBarForegroundView.subviews {
if typeName(statusBarItem) == "UIStatusBarServiceItemView" {
carrierName = (statusBarItem.value(forKey: "_serviceString") as! String)
}
}
}
}
return carrierName
}

for Swift and ios 12.0 < do the following:
import CoreTelephony
static var carrierName:String? {
CTTelephonyNetworkInfo().serviceSubscriberCellularProviders?.first?.value.carrierName ?? ""
}

CTCarrier, carrierName and other info is deprecated as of iOS 16 with no replacement: https://developer.apple.com/documentation/coretelephony/ctcarrier.

https://developer.apple.com/iphone/prerelease/library/documentation/NetworkingInternet/Reference/CTCarrier/Reference/Reference.html#//apple_ref/doc/uid/TP40009596-CH1-DontLinkElementID_3
There is a such way however it's only available on iOS 4 so you won't be able to use it on previous versions. And this probably breaks your backward compatibility too.

When you print output of carrier?.description
This is what you see:
[\"0000000100000001\": CTCarrier (0x2803a1980) {\n\tCarrier name: [Vodafone]\n\tMobile Country Code: [214]\n\tMobile Network Code:[01]\n\tISO Country Code:[es]\n\tAllows VOIP? [YES]\n}\n]
Formatted (\n and \t):
[\"0000000100000001\": CTCarrier (0x2803a1980) {
Carrier name: [Vodafone]
Mobile Country Code: [214]
Mobile Network Code:[01]
ISO Country Code:[es]
Allows VOIP? [YES]
}
]
So get carrier name from status bar is a good option (at least for me)
I mean the answer of "codethemall" user.

More of an important comment. Just to add docs about the carrierName:
The carrier provides this string, formatting it for presentation to
the user. The value does not change if the user is roaming; it always
represents the provider with which the user has an account.
If you configure a device for a carrier and then remove the SIM card,
this property retains the name of the carrier. If you then install a
new SIM card, its carrier name replaces the previous value of this
property.
The value for this property is nil if the user never configured a
carrier for the device.

Related

Is there a way to tell if a MIDI-Device is connected via USB on iOS?

I'm using CoreMIDI to receive messages from a MIDI-Keyboard via Camera Connection Kit on iOS-Devices. My App is about pitch recognition. I want the following functionality to be automatic:
By default use the microphone (already implemented), if a MIDI-Keyboard is connected use that instead.
It's could find out how to tell if it is a USB-Keyboard using the default driver. Just ask for the device called "USB-MIDI":
private func getUSBDeviceReference() -> MIDIDeviceRef? {
for index in 0..<MIDIGetNumberOfDevices() {
let device = MIDIGetDevice(index)
var name : Unmanaged<CFString>?
MIDIObjectGetStringProperty(device, kMIDIPropertyName, &name)
if name!.takeRetainedValue() as String == "USB-MIDI" {
return device
}
}
return nil
}
But unfortunately there are USB-Keyboards that use a custom driver. How can I tell if I'm looking at one of these? Standard Bluetooth- and Network-Devices seem to be always online. Even if Wifi and Bluetooth are turned of on the device (strange?).
I ended up using the USBLocationID. It worked with any device I tested so far and none of the users complained.But I don't expect many users to use the MIDI-Features of my app.
/// Filters all `MIDIDeviceRef`'s for USB-Devices
private func getUSBDeviceReferences() -> [MIDIDeviceRef] {
var devices = [MIDIDeviceRef]()
for index in 0..<MIDIGetNumberOfDevices() {
let device = MIDIGetDevice(index)
var list: Unmanaged<CFPropertyList>?
MIDIObjectGetProperties(device, &list, true)
if let list = list {
let dict = list.takeRetainedValue() as! NSDictionary
if dict["USBLocationID"] != nil {
devices.append(device)
}
}
}
return devices
}

How to detect that haptic feedback is disabled on iOs device?

I want to to show message in my application when haptic feedback is disabled in phone settings. How to detect that haptic feedback is disabled in device settings?
It's kludgy, but might this work?
- (BOOL)isHapticFeedbackDisabled {
BOOL result = NO;
UISelectionFeedbackGenerator *feedbackGenerator = [[UISelectionFeedbackGenerator alloc] init];
[feedbackGenerator prepare];
if ([feedbackGenerator.description containsString:#"prepared=0"]) result = YES;
feedbackGenerator = nil;
return result;
}
There is no way to check Haptic Feedback is enabled/disabled but there is private int _feedbackSupportLevel in UIKit for checking if device supports it:
func logFeedbackSupported() {
let supportLevel = UIDevice.current.value(forKey: "_feedbackSupportLevel")
print(supportLevel ?? "")
}
0: Not available,
1: First generation available (< iPhone 7),
2: Second generation available.
I advise you not to use Apples private APIs because:
The API could be changed in any version without you knowing about it.
Apple is parsing your app code to find out if you're using private API so be aware. Your app could be rejected.

Knowing programmatically if cell data is disabled for the app for iOS [duplicate]

I have an iOS app that makes some small network requests on app launch (resource updates, etc). If the user turns off cellular access for the app in iOS Settings, they get a prompt from iOS about network usage every time they launch. Is there a way to know programmatically that cellular data for this app has been disabled, so that I can disable the requests at startup?
So I found this on the apple dev forums from an Apple engineer (https://devforums.apple.com/message/1059332#1059332).
Another developer wrote in to DTS and thus I had a chance to
investigate this in depth. Alas, the news is much as I expected:
there is no supported way to detect that your app is in this state.
Nor is there a way to make a "no user interaction" network connection,
that is, request that the connection fail rather than present UI like
this. If these limitations are causing problems for your app, I
encourage you to file a bug describing your specific requirements.
https://developer.apple.com/bug-reporting/
So it looks like it is not possible to detect if cellular data for your app has been turned off.
Edit
I filed a radar for this requesting that it be added. I just got this notification in my radar
We believe this issue has been addressed in the latest iOS 9 beta.
I looked through the API diffs, but so far I can't find the new API.
As of iOS9, the capability to check the setting to enable/disable use of cellular data for your app (Settings/Cellular/AppName) is available using Apple's CTCellularData class. The following code will set cellularDataRestrictedState when it is run initially and then set it and log whenever it changes:
import CoreTelephony
var cellularDataRestrictedState = CTCellularDataRestrictedState.restrictedStateUnknown
let cellState = CTCellularData.init()
cellState.cellularDataRestrictionDidUpdateNotifier = { (dataRestrictedState) in
if cellularDataRestrictedState != .restrictedStateUnknown { // State has changed - log to console
print("cellularDataRestrictedState: " + "\(dataRestrictedState == .restrictedStateUnknown ? "unknown" : dataRestrictedState == .restricted ? "restricted" : "not restricted")")
}
cellularDataRestrictedState = dataRestrictedState
}
Unfortunately (as of iOS11) this seems to check only the state of the app's switch - if your app's switch is set to enabled and the user switches the Cellular Data master switch to disabled, this API will return the app's state as being "not restricted".
Just wanted to add an Objective C version of the above Swift code for future travellers.
- (void)monitorCanUseCellularData {
if (GCIsiOS9) {
CTCellularData *cellularData = [[CTCellularData alloc] init];
NSLog(#"%ld", cellularData.restrictedState);
// 0, kCTCellularDataRestrictedStateUnknown
[cellularData setCellularDataRestrictionDidUpdateNotifier:^(CTCellularDataRestrictedState state) {
NSLog(#"%ld", state);
self.canUseCellularData = cellularData.restrictedState ==2?true:false;
}];
}
}
I have found that the CTCellularData class needs some time to get to the correct value. In my implementation I call the didUpdateNotifier very early after appDidFinishLaunching. By the time my networking call are returning with errors I definitely have a correct value for the restricted state.
class CellularRestriction: NSObject {
private static var cellularData = CTCellularData()
private static var currentState = CTCellularDataRestrictedState.restrictedStateUnknown
static var isRestricted: Bool {
currentState = cellularData.restrictedState
return currentState == .restricted
}
static func prepare() {
if currentState == .restrictedStateUnknown {
cellularData.cellularDataRestrictionDidUpdateNotifier = { state in
currentState = cellularData.restrictedState // This value may be inconsistent, however the next read of isRestricted should be correct.
}
}
}
}
You can detect if cellular data disabled using NWPathMonitor class. (https://developer.apple.com/documentation/network/nwpathmonitor)
let cellMonitor = NWPathMonitor(requiredInterfaceType: .cellular)
cellMonitor.pathUpdateHandler = { path in
self.isCellConnected = path.status == .satisfied
}
Adding to dirkgroten's answer, you can use the Apple Reachability class, found here:
https://developer.apple.com/Library/ios/samplecode/Reachability/Introduction/Intro.html
It uses SCNetworkReachability, and is very straight forward to use, it will detect connectivity via Cell and WiFi as you will need to check both at start up.
There are lots of frameworks out there that will give you the status of your network connectivity, and of course you can roll your own. I've found AFNetworking to be one of the best. It has a singleton class called AFNetworkReachabilityManager that abstracts some of the complexities for you. Specifically you'll want to look at the two boolean properties:
reachableViaWWAN
reachableViaWiFi
There is also a reachability changed status block that you can set:
– setReachabilityStatusChangeBlock:
AFNetworking Github
AFNetworkReachabilityManager

How to detect whether custom keyboard is activated from the keyboard's container app?

I was wondering if there is a method that would allow me to detect from the keyboard container app whether the associated keyboard has been activated in the the device's Settings app.
For example, I am interested in adding a simple "steps" feature inside the container app where step 1 would be "activate the keyboard", and step 2 would be contingent on step 1's completion. As such, I am interested in figuring out whether there is a way to detect whether the keyboard extension is activated?
Thanks!
Here is a method I have used in one of my projects. I think it is what you asked for, hope it helps you.
- (BOOL)isCustomKeyboardEnabled {
NSString *bundleID = #"com.company.app.customkeyboard"; // Replace this string with your custom keyboard's bundle ID
NSArray *keyboards = [[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] objectForKey:#"AppleKeyboards"]; // Array of all active keyboards
for (NSString *keyboard in keyboards) {
if ([keyboard isEqualToString:bundleID])
return YES;
}
return NO;
}
Just in case here is Swift version of Kurt's brilliant and awesome answer:
func isKeyboardExtensionEnabled() -> Bool {
guard let appBundleIdentifier = Bundle.main.bundleIdentifier else {
fatalError("isKeyboardExtensionEnabled(): Cannot retrieve bundle identifier.")
}
guard let keyboards = UserDefaults.standard.dictionaryRepresentation()["AppleKeyboards"] as? [String] else {
// There is no key `AppleKeyboards` in NSUserDefaults. That happens sometimes.
return false
}
let keyboardExtensionBundleIdentifierPrefix = appBundleIdentifier + "."
for keyboard in keyboards {
if keyboard.hasPrefix(keyboardExtensionBundleIdentifierPrefix) {
return true
}
}
return false
}
The current documentation states By default, your extension and its containing app have no direct access to each other’s containers.
It is also stating that the container app can share data with the keyboard in the following fashion:
// Create and share access to an NSUserDefaults object.
NSUserDefaults *mySharedDefaults = [[NSUserDefaults alloc]
initWithSuiteName:#"com.example.domain.MyShareExtension"];
// Use the shared user defaults object to update the user's account.
[mySharedDefaults setObject:theAccountName forKey:#"lastAccountName"];
Read more on this: Communicating and persisting data between apps with App Groups
Obstacle no 1: According to the documentation, for this to work, the RequestsOpenAccess in the plist needs to be set to YES as it would gain the following capability:
Option to use a shared container with the keyboard’s containing app,
which enables features such as providing a custom lexicon management
UI in the containing app
Requesting full access for a simple case like this is definitely not preferred on my side.
Obstacle no 2: Using this knowledge of setting a NSUserDefault, leaves me to think of a method where this can be set in place. But there's no public method indicating an extension is installed. So this is a dead end for now.
--
[Update 1]
Not super relevant but still worth stating: the shouldAllowExtensionPointIdentifier app delegate method in combination with the constant UIApplicationKeyboardExtensionPointIdentifier can deal with disallowing custom keyboards. The extension point identifiers are not unique identifiers of the extension but of their type.
Read more on this: Can I disable custom keyboards (iOS8) for my app?
--
[Update 2]
Another question with same issue, but w/o solution: How to detect an app extension is enabled in containing app on iOS 8?
--
This is a work-in-progress answer stating my findings so far which I hope to be updating coming days should I find a solution.
You can use this function (Swift 3 and 4) to check your custom keyboard extension have open access or not:
func isOpenAccessGranted() -> Bool{
if #available(iOS 10.0, *) {
let originalString = UIPasteboard.general.string
UIPasteboard.general.string = "Sour LeangChhean"
if UIPasteboard.general.hasStrings {
UIPasteboard.general.string = originalString ?? ""
return true
}else{
UIPasteboard.general.string = ""
return false
}
} else {
// Fallback on earlier versions
if UIPasteboard.general.isKind(of: UIPasteboard.self) {
return true
}else{
return false
}
}
}

iOS: Determine if device language is Right to Left (RTL)

Is there a way to easily determine if the language the device is set to is right to left (RTL)?
In iOS 9 one can determine the current direction for each individual view.
if #available(iOS 9.0, *) {
if UIView.userInterfaceLayoutDirection(
for: myView.semanticContentAttribute) == .rightToLeft {
// The view is shown in right-to-left mode right now.
}
} else {
// Use the previous technique
if UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft {
// The app is in right-to-left mode
}
}
This is the recommended way of determining the layout direction in iOS 9.
WWDC 2015 video New UIKit Support for International User Interfaces. After minute 31:20.
There is an official way to do it:
if ([UIApplication sharedApplication].userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) {
}
I would recommend against using some of the other solutions, because they will not always return the correct locale. Just because it's on the top of preferred languages doesn't mean that the application supports it.
Source: https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPInternational/SupportingRight-To-LeftLanguages/SupportingRight-To-LeftLanguages.html
NSLocale has two methods +characterDirectionForLanguage: and +lineDirectionForLanguage:. The first is presumably Left-to-Right vs Right-to-Left and the second is Top-to-Bottom vs Bottom-to-Top. You can pass it the result of [[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode].
Update:
The original question asked was how to determine whether the device language is RTL. Using +[NSLocale characterDirectionForLanguage:] and +[NSLocale lineDirectionForLanguage:] is unambiguously correct for that; you can pass either [[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode] or [NSLocale preferredLanguages][0] to that to get the relevant info (I'm not sure offhand whether the NSLocaleLanguageCode uses the preferred language, or the set region).
However, it's very likely that what the original poster actually wanted to know is whether the application's interface should be laid out in RTL or LTR. This is very similar to asking what the direction of the language is, except it takes the application's available localizations into account. If the application is not localized into the user's preferred language, it will use a non-preferred language instead. And the answer to this question is to use [UIApplication sharedApplication].userInterfaceLayoutDirection.
Make sure you return the currently selected language, not the current region of the device. The region and language are often the same. However, if I am in North America and I set my language to Japanese, my region will still be English (United States). In order to check the character direction of the currently selected language, you can do:
+ (BOOL)isDeviceLanguageRTL {
return ([NSLocale characterDirectionForLanguage:[[NSLocale preferredLanguages] objectAtIndex:0]] == NSLocaleLanguageDirectionRightToLeft);
}
You may likely want to cache the result, using dispatch_once.
Keep in mind that this is the user's preferred language direction, and not necessarily the language direction of the text. For that, use a C function that is based on u_charDirection.
Here is a swift 3 version:
import UIKit
extension UIView
{
/// Returns text and UI direction based on current view settings
var userInterfaceLayoutDirection: UIUserInterfaceLayoutDirection
{
if #available(iOS 9.0, *) {
return UIView.userInterfaceLayoutDirection(for: self.semanticContentAttribute)
} else {
return UIApplication.shared.userInterfaceLayoutDirection
}
}
}
If you just want to know a specific views layout direction on iOS 10+ you can use:
view.effectiveUserInterfaceLayoutDirection == .rightToLeft
Thanks to Kevin Ballard's answer I was able to create the following utility function to do this:
+ (BOOL)isDeviceLanguageRTL {
return [NSLocale characterDirectionForLanguage:[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]]==NSLocaleLanguageDirectionRightToLeft;
}
Here is how i Used it :
+(NSTextAlignment) alignmentOfLanguage {
if ([NSLocale characterDirectionForLanguage:[[NSLocale preferredLanguages] objectAtIndex:0]]==NSLocaleLanguageDirectionRightToLeft){
return NSTextAlignmentRight;
}
return NSTextAlignmentLeft;
}
The Last example didn't work for me , but with a little variant , i got it rightX2 .
any comments?
if you want to check if the device is running in RTL or LTR in swift 3
if(UIApplication.shared.userInterfaceLayoutDirection == UIUserInterfaceLayoutDirection.rightToLeft) {
//RTL
} else {
//LTR
}
Ok, although it's an old question with an accepted answer, I will answer it anyway.
For those who wants to check whether the device language is RTL, independent if your application supports or not this language, you should use [NSLocale characterDirectionForLanguage:] like this:
+ (BOOL)isDeviceLanguageRightToLeft {
NSLocale *currentLocale = [NSLocale currentLocale];
NSLocaleLanguageDirection direction = [NSLocale characterDirectionForLanguage:[currentLocale objectForKey:NSLocaleLanguageCode]];
return (direction == NSLocaleLanguageDirectionRightToLeft);
}
The code above will return YES if your app only supports english, but your device is set to Arabic for example.
Apple recommends that you use [UIApplication sharedApplication].userInterfaceLayoutDirection, just because it returns the direction based on the language that your app is using (has support to). Here is the code snippet:
+ (BOOL)isAppLanguageRightToLeft {
NSLocaleLanguageDirection direction = [UIApplication sharedApplication].userInterfaceLayoutDirection;
return (direction == UIUserInterfaceLayoutDirectionRightToLeft);
}
The code above will return NO when your app only supports english, but your device is set to Arabic for example.
if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) {
if ([UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.view.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft) {
NSLog(#"Right to left");
}
else{
NSLog(#"left to Right");
}
} else {
/* Use the previous technique */
//Work for earlier ios 6 to ios 10
if ([NSLocale characterDirectionForLanguage:[[NSLocale preferredLanguages] objectAtIndex:0]] == NSLocaleLanguageDirectionRightToLeft) {
NSLog(#"Right to left");
}
else{
NSLog(#"left to Right");
}
}
must watch Advanced Topics in Internationalization wwdc2014
For iOS 9 and above
extension UIView {
var isLayoutDirectionRightToLeft: Bool {
UIView.userInterfaceLayoutDirection(for: semanticContentAttribute) == .rightToLeft
}
}
Rose Perrone is completely correct. However the use of dispatch_once in a getter for a simple boolean value - is really too much overhead. Unnecessary use of dispatch once.
Because you will probably want to use that many times inside a layout or drawing function.
So you have two faster options:
+ (BOOL)isRtl
{
static BOOL isRtl = NO;
static BOOL isRtlFound = NO;
if (!isRtlFound)
{ // This is "safe enough". Worst case is this code will be called twice in the app's lifecycle...
isRtl = [NSLocale characterDirectionForLanguage:[NSBundle mainBundle].preferredLocalizations[0]] == NSLocaleLanguageDirectionRightToLeft;
isRtlFound = YES;
}
return isRtl;
}
Or just cache it in a static variable, using the static constructor:
static BOOL s_isRtl = NO;
+ initialize
{
s_isRtl = [NSLocale characterDirectionForLanguage:[NSBundle mainBundle].preferredLocalizations[0]] == NSLocaleLanguageDirectionRightToLeft;
}
Note that this will actually share the static variable between any class that uses this code.
you can check RTL like this
- (BOOL)isDeviceLanguageRTL {
return ([NSLocale characterDirectionForLanguage:[[NSLocale preferredLanguages] objectAtIndex:0]] == NSLocaleLanguageDirectionRightToLeft);
}
if ([self isDeviceLanguageRTL]) {
//RTL
}
else
{
//LRT
}
On macOS, NSView has a userInterfaceLayoutDirection property you can use to determine the language direction. Credits to this answer for the iOS version.
let view = NSView()
if view.userInterfaceLayoutDirection == .rightToLeft {
print("RTL")
}

Resources