初めてのSwiftアプリ開発:FlowIMEを作った話
はじめに
macOSで日本語と英語を切り替えながら文章を書くの、めんどくさくないですか?
「日本語打って、英語打って、また日本語に戻して...」この繰り返しがストレスすぎて、IMEを自動で切り替えるアプリ「FlowIME」を作ることにしました。
これが私の初Swift、初macOSアプリ開発です。この記事では、開発で苦労したこと、ハマったところ、そして今も抱えている課題について書いていきます。
🔗 公式サイト: https://flowime.netlify.app/
FlowIMEとは?
FlowIMEは、カーソル前の文字が日本語か英語かを判定し、自動的にIMEを切り替えるmacOSアプリです。
主な機能
-
スマートな自動切り替え
- カーソル前の文字が日本語なら日本語IMEに、英語なら英語モードに自動切り替え
-
コンテキスト認識
- 空白・改行・文頭では切り替えを行わず、ユーザーの意図を尊重
-
高速入力対応
- 連続入力中は判定をスキップし、タイピングの邪魔をしない
-
手動操作の尊重
- ユーザーが手動でIMEを切り替えた場合、自動判定を一時停止
なぜ作ったのか
プログラミングのコメント書いたり、技術文書作ったりしてると、日本語と英語を行ったり来たりすることが多いんですよね。
で、ふと思ったんです。「カーソルの前の文字見れば、次に何語打つかわかるんじゃね?」って。
このシンプルすぎるアイデアから、全てが始まりました。
苦労したこと
1. 日本語変換処理中のバグとの戦い
これが一番ハマりました。日本語変換してる最中に英語に切り替わるんですよ。
日本語打ってる途中で、変換もまだ確定してないのに自動切り替えが発動して、入力が中断されちゃう。マジで困りました。
特にやばかったケース:
- Enterで変換確定した直後:確定直後に次の文字打つと、システムの遅延で英語と誤判定される
- Spaceで変換候補選んでる時:候補選んでる間に切り替わったら最悪
- Deleteで変換キャンセルした時:日本語入力やり直したいだけなのに英語になる
解決策:日本語セッションの追跡
「日本語入力セッション」っていう仕組みを作りました。日本語文字(ひらがな、カタカナ、漢字)を打ち始めたら、以下のどれかが起こるまでセッション継続:
- カーソル移動(矢印キー)
- マウスクリック
- アプリ切り替え
- 1.5秒放置
セッション中は自動切り替えしません。ユーザーの日本語入力を邪魔しないようにしました。
あと、Spaceキー押してる間(変換候補選択中)は絶対に切り替えないようにしました。これ超重要。
2. 日本語入力中に英語を打ちたい時の対処
これも結構難しかった。
例えば、こんな文章:
このアプリはhttps://example.comで公開してます。
「このアプリは」まで日本語で書いて、次にURL英語で打ちたいじゃないですか。でも単純に「カーソルの前が日本語だから日本語IME」って判定すると、URLまで日本語モードで打つことになっちゃう。
問題の本質
ユーザーの意図を読み取る必要がありました:
- 連続して日本語打ってる時:切り替えない(上の日本語セッション)
- カーソル移動して新しい場所に入力する時:前の文字見て判断
- 手動でIME切り替えた時:ユーザーの意図を尊重
解決策:決定ゲート
「決定ゲート」っていう仕組みを作りました。自動切り替えするかどうかの判断を、ゲート(門)で制御するイメージ。
ゲート開いてる時(自動切り替えON):
- マウスクリック直後
- 矢印キー押した後
- アプリ切り替えた後
- しばらく何も打たなかった後
ゲート閉じてる時(自動切り替えOFF):
- 日本語を連続入力中
- 手動でIME切り替えた直後(数秒間)
スロットル処理
0.2秒以内に連続でキー押された場合は判定スキップ。高速タイピング中に余計な判定入って遅くなるの防ぎます。
手動切り替えの尊重
Cmd+SpaceとかControl+Spaceで手動切り替えしたら、「ユーザーが自分で切り替えた」って判断して、数秒間は自動切り替え停止します。
これらを組み合わせて、やっと実用的になりました。
3. Accessibility APIとイベントタップの複雑さ
他のアプリの入力監視するには、Accessibility権限が必要なんですよ。これが予想以上にめんどくさかった。
イベントタップが壊れやすすぎる
キーボード入力監視するために「イベントタップ」っていう仕組み使うんですけど、これがマジで壊れやすい。
タイムアウトで勝手に無効化される:システムが忙しいと、macOSが勝手にイベントタップ無効化しちゃうんです。悪意のあるアプリからシステム守るための機能なんですけど、正当なアプリにとっては迷惑すぎ。
無効化検出して、自動で再有効化する仕組み作る必要がありました。
権限が突然消える:ユーザーがアプリ動かしてる最中にシステム設定でAccessibility権限外すと、アプリがクラッシュする可能性あり。
これ防ぐために、2秒ごとに権限チェックして、なくなったら安全に再起動する仕組み入れました。
テキスト取得が超難しい
「カーソルの前の文字取得する」って、簡単そうじゃないですか?実は超難しいです。
Accessibility API使って取得するんですけど:
- アプリによって動作が違う:VSCodeは取れるけどChromeは取れない、みたいな
- 遅延がヤバい:100ms以上かかることもあって、タイピングの邪魔になる
- セキュリティ制約:パスワードフィールドとかは取得できない
取得失敗した時のフォールバック処理とか、遅延最小化するための最適化とか、色々やる必要ありました。
あと、ユーザーに権限付与してもらう必要があるから、初回起動時の説明どうするかも悩みました。
4. IME切り替えAPIの制約とGoogle日本語入力の問題
macOSには公式の「IME切り替えAPI」がないんですよ。だからText Input Services(TIS)っていう低レベルAPI直接いじる必要がありました。
macOS標準IMEは簡単
macOS標準の日本語入力(ことえり/日本語IM)は、わりと簡単:
- 利用可能な入力ソース一覧取得
- 日本語IME探す
-
TISSelectInputSourceで切り替え
これだけで動きます。
Google日本語入力の悪夢
ところが、Google日本語入力は全く別物でした。
Google日本語入力って内部的に複数の「モード」持ってるんです:
-
ひらがなモード(
com.google.inputmethod.Japanese.base) -
英数モード(
com.google.inputmethod.Japanese.Roman)
で、これらが通常の方法で切り替えられない。
macOSのAPIで「選択可能な入力ソース」として認識されないことがあって、直接切り替えようとしても失敗します。
苦肉の策:Control+Spaceのシミュレーション
仕方ないから、Control+Spaceキーみたいに入力ソースを順番にサイクルする方法実装しました。
仕組み:
- 現在の入力ソース確認
- 目的のモード(
.base)じゃなかったら、次の入力ソースに切り替え - また確認して、まだ違えば次へ
- 最大10回試行して、それでもダメなら諦め
問題点:
- 不安定:システムの状態で失敗する
- 遅い:複数回切り替え必要で、各50ms待機
- 予測不能:ユーザーが他のIME入れてると予期しない動作
公式ドキュメントほぼなくて、試行錯誤とログ出力の繰り返しでした。
現在の課題と制約
対応IMEについて(重要)
実は、基本的にABC(英語キーボード)と日本語ローマ字入力でしか動作確認してません。
私が普段使ってるのがこの組み合わせなんで、開発もこれベースでやってました。
動作確認済み
- ABC(英語キーボード) + macOS標準の日本語ローマ字入力
一応実装はしてるけど不安定
- Google日本語入力:実装はしてるけど超不安定です
Google日本語入力は以下の問題があります:
-
.baseモード(ひらがな)が選択不可能とマークされてる場合がある - 入力ソース切り替え中に予期しない動作
- システムの状態で切り替え失敗する
これ、Google日本語入力がmacOSのInput Source APIと完全に統合されてないのが原因で、個人開発じゃ完全解決は難しいです。
未対応
- US配列以外のキーボード
- JIS配列
- かな入力
- その他のIME(ATOK、Azookey等)
申し訳ないですが、色んな環境でテストする時間なかったんで、ABC + 日本語ローマ字以外は動かない可能性高いです。
OSバージョンによって動かない可能性
現在、macOS 15.5以降を動作環境としていますが、それ以前のバージョンや将来のバージョンでは動作しない可能性があります。
個人開発のため、広範囲のOSバージョンでのテストやメンテナンスが難しく、最新のmacOSでの動作を優先しています。
特に、macOSのセキュリティポリシーやAccessibility APIの仕様変更により、将来的に動作しなくなるリスクもあります。
Apple Siliconとの互換性
幸い、SwiftUIとAppKitを使用しているため、Apple Silicon(M1/M2/M3)とIntel Macの両方で動作します。
ただし、実機テストは限られた環境でしか行えていないため、一部の環境では予期しない問題が発生する可能性があります。
技術スタック
- 言語: Swift
- フレームワーク: SwiftUI, AppKit
-
API:
- Accessibility API(キーボード入力の監視)
- Text Input Services(IME切り替え)
- Carbon Events(キーボードイベント)
- 開発環境: Xcode, macOS 15.5+
配布について
アプリは完全無料で公開しています:
DMGファイルをダウンロードして、Applicationsフォルダにドラッグ&ドロップするだけで使用できます。
ただし、初回起動時にAccessibility権限の付与が必要です。
学んだこと
Swiftの表現力
Swiftは初めて触る言語でしたが、型安全性が高く、モダンな言語機能(Optional、Guard文、Extensionなど)により、安全で読みやすいコードが書けました。
// Optionalの安全な展開
guard let button = statusItem?.button else { return }
// Extensionで機能を拡張
extension String {
var isJapanese: Bool {
// 日本語判定ロジック
}
}
SwiftUIの便利さと制約
設定画面の実装にはSwiftUIを使用しましたが、宣言的UIの書き心地は非常に快適でした。
ただし、メニューバーアプリのような特殊なUIはAppKitを併用する必要があり、両方のフレームワークを理解する必要がありました。
macOSアプリ開発特有の難しさ
iOSアプリと比べて、macOSアプリは以下の点で難易度が高いと感じました:
- セキュリティ制約(Sandbox、権限管理)
- ドキュメントの少なさ(特にメニューバーアプリ)
- レガシーAPI(Carbon)との共存
- コード署名とNotarization(公証)の複雑さ
今後の展望
個人開発なんで限界ありますけど、以下考えてます:
- Google日本語入力の安定化(できれば)
-
多言語対応
- 韓国語、中国語、フランス語など
- 多言語混在の文章にも対応したい
-
設定のカスタマイズ性向上
- 自動切り替えの感度調整
- 特定アプリでの無効化
- パフォーマンスの最適化
特に多言語対応は将来的にやりたいですね。日本語・英語だけじゃなくて、韓国語とか中国語とか混ぜて使う人もいるでしょうし。
ただ、時間とリソースの制約あるんで、いつ実現できるかは未定です。
まとめ
初Swiftアプリ開発、想像以上に大変でしたけど、めちゃくちゃ学びになりました。
特に日本語変換中のバグとか、日本語入力中に英語挿入したい時の対処とか、IME制御の複雑さは予想外。
完璧なアプリじゃないですけど、私は毎日使ってて、IME切り替えのストレスかなり減りました。
同じ不便さ感じてる人いたら、ぜひ試してみてください。
ダウンロード: FlowIME公式サイト
対応環境: macOS 15.5以降(Apple Silicon / Intel)
価格: 無料
おまけ:開発期間と統計
- 開発期間: 約3日(空き時間)
- 総コード行数: 約800行
- 主要ファイル数: 8ファイル
- 使用したライブラリ: 標準ライブラリのみ
- 開発パートナー: Claude(生成AI)
正直、3日でここまで作れたのは生成AIと一緒に開発したからです。
私、Swift初心者なんですよ。でもClaude Code使って、AIと対話しながら開発したら、めちゃくちゃスムーズに進みました。
- わからないAPIの使い方 → AIに聞いたらすぐ教えてくれる
- バグで詰まった → AIと一緒にデバッグ
- 複雑なロジック → AIと会話しながら整理
AIの進化、マジでやばいです。
数年前なら、Swift初心者が3日でこんなアプリ作るの無理でした。でも今は、AIがペアプログラミングのパートナーになってくれる。
技術的なハードルが劇的に下がってます。「プログラミングできないから無理」って時代じゃないですよ。
「こんなアプリあったらいいな」って思ったら、AIと一緒にチャレンジしてみてください!
バグ報告・機能要望・質問について
個人開発なんで、色んな環境でテストできてないんですよ。バグ見つけたり、機能要望あったら、Twitterで連絡ください。
Twitter/Xでのフィードバック
開発者: @taro05069596
ハッシュタグ: #FlowIMEつけてツイートしてください
以下の情報あると助かります:
🐛 バグ報告:
- macOSのバージョン
- 使ってるIME(Google日本語入力、macOS標準など)
- どんな操作した時に起きたか
- 期待してた動作と実際の動作
💡 機能要望:
- どんな機能欲しいか
- それがあると何が便利になるか
❓ 質問:
- 使い方わかんない
- 設定どうすればいい
対応について
個人開発なんですぐ対応できないかもですけど、できる限り早く対応します。
優先的に対応するやつ:
- クリティカルなバグ(クラッシュするとか)
- 多くの人に影響する問題
- 再現手順が明確な問題
フィードバック待ってます!
Discussion