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

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")
}

Related

Different values of BOOL variable in debug and release version

I have one method in which there is one condition which checks the bool value and do some task based on the condition. It is working perfectly in the debug build but in the release build the bool value always return true.
Below is sample code of the method which is behaving differently in debug and release version.
-(void)addNotification:(NSMutableDictionary *)dictNotificationData
{
BOOL addNotification;
if ([[dictNotificationData objectForKey:#"id"] integerValue] == 2) {
if ([[dictNotificationData objectForKey:#"isActive"] boolValue]) {
addNotification = YES;
}
}
else {
addNotification = NO;
}
//In the release version this value always return true eventhough it is going in the else part.
if (addNotification) { 
//code for local notification
}
}
Please let me know if any one has any idea about why it is behaving differently in debug and release version.
Actually I found the solution for that. The Bool local variable always initialised as garbage value if we do not provide any which was creating the problem in my case. When I initialised BOOL addNotification = NO; it works fine.
Found the answer here.
Default value of BOOL
Thanks All.
Not the question that you are asking but in your code is it possible that addNotification is tested before it is initialised - if the second 'if' is false.
For boolValue check please try this
NSNumber * isSuccessNumber = (NSNumber *)[response objectForKey: #"success"];
if([isSuccessNumber boolValue] == YES)
{
} else
{
}
Boolen value can be changed on the device version. So what the result for this line, if this line return 0 or 1, can be change the result. [dictNotificationData objectForKey:#"isActive"]
For instance, run your code on both 32-bit iOS and 64-bit iOS. It should correctly display “Different” on one, but not the other.
if (different(1, 2)) {
NSLog(#"Different!");
} else {
NSLog(#"Not different.");
}

How Can I set Localised Direction within application?(RTL if user select arabic, LTR is selected language is English)

My Application must support Arabic(right to left direction) and English(left to right direction) language, I need to set UI and based on user select language from my application.
I will implement UI with NSLayoutConstraint so it can update UI automatically, based on leading and trailing constraints.
Now My Question is how can i achieve this? As My device language is say English and from my application user select Arabic(limited to my app only), So my Flow,UI,Animations etc should be right to left.
here are the sample snap for ui Direction
Thanks
Bottom line you can change the language from inside the app. But, an
extra work is needed. I am listing a background on limitations for each iOS version and then providing the solution. because while explaining the solution you need to understand the reason behind it.
Update (iOS 9+)
Solution Key: semanticContentAttribute
Some says that this way is not official and may cause efficiency problems.
UI direction can be forced now without closing the app:
UIView.appearance().semanticContentAttribute = .forceRightToLeft
Note, bars in iOS 9 can't be forced to RTL (Navigation and TabBar). While it get forced with iOS 10.
The only remaining thing that can't be forced RTL in iOS 10 is the default back button.
Changing app the language along with forcing the layout doesn't
automatically forces the system to use the localized string file
associated with the language.
Below iOS 9
Short answer:
Changing the app language will not force the layout direction according to the selected language without an app restart.
Apple's guide line:
Apple doesn't provide that because they don't prefere to allow the user
to change language from inside the app. Apple pushes the developers to
use the device language. and not to change it from inside the app at all.
Workarwound:
Some apps that supports Arabic language notes the user, in the
settings page where the user can change the language, that the layout will not take effect without an app restart. store
link for sample app
Example code to change app language to arabic:
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:#"ar", #"en", nil] forKey:#"AppleLanguages"];
That won't take effect without a restart
How to change app language without an app restart
Solution is to provide your own localization solution:
Use Base Internationalization but don't localize storyboards.
Create one strings file and localise it to all languages and base for (english).
Place the change language feature in a ViewController that starts before any other ViewController even before your main one. OR use an unwindSegue to go back to the root view controller after changing the language.
Set strings for your views in viewDidLoad method for all view controllers.
Don't satisfy points 5 and 6 if you are not supporting below iOS 9
Set the constraints for your views in viewDidLoad method for all view controllers.
When you set the constraints use right/left according to the language selected instead of leading/trailing.
Language class sample (Swift):
Initialise an object of this class in your singlton class.
class LanguageDetails: NSObject {
var language: String!
var bundle: Bundle!
let LANGUAGE_KEY: String = "LANGUAGE_KEY"
override init() {
super.init()
// user preferred languages. this is the default app language(device language - first launch app language)!
language = Bundle.main.preferredLocalizations[0]
// the language stored in UserDefaults have the priority over the device language.
language = Common.valueForKey(key: LANGUAGE_KEY, default_value: language as AnyObject) as! String
// init the bundle object that contains the localization files based on language
bundle = Bundle(path: Bundle.main.path(forResource: language == "ar" ? language : "Base", ofType: "lproj")!)
// bars direction
if isArabic() {
UIView.appearance().semanticContentAttribute = .forceRightToLeft
} else {
UIView.appearance().semanticContentAttribute = .forceLeftToRight
}
}
// check if current language is arabic
func isArabic () -> Bool {
return language == "ar"
}
// returns app language direction.
func rtl () -> Bool {
return Locale.characterDirection(forLanguage: language) == Locale.LanguageDirection.rightToLeft
}
// switches language. if its ar change it to en and vise-versa
func changeLanguage()
{
var changeTo: String
// check current language to switch to the other.
if language == "ar" {
changeTo = "en"
} else {
changeTo = "ar"
}
// change language
changeLanguageTo(lang: changeTo)
Log.info("Language changed to: \(language)")
}
// change language to a specfic one.
func changeLanguageTo(lang: String) {
language = lang
// set language to user defaults
Common.setValue(value: language as AnyObject, key: LANGUAGE_KEY)
// set prefered languages for the app.
UserDefaults.standard.set([lang], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()
// re-set the bundle object based on the new langauge
bundle = Bundle(path: Bundle.main.path(forResource: language == "ar" ? language : "Base", ofType: "lproj")!)
// app direction
if isArabic() {
UIView.appearance().semanticContentAttribute = .forceRightToLeft
} else {
UIView.appearance().semanticContentAttribute = .forceLeftToRight
}
Log.info("Language changed to: \(language)")
}
// get local string
func getLocale() -> NSLocale {
if rtl() {
return NSLocale(localeIdentifier: "ar_JO")
} else {
return NSLocale(localeIdentifier: "en_US")
}
}
// get localized string based on app langauge.
func LocalString(key: String) -> String {
let localizedString: String? = NSLocalizedString(key, bundle: bundle, value: key, comment: "")
// Log.ver("Localized string '\(localizedString ?? "not found")' for key '\(key)'")
return localizedString ?? key
}
// get localized string for specific language
func LocalString(key: String, lan: String) -> String {
let bundl:Bundle! = Bundle(path: Bundle.main.path(forResource: lan == "ar" ? lan : "Base", ofType: "lproj")!)
return NSLocalizedString(key, bundle: bundl, value: key, comment: "")
}
}
Language class sample (Objective-c):
Swift sample provides full solution. sorry you need to convert it yourself.
#implementation LanguageDetails
#synthesize language;
#synthesize bundle;
#define LANGUAGE_KEY #"LANGUAGE_KEY"
// language var is also the strings file name ar or en
- (id)init
{
if (self = [super init]) {
language = [[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0];
if (![language isEqualToString:#"ar"])
language = #"en";
language = [Common valueForKey:LANGUAGE_KEY defaultValue:language];
bundle = [NSBundle mainBundle];
}
return self;
}
- (BOOL)rtl {
return [NSLocale characterDirectionForLanguage:language] == NSLocaleLanguageDirectionRightToLeft;
}
- (void)changeLanguage
{
if ([language isEqualToString:#"ar"])
language = #"en";
else
language = #"ar";
[Common setValue:language forKey:LANGUAGE_KEY];
}
- (void)changeLanguageTo:(NSString *)lang
{
language = lang;
[Common setValue:language forKey:LANGUAGE_KEY];
}
- (NSString *) LocalString:(NSString *)key {
return NSLocalizedStringFromTableInBundle(key, language, bundle, nil);
}
#end
use Directional Layout Margins (iOS 11.0+)
and set Views to
Right To Left
UIView.appearance().semanticContentAttribute = .forceRightToLeft
Left To Right
UIView.appearance().semanticContentAttribute = .forceLeftToRight
Use this line of code it will do your magic and will change layout without closing application.
From right to left
UIView.appearance().semanticContentAttribute = .forceRightToLeft
And for Right to Left Flip
UIView.appearance().semanticContentAttribute = .forceLeftToRight
and if you want to change textfield layout or text change then use this code because i faced this issue . textfield's texts was not changning layout.
check this code to change layout of textfield text.
extension UITextField {
open override func awakeFromNib() {
super.awakeFromNib()
if UserDefaults.languageCode == "ar" {
if textAlignment == .natural {
self.textAlignment = .right
}
}
}
}
#ashwin, I was trying to do the exact same thing as you were.
I was hoping to find I could turn my app into a RTL language (right to left) just by changing something in the info.plist, appdelegate or who knows what. The idea is that someone with a LTR device can have my app in RTL, changing it within the app and with no need to restart it.
Looks like there is not such a thing (I'll be glad to hear if anyone disagrees with this). However, I've found some options that are not that bad.
It turns out you can force a specific view to be LTR or RTL. Thus you can set this property on every view of your app. The way you do it is up to you.
self.view.semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;
Reference: UISemanticContentAttribute
You can always flip views horizontally until you get the desired setup.
[view setTransform:CGAffineTransformMakeScale(1, 1)];
Here's some code that might help you.
void reloadRTLViewAndSubviews(UIView *view)
{
reloadRTLViews(#[view]);
reloadRTLViews([view subviews]);
}
void reloadRTLViews(NSArray *views)
{
if (isRTL())
{
[views enumerateObjectsUsingBlock:^(UIView* view,
NSUInteger idx,
BOOL * stop)
{
[view setTransform:CGAffineTransformMakeScale(-1, 1)];
}];
}
else
{
[views enumerateObjectsUsingBlock:^(UIView* view,
NSUInteger idx,
BOOL * stop)
{
[view setTransform:CGAffineTransformMakeScale(1, 1)];
}];
}
}
BOOL isRTL()
{
return isRTL_app();
}
BOOL isRTL_device()
{
BOOL isRTL = ([NSLocale characterDirectionForLanguage:[[NSLocale preferredLanguages] objectAtIndex:0]] == NSLocaleLanguageDirectionRightToLeft);
return isRTL;
}
BOOL isRTL_scheme()
{
BOOL isRTL = ([UIView userInterfaceLayoutDirectionForSemanticContentAttribute:[UIView new].semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft);
return isRTL;
}
BOOL isRTL_app()
{
NSString *languageIdentifier = [AppDataManager sharedManager].languageIdentifier;
NSArray *rtl_languages = #[#"ar"];
BOOL isRTL;
if ((languageIdentifier == nil) || (languageIdentifier.length == 0))
{
isRTL = (isRTL_device() || isRTL_scheme());
}
else if ([rtl_languages containsObject:languageIdentifier])
{
isRTL = YES;
}
else
{
isRTL = NO;
}
return isRTL;
}
BOOL deviceLanguageDirectionEqualAppLanguageDirection()
{
if ((isRTL_device() || isRTL_scheme()) && isRTL())//All RTL
{
return YES;
}
else if (!(isRTL_device() || isRTL_scheme()) && !isRTL())//All LTR
{
return YES;
}
return NO;//RTL-LTR or LTR-RTL
}
void transformViews(NSArray *views)
{
[views enumerateObjectsUsingBlock:^(UIView* view,
NSUInteger idx,
BOOL * stop)
{
[view setTransform:CGAffineTransformMakeScale(-1, 1)];
}];
}
So if you were to change a UIViewController to be in RTL you can make all the setup so that it would suffice to:
reloadRTLViewAndSubviews(self.view);
Note: this is not the way it should be and Apple's guidelines say that the language should be change from the iOS settings. But this solution works if the language must be changed within the app and a language direction change between LTR and RTL is involved. And no app reset needed either.
I hope it helped.

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
}
}
}

Check for class existence in Swift

I want to use NSURLQueryItem in my Swift iOS app. However, that class is only available since iOS 8, but my app should also run on iOS 7. How would I check for class existence in Swift?
In Objective-C you would do something like:
if ([NSURLQueryItem class]) {
// Use NSURLQueryItem class
} else {
// NSURLQueryItem is not available
}
Related to this question is: How do you check for method or property existence of an existing class?
There is a nice section in https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/AdvancedAppTricks/AdvancedAppTricks.html#//apple_ref/doc/uid/TP40007072-CH7-SW4 called Supporting Multiple Versions of iOS, which explains different techniques for Objective-C. How can these be translated to Swift?
Swift 2.0 provides us with a simple and natural way to do this.It is called API Availability Checking.Because NSURLQueryItem class is only available since iOS8.0,you can do in this style to check it at runtime.
if #available(iOS 8.0, *) {
// NSURLQueryItem is available
} else {
// Fallback on earlier versions
}
Simplest way I know of
if NSClassFromString("NSURLQueryItem") != nil {
println("NSURLQueryItem exists")
}else{
println("NSURLQueryItem does not exists")
}
Try this:
if objc_getClass("NSURLQueryItem") != nil {
// iOS 8
} else {
// iOS 7
}
I've also done it like this too:
if let theClass: AnyClass = NSClassFromString("NSURLQueryItem") {
// iOS 8
} else {
// iOS 7
}
Or, you can also check system version like so, but this isn't the best practice for iOS dev - really you should check if a feature exists. But I've used this for a few iOS 7 hacks... pragmatism over purity.
switch UIDevice.currentDevice().systemVersion.compare("8.0.0", options: NSStringCompareOptions.NumericSearch) {
case .OrderedSame, .OrderedDescending:
iOS7 = false
case .OrderedAscending:
iOS7 = true
}

Retrieving Carrier Name from iPhone programmatically

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.

Resources