As you aware that the famous _XPConnectionHasEntitlement has no longer works in iOS 8, is there anyother way to bypass the entitlements with the tweaks that requires entitlements? I come to know that _BSAuditTokenTaskHasEntitlement might solve the issue, but I can't get through it.
I'm using following snippet of code to hook into backboardd & assertionsd.
static int (*orig_BSAuditTokenTaskHasEntitlement)(id connection, NSString *entitlement);
static int hooked_BSAuditTokenTaskHasEntitlement(id connection, NSString *entitlement) {
NSLog(#"Got it.");
if (xpc_connection_get_pid(connection) == [[UIDevice currentDevice] __qrwaGetPIDForProcess:#"SpringBoard"] && [entitlement isEqualToString:#"com.apple.multitasking.unlimitedassertions"]) {
return 1;
} else {
return orig_BSAuditTokenTaskHasEntitlement(connection, entitlement);
}
}
%ctor {
%init;
MSHookFunction(((int *)MSFindSymbol(NULL, "_BSAuditTokenTaskHasEntitlement")), (int*) hooked_BSAuditTokenTaskHasEntitlement, (int**) &orig_BSAuditTokenTaskHasEntitlement);
}
The problem with it, the NSLog statements never printed. So I feel that something wrong with syntax of the function _BSAuditTokenTaskHasEntitlement, but not sure.
If anyone points me right direction, I appreciate their help.
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
}
}
}
I have a segmented control I wrote myself (UISegmenterControl) which sends a message once the selected segment has changed:
- (void)setSelectedSegmentIndex:(NSInteger)selectedSegmentIndex
{
if(_selectedSegmentIndex != selectedSegmentIndex) {
NSInteger segmentIndex = 0;
for (UISegmenterControlSegment *segmentButton in self.segmentButtons) {
if (segmentIndex == selectedSegmentIndex) {
[segmentButton setSelected:YES];
_selectedSegmentIndex = selectedSegmentIndex;
if (self.target != nil) {
if ([self.target respondsToSelector:self.action]) {
objc_msgSend(self.target, self.action, self);
}
}
}
else {
[segmentButton setSelected:NO];
}
segmentIndex++;
}
}
}
The objc_msgSend fires off (void)didChangeSegmentControl:(UISegmenterControl *)control in my view controller whilst passing a pointer to the segmented control itself.
Problem is, since upgrading to XCode 5.1 beta 4 and my development device to IOS 7.1 beta 4 my code is falling over on the device with EXC_BAD_ACCESS (code=1) when didChangeSegmentControl fires off.
It seems that the pointer to the segmented control (i.e. the 'control' parameter) is being lost between the objc_msgSend and didChangeSegmentControl.
What's confusing me most here is that this wasn't happening until I upgraded and still doesn't happen either on the simulator or on my device when profiling the app. I guess what I'm asking is: does this look to be a problem with my code (that somehow functioned perfectly fine on previous IOSes and/ or XCodes) or might this be an issue with the latest beta?
Please let me know if you need any more information about my app.
Rather than using objc_msgSend, can you use the following instead:
[self.target performSelectorOnMainThread:self.action withObject:self waitUntilDone:YES];
P.S. Thanks for the update on the correct objects in the comments
Using the following method i get the right order instantly on iOS 5 but the order not changing on iOS 6 & 7 after switching it in the settings, nor after restarting application (after closing settings).
BOOL firstNameFirst = NO;
if (IOS_VERSION>=7) {
firstNameFirst = (ABPersonGetCompositeNameFormatForRecord(NULL)==kABPersonCompositeNameFormatFirstNameFirst);
}else{
firstNameFirst = (ABPersonGetCompositeNameFormat() == kABPersonCompositeNameFormatFirstNameFirst);
}
What am i missing?
EDIT:
This bool supposed to store the user default order, so i can order the first and last name strings and show them to the user in the way he set it in the iPhone settings.
EDIT2: the IOS_VERSION macro works perfectly, i checked them using breakpoint, but whatever the Settings / Mail, Contacts, Calendar / Contacts / Display Order settings is, i always get YES for firstNameFirst's value
I hope to be helpful to you.
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
BOOL firstNameFirst = NO;
if (IOS_VERSION>=7) {
firstNameFirst = (ABPersonGetCompositeNameFormatForRecord(NULL)==kABPersonCompositeNameFormatFirstNameFirst);
}else{
firstNameFirst = (ABPersonGetCompositeNameFormat() == kABPersonCompositeNameFormatFirstNameFirst);
}
if(addressBook != NULL) {
CFRelease(addressBook);
}
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1)
{
// Load resources for iOS 6.1 or earlier
} else
{
// Load resources for iOS 7 or later
}
do like this.
reference taken from this.
This Link
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")
}