2014年12月22日

iOS

Yahoo!キーボードを支える技術とリーンスタートアップの実践

  • このエントリーをはてなブックマークに追加

Yahoo! JAPAN Tech Advent Calendar 2014の22日目の記事です。一覧はこちら

mainImage
はじめまして、Yahoo!キーボード PM 兼 iOS アプリ開発の千葉俊輝です。
今回は、iOS 8 で新たに追加された Custom Keyboard の作り方とYahoo!キーボードで使われているテクニックやプロダクトとしての成功ポイントを紹介します。

iOS 8 Custom Keyboard ?

これまで、ユーザーは標準キーボードしか利用できませんでした。
iOS 8 ではサードパーティ製のソフトウエアキーボードが作成可能となったので、ユーザーは好みや目的に応じてキーボードを使用できるようになりました。

iOS 8 Custom Keyboard の全体図

キーボードアプリ全体の流れはこの図の通りです。
次のセクションでキーボードアプリの作り方を紹介します。
keyboardImage
(画像引用元:App Extension Programming Guide

Custom Keyboard を作ってみる

  • 通常通りプロジェクトつくる
    step1

  • 新しく Target を追加
    step2

  • Application Extension から Custom Keyoboard を選択
    step3

  • Product Name を記述して Target の追加完了
    step4

  • Target を追加するとカスタムキーボードのファイル群が追加される
    step5

UIInputViewController & UITextDocumentProxy ?

テンプレートで Custom Keyoboard を選択すると自動的に UIInputViewController が生成されます。
UIInputViewController には、以下のメソッドとプロパティが定義されています。

var inputView: UIInputView!
var textDocumentProxy: NSObject { get }
var primaryLanguage: String?
func dismissKeyboard()
func advanceToNextInputMode()
func requestSupplementaryLexiconWithCompletion(completionHandler: (UILexicon!) -> Void)

また、UIInputViewController には、UITextDocumentProxy というプロトコルが定義されています。
UITextDocumentProxy には、以下のメソッドとプロパティが定義されています。

var documentContextBeforeInput: String! { get }
var documentContextAfterInput: String! { get }
func adjustTextPositionByCharacterOffset(offset: Int)

UITextDocumentProxy というプロトコルは、UIKeyInput というプロトコルを採用しています。
UIKeyInput には、以下のメソッドが定義されています。

func hasText() -> Bool
func insertText(text: String)
func deleteBackward()

UIInputViewController でなにができるのか?

  • inputView キーボードのレイアウトを設定
  • dismissKeyboard() キーボードを閉じる
  • advanceToNextInputMode() キーボードの切り替え
  • requestSupplementaryLexiconWithCompletion ユーザー辞書取得

UITextDocumentProxy でなにができるのか?

  • documentContextBeforeInput 入力前の文章取得
  • documentContextAfterInput 入力後の文章取得
  • adjustTextPositionByCharacterOffset(offset: Int) キャレット(カーソル)の移動

UIKeyInput でなにができるのか?

  • hasText() テキスト入ってるか Bool で判定
  • insertText() テキストをテキストフィールドに入力
  • deleteBackward() 文字の削除

API で提供されているのは基本的にこれらのみです。
これらの API で提供されていないこと(日本語の変換やフリック入力など)を実現したい場合は、独自に実装しなければいけません。

また、Custom Keyboard を作る上で気をつけなければならないことを次のセクションで紹介します。

Custom Keyboard で気をつけること

Custom Keyboard の制限

※フルアクセス許可をもらえない場合

  • コンテナアプリ(アプリ本体)とキーボード間でのデータ共有ができない
  • キーボード自体からファイルシステムへのアクセスができない
  • iCloud、ゲームセンター、アプリ内課金できない

Custom Keyboard での注意

  • キーボード切り替えボタン(グローブキー)必須
  • 編集メニュー(コピー、カット、ペーストなど)、インライン自動補正制御にアクセスできません

参考

iOS 8で解禁されたカスタムキーボードを作ってみよう

Yahoo!キーボード

Yahoo!キーボードは、見た目の変更が出来る他、キーボードの切替がスワイプでできたり、文字の修正をカーソルで移動できたりと入力が便利に楽しくなるキーボードです。

フリック入力とスワイプキーボード切り替え機能の実現

Yahoo!キーボードの独自機能として、スワイプキーボード切り替え機能があります。
スワイプキーボード切り替え機能を実現する為には大きな課題がありました。
それは、テンキーのフリック入力とキーボードをスワイプして切り替える機能とのバッティングです。

フリック入力の実装

フリック入力の実装では、「UIResponder」クラスの以下メソッドを使用します。

func touchesBegan(touches: NSSet, withEvent event: UIEvent)
func touchesMoved(touches: NSSet, withEvent event: UIEvent)
func touchesEnded(touches: NSSet, withEvent event: UIEvent)
func touchesCancelled(touches: NSSet!, withEvent event: UIEvent!)
  • touchesBegan 指を View または Window に一本以上タッチしたときに実行されるメソッド
  • touchesMoved 指を View または Window に一本以上移動したときに実行されるメソッド
  • touchesCancelled システムイベント(メモリ不足など)がタッチイベントをキャンセルしたときに実行されるメソッド
  • touchesEnded 指を View または Window に一本以上離したときに実行されるメソッド

具体的な実装例は量が多すぎるので割愛させていただきます。
流れとしては、touchesBegan でフリックガイドの View を作成し、ボタンに対して addSubview します。
touchesMoved で4方向のどちらに向かって指を動かしているか検出し、フリックガイドに表示する文字を変化させます。(座標計算を行っているため、8方向でも検出可能です)
touchesEnded でフリック方向の文字列を insertText() の引数に渡す事で文字列をテキストフィールドに挿入します。
touchesEnded と touchesCancelled のタイミングで addSubview した View に対して removeFromSuperview() を呼び、ガイドの破棄を行うことで、標準キーボードのように指が触れたタイミングでガイドを表示し、指が離れたタイミングでガイドが非表示になる表現が実現できます。

フリックイメージ

スワイプキーボード切り替えの実装

スワイプキーボード切り替えの実装では、「UIScrollView」クラスの以下プロパティを使用します。

var contentSize: CGSize // default CGSizeZero
var pagingEnabled: Bool // default NO. if YES, stop on multiples of view bounds
var scrollEnabled: Bool // default YES. turn off any dragging temporarily
  • contentSize スクロール画面サイズ
  • pagingEnabled ページ毎にスクロールを許可するか
  • scrollEnabled スクロールを許可するか

実装自体は簡単で、キーボードとなる View を ScrollView に addSubview し、addSubview した View に合わせて、ScrollView の contentSize を設定します。
pagingEnabled と scrollEnabled をそれぞれ true にするだけでスワイプでのページング処理は完成となります。

スワイプイメージ

フリック入力とスワイプキーボード切り替えの共存

フリック入力とスワイプキーボード切り替え両方を実装すると両方のイベントが発生してしまいます。
具体的にいうと、横フリックするとスワイプイベントが発生しキーボードが切り替わってしまい、ユーザーにとって使いづらい機能となります。

Yahoo!キーボードでは、フリック入力開始のタイミングでスクロールを制御することで、フリック入力とスワイプキーボード切り替えの共存を実現しています。

成功のポイント

このYahoo!キーボードというプロダクトは Swift で開発した事以外に挑戦したことがあります。
それは、リーンスタートアップです。
Yahoo!キーボードPJは、アプリ1名、サーバー1名、デザイン1名、ビジネス1名の計4名で構成されている少人数のプロジェクトです。少人数で新規サービスを失敗の確率を減らして生み出す為にリーンスタートアップモデルにのっとって進めたことが挑戦したことであり、成功のポイントでした。

リーンスタートアップ ?

従来の開発手法とは異なり、実際の顧客の反応を見ながら学習し、製品の改善や軌道修正を行うことで失敗のコストを小さくする手法のことです。

Yahoo!キーボードPJが行ったリーン

まず、はじめに行った事はアイディアをリーンキャンバスに起こすことです。
リーンキャンバスとは、このようにプロダクトの全体像を整理したものです。
リーンキャンバス

最初は全てが仮説にすぎません。
開発者は、ついついユーザーはこんな機能を必要としているだろう、こんなUIが使いやすいだろうという“思い込み”で製品開発をスタートさせてしまいます。しかしそれらは仮説であり、製品を作りこんでから間違いだとわかると、その開発期間が無駄になってしまいます。
このリスクを回避するために、ユーザーインタビューを早期から実施しました。

リーンキャンバスの課題にあたる部分についてインタビューを実施することで、私たちが考える課題は間違っていないか? 他にも課題はあるのではないか? という視点で仮説の検証を行っていきました。
課題の確認後は、MVP(必要最低限機能のプロダクト)を作成し、解決策についてインタビューを実施しました。
課題に対して、この解決策で間違っていないか? 他の方法があるのではないか? このMVPで問題が発生しないか? という視点で検証し、改善を繰り返していきました。

Yahoo!キーボードPJでは、ターゲットとしている女子高生や女子大生と直接会ってインタビューすることで間違いを排除していきました。
Yahoo!キーボードには、自分の写真と好きな文字色をキーボードの背景に設定できる機能があります。当初は、たくさんの種類のなかから文字色や文字のスタイルを選べる機能を考えていました。ですが、インタビューしてみると、「こんなにたくさんはいらない。背景に合わせて文字が見えればいい。」という声が聞けました。ユーザーはカスタマイズ性の高さよりもカスタマイズのしやすさに価値を感じている事が検証できたのです。

私たちが考えるユーザーにとって欲しいと思われる機能は、インタビューを繰り返す中で削ってしてしまい、ほとんど残っていません。
今までは、「ユーザーはこう考えてるに違いない、こういう機能が欲しいはずだ」と考えていました。
しかし、それはあくまでも仮説にすぎず、事実ではありません。
このリーンスタートアップモデルにのっとり、実際にユーザーの声を聞き、仮説を検証する事でプロダクトをゴールに近づける事ができました。

答えは社内にありません。
答えを見つけるために、社外へ出てユーザーに問うことが成功の鍵となるでしょう。

さいごに

Yahoo!キーボードPJは、新しい技術や開発手法でプロダクトを生み出しました。
生み出した後も改善を繰り返し、ユーザーに必要とされるプロダクトに成長させていきます。

「自分らしさを表現できるキーボード」- Yahoo!キーボードを DL して触っていただければと思います。

Yahoo!キーボード

Yahoo! JAPANでは情報技術を駆使して人々や社会の課題を一緒に解決していける方を募集しています。詳しくは採用情報をご覧ください。

  • このエントリーをはてなブックマークに追加