壁紙・写真系のアプリを長く作っていると、ユーザーから「画像の中のテキストを読み取りたい」という声が増えてきます。2014年から個人でアプリ開発を続けてきた中で、こうした要望への対応はいつも手間がかかる作業のひとつでした。OCR(光学文字認識)を実装しようとして最初に調べたのがGoogle ML Kitでしたが、公式の手順どおりにやると、SwiftUIとの組み合わせでうまくいかない部分がいくつかあります。
Antigravityに実装を手伝ってもらいながら整理した結果、通常なら1〜2日かかりそうな作業が半日以内に終わりました。その過程で気づいたことと、ドキュメントには書かれていなかった詰まりポイントをまとめます。
ML KitはCore MLやTFLiteとどう違うのか
スマートフォンでAIを動かす方法は複数あります。Appleのエコシステムに閉じてよいならCore MLが最も安定していますが、iOS/Android両対応のアプリを1人で作る場合には別の選択肢が必要です。
Google ML Kitは、GoogleがFirebaseと連携して提供するオンデバイスAI SDKです。内部的にはTensorFlow Lite(TFLite)を使っていますが、開発者はTFLiteの低レベルAPIを意識せず済む高水準なAPIを利用できます。テキスト認識・顔検出・バーコードスキャン・ポーズ推定など、よく使われる機能があらかじめモデル込みで提供されているのが特徴です。
3つの選択肢をざっくり整理するとこうなります:
- Core ML: Apple専用。
.mlmodelファイルを使う。iOS/macOSのパフォーマンスは最高水準だが、Androidには使えない - TFLite: Google製クロスプラットフォームML。カスタムモデルを持ち込める柔軟性があるが、設定が複雑
- ML Kit: TFLite上のハイレベルAPI。既製モデルを使うだけなら最も手軽で、iOS/Android両対応
個人でiOS/Android両方を1人でリリースする場合、ML Kitは現実的な選択肢です。セットアップさえ終われば、テキスト認識を数十行のコードで実装できます。
AntigravityでML Kitを組み込む(Swift Package Manager経由)
ML KitはCocoaPodsとSwift Package Manager(SPM)の両方に対応しています。Xcode 15以降ならSPMが安定して使えるため、こちらが推奨です。
Antigravityに「ML Kitのテキスト認識をSPMで追加してSwiftUIのカメラビューに統合したい」と伝えると、依存関係の追加とカメラ権限のInfo.plist設定、AVFoundationとの接続コードまで一括して提案してくれました。
// Package.swift の dependencies に追加(テキスト認識 + 顔検出)
.package(
url: "https://github.com/google/mlkit-ios-official",
from: "6.0.0"
)ここで注意が必要な詰まりポイントがあります。ML KitのバイナリはXCFrameworkとして配布されており、SPMでのリンクにXcode 15.0.1以前では既知のバグがあります。公式ドキュメントにはこの制限が書かれていませんでした。
Antigravityはエラーメッセージから原因を特定して「Xcode 15.2以降を使うか、CocoaPodsに切り替える」と提案してくれました。Xcodeをアップデートして解決しましたが、CI環境を使っている場合はXcodeバージョンの確認を先に済ませておくと良いと思います。
テキスト認識の実装(iOS)
ML Kitのテキスト認識はTextRecognizerクラスを使います。SwiftUIのViewとAVCaptureSessionを連携させるコードをAntigravityが一度に書いてくれましたが、スレッド管理が重要です。
import MLKitTextRecognition
import MLKitVision
import AVFoundation
// カメラフレームからテキストをリアルタイム検出する
func recognizeText(from sampleBuffer: CMSampleBuffer) {
let visionImage = VisionImage(buffer: sampleBuffer)
visionImage.orientation = imageOrientation() // 端末向きを考慮
let recognizer = TextRecognizer.textRecognizer()
recognizer.process(visionImage) { [weak self] result, error in
guard error == nil, let result = result else { return }
let detectedText = result.text
DispatchQueue.main.async {
// UI更新はメインスレッドで
self?.detectedString = detectedText
}
}
}
// デバイス向きとAVFoundationの向きを変換する
func imageOrientation() -> UIImage.Orientation {
switch UIDevice.current.orientation {
case .landscapeLeft: return .up
case .landscapeRight: return .down
case .portraitUpsideDown: return .left
default: return .right
}
}ここで詰まったのがvisionImage.orientationでした。端末の向きに応じてCMSampleBufferの画像回転が変わるため、上記のような変換が必要になります。Antigravityはこの処理を自動で含めてくれましたが、シミュレータでは動作確認できません。実機テストは必ず行ってください。
顔検出で公式ドキュメントに書かれていなかったこと
顔検出(Face Detection)を試したとき、リアルタイム処理での精度がスナップショット処理より明らかに低いという挙動がありました。公式ドキュメントにはリアルタイムと静止画での精度差について記載がありませんでした。
フレームレートを下げ(30fps→15fps)、FaceDetectorOptionsのperformanceModeを.accurateに設定することで改善しましたが、処理遅延も増えます。
let options = FaceDetectorOptions()
options.performanceMode = .accurate // デフォルトは .fast
options.landmarkMode = .all // 目・鼻・口などのランドマーク検出
options.classificationMode = .all // 笑顔・目の開閉など
let faceDetector = FaceDetector.faceDetector(options: options)リアルタイム顔検出を使う場合は、精度とレイテンシのトレードオフを設計段階から意識する必要があります。このアプリでは最終的に「顔検出はスナップショットモードで使う」という設計に切り替えました。
Antigravityが効いた場面・効かなかった場面
効いた場面:
- SPMの依存関係エラーの原因特定と解決策提示(Xcodeバージョン問題を即座に特定)
- AVFoundationとML Kitを接続するボイラープレートコードの一括生成
- メインスレッド / バックグラウンドスレッドの適切な分離
効かなかった場面:
- 実機でしか再現しないバグの診断(シミュレータ前提のコード提案が来ることがあった)
- ML Kitのモデル更新ポリシーや2025年以降の仕様変更(古い情報に基づいた提案があった)
後者については、Antigravityに「最新の公式ドキュメントを参照した上で回答してほしい」と伝えると、その後の提案が慎重になりました。AIツールは使い方次第で、得られる回答の質が変わります。
個人開発での使いどころ
2014年から個人でアプリを作り続けて感じるのは、既製モデルを使える選択肢が増えたことの意義の大きさです。カスタムモデルを訓練する時間がない個人開発者にとって、精度が十分な既製モデルは実用的な選択肢です。
ただし注意したいのは、Firebase依存を増やしすぎるとコスト管理が複雑になる点です。ML Kitのオンデバイス機能は無料ですが、Cloud APIと組み合わせると課金が発生します。個人開発の初期フェーズでは、まずオンデバイス機能だけで実現できる範囲から始めることをお勧めします。
まずGoogle公式のML Kitサンプルリポジトリを手元で動かしてから、Antigravityと一緒に自分のアプリに移植するのがスムーズでした。Antigravityはサンプルコードを読み込んで「このプロジェクトの構造に合わせて書き換えると」と提案してくれるので、既製コードの移植作業と相性が良いです。
関連記事として、Core MLとTFLiteを使ったオンデバイスAI実装や、Xcode × AntigravityでiOS開発を加速する方法も参考になると思います。