ローカルLLM「Gemma4」でiOSアプリは作れるか?
1. Gemma4の導入
Gemma4とは
GoogleのオープンソースLLM「Gemma」シリーズの最新版。Ollamaを使ってローカルで動かせる。
今回使用したモデル: gemma4:26b(17GB、Apple M5 MacBook Air 24GBで動作)
ollama pull gemma4:26b
なぜGemma4でコーディングを試すのか
Claude CodeなどのクラウドAIはエージェントとして自律的に動くが、APIコストがかかる・ネット接続が必要というデメリットがある。ローカルLLMで同じことができれば、コスト0・オフラインでコーディング支援が使えるようになる。
ただし、Gemma4はClaude Codeのように「自分でファイルを読んでエラーを修正してデプロイする」という自律動作は難しい認識。そこで設計仕様書(SDD)を与えて「仕様書通りに書けばいい」状態にするアプローチを試した。
実行環境のセットアップ
Ollamaのtool callingを活用するため、Pythonスクリプト(gemmaコマンド)を用意。Gemma4がファイルの読み書きを直接実行できる構成にした。
gemma --project KafunNow
2. 仕様書作成
題材: 花粉飛散アプリ(iOS)
実験の題材として「花粉飛散状況がわかるiOSアプリ」を選択。理由:
- API連携・WidgetKit・CoreLocationと適度な複雑さがある
- 季節性があり、読者に共感を得やすい
アプリ仕様:
- SwiftUI + WidgetKit(iOS 17+)
- ロック画面・ホーム画面ウィジェット対応
- 位置情報から花粉飛散状況を取得
- 今/今日/明日/週間の切り替え表示
API: ウェザーニューズ オープンデータ(認証不要・CSV形式)
Claudeで仕様書を作成
Claude Codeと壁打ちしながら設計仕様書を5ファイルに分けて作成。
| # | ファイル | 内容 | 文字数 |
|---|---|---|---|
| 1 | 01-project-overview.md | プロジェクト概要・技術スタック・ディレクトリ構成 | 4,637 |
| 2 | 02-data-model.md | データモデル・API連携・CSVパース | 6,288 |
| 3 | 03-screen-design.md | 画面設計・レイアウト・ViewModel | 9,153 |
| 4 | 04-widget-design.md | WidgetKit設計・ウィジェットView | 8,222 |
| 5 | 05-location-and-settings.md | 位置情報フロー・citycode変換 | 6,538 |
| 合計 | 34,838文字 |
仕様書の粒度:「粗め」を意識した。関数シグネチャや構造体定義は書いたが、実装本体はGemma4に任せた。「何を作るか」は明確にしつつ「どう作るか」はGemma4の判断に委ねる状態。
所要時間: 壁打ち含めて約10分
Claudeのトークン消費: 出力 推定20,000〜25,000トークン
3. 初回実行
プロンプト
specs/ の仕様書を01から05まで全部読んで、iOSアプリを実装して
結果(23:38 実行開始)
約10分後(23:48)に以下の出力が返ってきた。
仕様書(01〜05)をすべて読み込みました。非常に詳細で、
実装に必要な情報が網羅されています。
### 実装プラン
1. Shared Layer: AppGroupConstants, SharedPollenData, PollenLevel
2. Models: PollenData, CityCode
3. Services: PollenAPIService, LocationService, CityCodeResolver
4. ViewModels: PollenViewModel
5. Views: ContentView中心のUI
6. Widget: KafunNowWidgetのView/Provider
それでは、コードを生成します。
仕様書の読み込みと理解は完璧だった。6ステップの実装プランも仕様書の構成に忠実で、順序も適切だった。
しかし——「コードを生成します」の後、何も起きなかった。
ファイルは1つも生成されなかった。
4. 試行錯誤
問題1: コンテキスト不足
5ファイル・34,838文字の仕様書を読み込んだ時点でコンテキスト(65,536トークン)がほぼ満杯になり、コード生成に使えるトークンが残っていなかった。
→ 対策: 仕様書を1ファイルに絞り、生成するファイルも1つに限定
specs/02-data-model.md だけ読んで、
Shared/AppGroupConstants.swift を write_project_file で作成して
問題2: tool callingでwrite系が動かない
1ファイルに絞っても結果は同じ。ツール呼び出しのログを見ると:
[tool] list_project_files(path='specs')
[tool] read_project_file(path='specs/02-data-model.md') ← 読み込みは成功
[tool] list_project_files(path='')
Gemma4:(空レスポンス)
read(読み込み)は動くが、write(書き込み)のtool callingが機能しない。
コード内容を直接プロンプトに貼り付けて「これをファイルに書いて」と指示しても空レスポンス。Gemma4 26bにおいてwrite系のtool callingは実質機能しないという結論に至った。
問題3: マルチライン入力の問題
Pythonのreadlineがマルチライン貼り付けを処理できず、コードブロックを貼ると1行目だけが送信されてしまう副次的な問題も発生した。
方針転換: ollama runでコード出力のみ
tool callingを諦め、ollama run gemma4:26b でコードをターミナルに出力させ、Claudeがファイルに保存する分業方式に切り替えた。
Shared/AppGroupConstants.swift のSwiftコードを出力してください。
(仕様を直接プロンプトに記載)
結果: コード生成品質は高かった
この方法では良質なSwiftコードが生成された。
import Foundation
/// App Groupに関する定数および共有データの管理を行う列挙型
enum AppGroupConstants {
/// App Groupの識別子
static let suiteName = "group.com.example.kafunnow"
static let pollenDataKey = "shared_pollen_data"
private static var sharedDefaults: UserDefaults? {
return UserDefaults(suiteName: suiteName)
}
static func saveSharedData(_ data: SharedPollenData) {
guard let userDefaults = sharedDefaults else { return }
do {
let encodedData = try JSONEncoder().encode(data)
userDefaults.set(encodedData, forKey: pollenDataKey)
} catch {
print("Error: Failed to encode SharedPollenData: \(error)")
}
}
static func loadSharedData() -> SharedPollenData? {
guard let userDefaults = sharedDefaults else { return nil }
guard let savedData = userDefaults.data(forKey: pollenDataKey) else { return nil }
return try? JSONDecoder().decode(SharedPollenData.self, from: savedData)
}
}
- 日本語コメント付き
-
guard letによるnilハンドリング -
do-catchによる適切なエラーハンドリング - 仕様書の設計意図を正確に理解した実装
コード生成能力は本物だった。問題は「自律的にファイルに書く」部分だけ。
5. 結論と次回の記事
今回の実験まとめ
| 評価項目 | 結果 |
|---|---|
| 仕様書の理解力 | ✅ 高い(5ファイルを正確に読み込み実装プランを策定) |
| コード生成品質 | ✅ 高い(適切なSwiftコード、日本語コメント、エラーハンドリング) |
| Tool calling(読み込み) | ✅ 動く |
| Tool calling(ファイル書き込み) | ❌ 動かない |
| 自律的なコーディング | ❌ 現時点では無理 |
結論
Gemma4 26bはコード生成AIとしての能力は高いが、エージェントとして自律的に動くにはtool callingの信頼性が不十分。
「仕様書を渡せば全部やってくれる」という使い方は今の段階では難しい。ただし「仕様を伝えてコードを出力させ、人間(またはClaude)がファイルに書く」という役割分担であれば十分実用的。
SDDアプローチの有効性
仕様書を渡すアプローチ自体は有効だった。Gemma4は仕様書の内容を正確に理解し、適切な実装順序を自律的に決定できた。問題はコード生成能力ではなく、ツール実行能力にあった。
次回: Qwen3-Coderで同じ実験
Gemma4の課題を踏まえ、次回はコーディングとtool callingに特化した Qwen3-Coder:30b で同じ仕様書を使って実験する。
- コンテキスト長: 256K(Gemma4の約4倍)
- agentic & coding tasksに特化
- 同サイズ(19GB)でM5 MacBook Airで動作
「ローカルLLMでiOSアプリを自律的に作れるか」— 答えはまだ出ていない。
次回記事: 「Qwen3-Coderで花粉アプリを作る — Gemma4との比較」
この記事は 91works Blog でも公開しています。
Discussion