Custom Keyboard Extensions: Getting Started

Custom keyboard extensions give you the ability to provide keyboards to apps outside of your own. In this tutorial, you’ll walk through creating a custom keyboard extension with advanced features like autocomplete. By Eric Cerney.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Creating a Keyboard Extension

To create a keyboard extension on an existing project, go to File ▸ New ▸ Target…, select iOS and choose Custom Keyboard Extension. Click Next and name your keyboard MorseKeyboard. Make sure you select MorseCoder for Embedded in Application.

Click Finish to create your extension. If you get a popup to active the scheme, select Activate.

Xcode creates a new folder in the Project navigator for the extension. Expand MorseKeyboard to reveal its contents.

Take a look at each of the created keyboard extension files:

  • KeyboardViewController.swift: A UIInputViewController subclass that acts as the primary view controller for custom keyboard extensions. This is where you’ll connect the MorseKeyboardView and implement any custom logic for the keyboard similar to how it’s done in PracticeViewController.
  • Info.plist: A plist that defines the metadata for your extension. The NSExtension item contains keyboard specific settings. You’ll cover the importance of this item later in the tutorial.

And that’s it. Two files is all you need to get going with a keyboard extension!

Open KeyboardViewController.swift to get a closer look at the generated template. You’ll notice a lot of code within viewDidLoad(). As mentioned earlier, the main requirement of a custom keyboard is that it provides a key to switch to other keyboards.

This code programmatically adds a UIButton to the keyboard with an action of handleInputModeList(from:with:). This method provides two different pieces of functionality:

  • Tapping the button will switch to the next keyboard in the list of user-enabled keyboards.
  • Long-pressing the button will present the keyboard list.

Time to run and test the keyboard! Unfortunately, running and debugging a keyboard extension is a little more involved than a typical app.

Enabling a Custom Keyboard

Select the MorseKeyboard scheme from the scheme selector at the top of Xcode.

Build and run the scheme, and you’ll be able to choose which app to embed it in. For your purposes, select Safari since it has a text field for you to use, and click Run.

After Safari loads, minimize the app (Press Command + Shift + H if on a simulator). Open the Settings app and go to General ▸ Keyboard ▸ Keyboards and select Add New Keyboard…. Finally, select MorseCoder.

Fortunately, you only need to add the keyboard once to each of your devices. Close the Settings app and open Safari. Select the address bar to present the system keyboard.

Note: If the iOS system keyboard doesn’t present itself in the Simulator, that may be because you’ve configured it to treat your computer’s keyboard as a connected hardware keyboard. To undo this, go and toggle Hardware ▸ Keyboard ▸ Connect Hardware Keyboard.

Once you see the system keyboard, long-press on the globe key. Select the newly added MorseKeyboard.

And there you have it: your very lacklustre keyboard! If you tap or long-press the Next keyboard button, you can switch back to the system keyboard.

That’s enough of this basic functionality. It’s time to join the keyboard big leagues with some custom UI!

Customizing Keyboard UI

Rather than having to recreate the UI in both your container app and extension, you can share the same code across both targets.

In the Project navigator, Command-click the following six files to select them:

  • MorseColors.swift
  • MorseData.swift
  • KeyboardButton.swift
  • MorseKeyboardView.swift
  • MorseKeyboardView.xib
  • Assets.xcassets

Now open the File inspector and add the selected files to the MorseKeyboard target:

This will add these files to both compilation targets, the freestanding MorseCoder app and the MoreKeyboard app extension. This is perhaps the simplest way to share code — use exactly the same source code file twice, for different targets which create different build products.

Now that your keyboard extension has access to these files, you can reuse the same UI. Open KeyboardViewController.swift and replace the entire contents of KeyboardViewController with:

// 1
var morseKeyboardView: MorseKeyboardView!

override func viewDidLoad() {
  super.viewDidLoad()
  
  // 2
  let nib = UINib(nibName: "MorseKeyboardView", bundle: nil)
  let objects = nib.instantiate(withOwner: nil, options: nil)
  morseKeyboardView = objects.first as! MorseKeyboardView
  guard let inputView = inputView else { return }
  inputView.addSubview(morseKeyboardView)
  
  // 3
  morseKeyboardView.translatesAutoresizingMaskIntoConstraints = false
  NSLayoutConstraint.activate([
    morseKeyboardView.leftAnchor.constraint(equalTo: inputView.leftAnchor),
    morseKeyboardView.topAnchor.constraint(equalTo: inputView.topAnchor),
    morseKeyboardView.rightAnchor.constraint(equalTo: inputView.rightAnchor),
    morseKeyboardView.bottomAnchor.constraint(equalTo: inputView.bottomAnchor)
    ])
}

KeyboardViewController now only contains code to set up the keyboard view. Here’s what you created:

  1. A property to hold reference to a MorseKeyboardView object.
  2. An instance of MorseKeyboardView is added to the controller’s root inputView.
  3. Constraints pinning morseKeyboardView to the superview are added and activated.

Rather than building out the UI from scratch, you’re simply reusing the same code from the host app. Build and run the keyboard extension through Safari. Select the address bar and long-press the globe key to select MorseKeyboard.

Things are looking a little better: The keys are clickable, and the correct letter is being shown within the keyboard preview, but the address bar isn’t updating.

Note: You’ll notice that the height of the keyboard is different from that of the system keyboard. Keyboard extensions automatically infer their height based on the nib’s Auto Layout constraints.

Note: You’ll notice that the height of the keyboard is different from that of the system keyboard. Keyboard extensions automatically infer their height based on the nib’s Auto Layout constraints.

Things look great, but something’s missing: the mandatory next keyboard button!

Open MorseKeyboardView.xib and you’ll see there’s already a next keyboard globe added to the nib.

Hmm… it looks like something’s hiding the next keyboard key. Open MorseKeyboardView.swift and look at the method setNextKeyboardVisible(_:).

This is a custom method that actives and deactivates certain constraints to hide or show the next keyboard key. It exists because there are situations where you need to hide the key.

UIInputViewController defines a property needsInputModeSwitchKey that tells your custom keyboard whether or not it’s required to show a next keyboard key. An example of when this might be false is when your keyboard is running on an iPhone X. This device provides a next keyboard key below a raised keyboard by default, so you don’t need to add your own.

You’ll use this property to control the visibility of the globe key. Open KeyboardViewController.swift and add the following line to the bottom of viewDidLoad():

morseKeyboardView.setNextKeyboardVisible(needsInputModeSwitchKey)

This tells the Morse keyboard whether or not to hide the next keyboard key based on the value of needsInputModeSwitchKey.

To test this out, you’ll need to run the keyboard on multiple devices. Build and run on both an iPhone X and any other iPhone. Remember to add the keyboard through Settings ▸ General ▸ Keyboard ▸ Keyboards ▸ Add New Keyboard… if you’re running a new device.

You should see that the iPhone X hides your custom next keyboard key because there’s already a system globe key. You’ll also see your custom key as expected on the other device.

Right now your key doesn’t do anything, but frankly, neither do any of the other buttons. It’s time to fix that!

Eric Cerney

Contributors

Eric Cerney

Author

Alexis Gallagher

Tech Editor

Chris Belanger

Editor

Jeff Rames

Final Pass Editor

Richard Critz

Team Lead

Over 300 content creators. Join our team.