📖

ローカル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