📷

Instagram の DM 不具合はなぜ起きたのか ^_^^_^^_^

2021/04/03に公開

何が起こっているのか

インスタグラムのDMで「漢字変換ができない」「濁点が入力できない」といったバグが発生しているようです。
※ 現在はアプデで修正済みな様子

なぜ起こるのか (再現する)

今回は「かえる」という文字のみ探し出して緑色にするという実装をしてみます。

class ViewController: UIViewController, UITextViewDelegate {
    let searchText = "かえる"
    
    func textViewDidChange(_ textView: UITextView) {
        let attributedText = NSMutableAttributedString(string: textView.text, attributes: [.foregroundColor: UIColor.darkText])
        if let range = textView.text.range(of: searchText) {
            attributedText.addAttributes([.foregroundColor: UIColor.green], range: range.toNSRange(from: textView.text))
        }
        textView.attributedText = attributedText
    }
}
動くためには以下のコードも必要です
  • UITextView の View への追加と delegate 適用
  • 下記の extension
  • その他色々 (まあわかるよね)


extension Range where Bound == String.Index {
    func toNSRange(from text: String) -> NSRange {
        return NSRange(self, in: text)
    }
}

これだけで簡単に再現します。
「かえる」の文字色を緑にすることは実現できていますが、
今回のポイントである変換できない/濁点を打てないバグが発生しています。

再現GIF
3倍速再生です

コードを見ていただいてもわかるように、
1文字入力されるたびにテキストを確認し、表示になんらかの修正を加えています。
入力中の文字を強制上書きしていることになるため、変換中である文字が解除されます。

つまり1文字入力するたびに"確定"されている状態です。

そのため、変換もできず、
「が」と打ちたくても「か」で確定されてしまい、濁点の位置にある顔文字「 ^_^ 」が入力されてしまいます。

英語圏などでは文字入力で「変換する」という習慣がないはずで、この問題に気づきにくいと思います。
そのため、インスタグラムレベルのサービスでもこのようなバグが発生したんだと思います。

ちなみに一時期界隈で話題になった Honk でも

Honk というリアルタイムチャットのようなものが一時期話題になっていました。
こちらでも同様の問題が今現在(2021/04/03)でも発生しています。

アメリカ発で、日本市場の優先順位も低いことが予想されるため、もうしばらくは対応されないでしょう。
英語圏がメインのアプリを探せば、このようなバグが残っているアプリはまだまだあると思います。

サードパーティキーボードでは発生しない

ちなみにこちらのバグは、 ATOK Simeji のようなサードパーティキーボードでは発生しないはずです。
なぜなら、標準キーボードで実現されている 「入力中の文字背景色を変えて行う変換機能」をOSレベルでサードパーティに開放していない からです。

だからなに?

「入力中の文字背景色を変えて行う変換機能」をOSレベルでサードパーティに開放していない

ので、入力された文字は (OS 的には) ひらがなのまま確定されているのと同じ状況となります。
そこで変換機能を実現するために、 入力中の文字を一時的に保持しておき、変換候補が選択されたら、 入力されていた文字をバックスペースで削除した上で、漢字を入力する という挙動がシステム的には走っています。

流れ的にはこうです。

  1. 「かえる」と入力する (OS的には変換中ではなく確定された状態)
  2. キーボード上の変換候補にある「蛙」をタップ
  3. 「バックスペース」キーを3回タップし、「蛙」を入力する、というのをシステム的に一瞬で行う
  4. ユーザーとしては「かえる」が「蛙」に変換されたように見える

今回のバグの原因は、前述の通り「 1文字入力するたびに強制的に確定されているのと同じ 」状況だからです。
一方でサードパーティキーボードは、バグに関係なく常に「 1文字入力するたびに確定している 」のと同じ状況であり、その状況で正常に動作するように作られているため、今回のバグには影響されなかったということになります。

補足

筆者は普段 ATOK を利用しています。 ATOK での現状をお伝えします。

バグが発生する状況でも ATOK では発生してないよという GIF

前述のバグが発生するコードとおなじビルドですが、ATOK では発生していません。

ATOK はユーザにどう説明しているか

「カーソル位置入力」という名称の機能になっていますが、これを ON にした状態が、先程 だからなに? に書いた通りの処理となります。

この機能は、OS の制限を無理やり回避して実現している機能であるため、Apple 的には喜ばれた実装ではないはずです。
なおかつ、このような無理な実装は、アプリによっては正常に動作しない可能性があります。
そのため、キーボードの上に入力中の文字を表示するような「 制限の中で望まれた実装 」をしているのが、「カーソル入力位置 = OFF」 ということになります。

解決策

めちゃくちゃ簡単です。

markedTextRange というプロパティがあり、変換中の文字の範囲が取得できます。
これが nil ならば、変換中のならば文字列が無いということになり、表示中のテキストに装飾処理を反映しても問題ないということになります。

func textViewDidChange(_ textView: UITextView) {
    if textView.markedTextRange != nil {
        // 変換中の文字がある!
        return
    }
    
    // ... 以下先程のコードが続く ...
}

「かえる」の緑装飾を実現しつつ、濁点の入力や変換などが機能していることが確認できました。

回避GIF

もっと細かく実装したいのであれば、 UITextRange の位置情報がとれるので、その値を活用するのも手です。

Discussion