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

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.

Tags:

Ios

Keyboard

Ios8