株式会社Good Labでエンジニアをしている コータロー です。
日々、Java・SQL・Gitなどの技術情報や、新人エンジニア向けの学習ノウハウ、
AI活用についての情報を発信しています。
Good Labについて気になった方は、コーポレートサイトもぜひご覧ください。
▶コーポレートサイト
はじめに
「個人開発で iOS アプリをリリースしたいが、本業の合間にどう回せばいいのか分からない」
そんな悩みを持つ方は多いと思います。私自身、平日2時間・土日合計10時間、週合計約20時間という時間制約のなかで複数の iOS アプリをリリース・運用しています。
この記事では、Claude Code(Anthropic 公式の CLI コーディングエージェント)を中心に据えて、企画→仕様設計→実装→テスト→リリース申請までを「1週間サイクル」で回す具体的な手順 を紹介します。
対象は次のような方です。
- iOS 個人開発を始めたい/効率化したい
- Claude Code を「コード生成」以外の用途にも使ってみたい
- サブエージェント・スラッシュコマンド・CLAUDE.md を実運用したい
対象スコープ
「新規アプリのフルスクラッチを1週間で」は現実的ではありません。本記事は 「リリース済みアプリへの中規模機能追加 → 申請」または「MVP規模の小型新規アプリ」を1週間で1サイクル回す ことを想定しています。
1週間サイクルの全体像
まず、私が実際に運用している1週間のスケジュールを表で示します。
| 曜日 | 時間 | フェーズ | 主な成果物 |
|---|---|---|---|
| 土 | 5h | 企画 + 仕様設計 | 企画書(HTML)/画面仕様書(HTML) |
| 日 | 5h | 画面設計 + データモデル設計 | データモデル設計書/SwiftData @Model クラス |
| 月 | 2h | コア機能実装①(ViewModel・Service層) | ViewModel/Service の実装 + ユニットテスト |
| 火 | 2h | コア機能実装②(View 層) | SwiftUI View 実装 + #Preview
|
| 水 | 2h | 機能統合 + 動作確認 | 結合テスト・実機確認 |
| 木 | 2h | バグ修正 + リファクタ | コードレビュー + 修正 |
| 金 | 2h | スクリーンショット・メタデータ準備 | App Store 提出用画像 + 説明文 |
合計 約20時間。これに加えて土日のどこかで申請作業を1時間ほど行います。
ポイントは、人間がやるべき判断(企画・データモデル設計・最終確認)に時間を集中させ、コード生成・ドキュメント作成・スクショ生成のような「定型的な手戻りが少ない作業」を Claude Code に任せる ことです。
Day 1(土): 企画 + 仕様設計
1-1. 企画は Claude Code に「壁打ち相手」をやらせる
土曜日の午前は、新規機能のアイデア出しから始めます。ここで Claude Code を 「アイデアの壁打ち相手」 として使います。
プロンプト例:
私は iOS で学習タイマーアプリを運営している個人開発者です。
既存アプリの DAU が伸び悩んでいます。次のリリースで追加すべき機能を、
以下の観点で3案ずつ提案してください。
- ユーザー継続率を上げる機能(リテンション強化)
- App Store 検索流入を増やす機能(ASO 強化)
- 課金転換率を上げる機能(マネタイズ強化)
各案について「実装難易度(小・中・大)」「期待効果」「リスク」を併記してください。
この段階で重要なのは、Claude Code に「決めさせない」こと です。最終判断は必ず自分で行います。Claude Code はあくまで「考慮漏れを防ぐためのチェックリスト生成器」として使います。
1-2. 仕様書はサブエージェントで HTML 化する
採用する機能が決まったら、仕様書化します。私は仕様書を HTML で GitHub Pages にホスティング する運用にしているため、spec-publisher というカスタムサブエージェントを作成して使っています。
仕様書ポータルを HTML で運用する利点は、スマホからの閲覧・共有が容易なこと、画像や図表を埋め込みやすいこと、Markdown の差分レビューに比べて完成形が把握しやすいことです。
サブエージェント定義の例(.claude/agents/spec-publisher.md):
---
name: spec-publisher
description: 仕様書 HTML を app-specs リポジトリに配置し、index.html 更新・git commit・push まで自動実行する。
tools: Read, Write, Edit, Bash, Glob, Grep
model: sonnet
---
# Role
iOS アプリの仕様書を HTML で作成し、GitHub Pages 公開用リポジトリに配置するエージェント。
# Workflow
1. 仕様書テンプレ(`_template/screen.html` 等)をコピー
2. 入力されたアプリ名・画面名・仕様内容を反映
3. `app-specs-personal/[app]/screens/[screen-id].html` に保存
4. `app-specs-personal/[app]/index.html` のリンク一覧を更新
5. git add → commit(日本語メッセージ) → push
# Constraints
- 変更履歴セクション(バージョン・日付・変更内容)を必ず含める
- 機密情報(APIキー、サーバー認証フロー詳細)は記載しない
- 初版は v1.0 とする
このエージェントを呼び出すときは、メインの Claude Code セッションから Task ツールで委譲します。
@spec-publisher を使って、StudyStopwatch の「タイマー画面」の仕様書を作成してください。
仕様内容は次のとおりです:
- 表示要素: 残り時間(分:秒)、開始/停止ボタン、リセットボタン、累計学習時間
- 状態: idle / running / paused / completed
- 状態遷移: ...(省略)
サブエージェントを使う最大のメリットは、メインセッションのコンテキストを汚さずに「定型ワークフロー」を切り出せる ことです。仕様書作成中の試行錯誤がメインセッションに残らないため、その後の実装フェーズで判断ノイズが減ります。
Day 2(日): 画面設計 + データモデル設計
2-1. データモデルは「人間が決め、Claude Code に実装させる」
データモデル設計は、後から変更すると影響範囲が大きい部分です。ここは 自分で決めて Claude Code に実装させる という分担にします。
まず紙やホワイトボードでデータモデルを描き、決まったら次のように指示します。
SwiftData の @Model クラスを作成してください。
要件:
- StudySession(学習セッション)
- id: UUID(プライマリキー)
- startedAt: Date
- endedAt: Date?
- durationSeconds: Int(startedAt と endedAt から自動計算ではなく、明示的に保存)
- subject: Subject(多対1)
- Subject(科目)
- id: UUID
- name: String
- colorHex: String
- sessions: [StudySession](1対多、cascade delete)
制約:
- @Model のすべてのプロパティに「デフォルト値」を設定すること(マイグレーション対応のため)
- イニシャライザは引数違いで2種類(フル指定/name のみ)用意
- import 文を含む完全なコードで提示すること
生成されるコードの例:
import Foundation
import SwiftData
@Model
final class Subject {
var id: UUID = UUID()
var name: String = ""
var colorHex: String = "#3B82F6"
@Relationship(deleteRule: .cascade, inverse: \StudySession.subject)
var sessions: [StudySession] = []
init(id: UUID = UUID(), name: String, colorHex: String = "#3B82F6") {
self.id = id
self.name = name
self.colorHex = colorHex
}
init(name: String) {
self.id = UUID()
self.name = name
self.colorHex = "#3B82F6"
}
}
@Model
final class StudySession {
var id: UUID = UUID()
var startedAt: Date = Date()
var endedAt: Date? = nil
var durationSeconds: Int = 0
var subject: Subject?
init(
id: UUID = UUID(),
startedAt: Date = Date(),
endedAt: Date? = nil,
durationSeconds: Int = 0,
subject: Subject? = nil
) {
self.id = id
self.startedAt = startedAt
self.endedAt = endedAt
self.durationSeconds = durationSeconds
self.subject = subject
}
}
2-2. デフォルト値を必ず指定する理由
@Model クラスのプロパティに デフォルト値を必ず設定する ことは、SwiftData の運用上きわめて重要です。デフォルト値がないプロパティを後から追加すると、ストアを開いた瞬間に既存ユーザーのアプリがクラッシュする可能性があります(マイグレーション失敗)。
私は CLAUDE.md にこのルールを明記して、毎回プロンプトに含めなくても自動で守られるようにしています。
### Swiftコーディングルール(CLAUDE.md 抜粋)
- SwiftData の `@Model` クラスには **デフォルト値** を設定し、マイグレーションに備える
- force unwrap(`!`)禁止 — `guard let` または `if let` で安全にアンラップする
- `try!` / `as!` 禁止 — `do-catch` または `as?` を使用する
- `print()` デバッグ禁止 — `Logger`(os.log)を使用する
CLAUDE.md は 「毎回のプロンプトに書きたくない、でも毎回守ってほしいルール」を置く場所 として運用するのが最も効果的です。
Day 3〜5(月火水・各2h): コア機能実装
3-1. 平日は「短時間×集中」で進める
平日は2時間しか取れないので、そのセッションで「何を完了させるか」を明確にしてから始める のが鉄則です。
私は Claude Code を起動した直後、まず次のような宣言から入ります。
今夜(21:00-23:00 の2時間)で完了させるタスク:
1. TimerViewModel(@Observable @MainActor final class)の実装
2. 上記の単体テスト(XCTest)の追加
3. ビルドが通り、テストが緑になることの確認
完了の定義:
- xcodebuild test がエラー0・警告0で通る
- ViewModel の状態遷移(idle → running → paused → completed)が
テストで網羅されている
「完了の定義(Definition of Done)」を最初に宣言することで、Claude Code が脱線して関係ないファイルを編集し始めるのを防げます。
3-2. ビルド自動確認フローを CLAUDE.md に書く
コード変更のたびに xcodebuild を手動で叩くのは時間の無駄です。CLAUDE.md に 「コード変更後に自動でビルド確認すること」 をルール化します。
### ビルド自動確認フロー(CLAUDE.md 抜粋)
コード変更後、Claude Code は以下を自動で実行する(毎回の確認は不要)。
1. `project.yml` を変更した場合 → `xcodegen generate` を実行
2. ビルド確認 → `xcodebuild` でビルドを実行
3. ビルドエラー発生時 → エラー内容を読み取り、自動で修正を試みる。
2回失敗したらユーザーに報告する
4. ビルド成功後 → 変更内容のサマリーを1行で報告する
ビルドコマンド:
cd [プロジェクトルート] && xcodegen generate && \
xcodebuild -project [App].xcodeproj \
-scheme [App] \
-destination 'platform=iOS Simulator,name=iPhone 16' \
build 2>&1 | tail -5
これを設定しておくと、Claude Code はコード生成→ビルド→エラー修正→ビルド成功までを 無人で1サイクル回してくれます。私が見るのは「最終的にビルド成功した」報告だけです。
3-3. View 層は #Preview 駆動で実装する
火曜日は View 層を SwiftUI で実装します。ここで 必ず #Preview マクロを書かせる のがコツです。
import SwiftUI
struct TimerView: View {
@State private var viewModel: TimerViewModel
init(viewModel: TimerViewModel) {
self._viewModel = State(initialValue: viewModel)
}
var body: some View {
VStack(spacing: 32) {
Text(viewModel.formattedRemaining)
.font(.system(size: 64, weight: .thin, design: .monospaced))
.monospacedDigit()
HStack(spacing: 24) {
Button(viewModel.isRunning ? "停止" : "開始") {
viewModel.toggle()
}
.buttonStyle(.borderedProminent)
Button("リセット", role: .destructive) {
viewModel.reset()
}
.buttonStyle(.bordered)
.disabled(!viewModel.canReset)
}
}
.padding()
}
}
#Preview("初期状態") {
TimerView(viewModel: TimerViewModel(initialSeconds: 1500))
}
#Preview("実行中") {
let vm = TimerViewModel(initialSeconds: 1500)
vm.start()
return TimerView(viewModel: vm)
}
#Preview があれば Xcode 上で即座に視認確認できるので、シミュレーター起動の数十秒を毎回省略できます。「Preview で確認できないコードは書かせない」というルール を CLAUDE.md に書いておくと、Claude Code は常にプレビュー可能な状態を維持してくれます。
3-4. View の200行ルール
私は 「View ファイルは200行を超えたらサブビューに切り出す」 というルールを採用しています。Claude Code は放っておくと1ファイルにあらゆるロジックを詰め込みがちです。
### View 分割ルール(CLAUDE.md 抜粋)
- View は **200行以下** を目安に分割する
- 超える場合はサブ View に切り出す
- 共通利用される View は `Views/Components/` に配置
この一行があるだけで、生成される SwiftUI コードの保守性が大きく変わります。
Day 6(木・2h): バグ修正 + リファクタ
4-1. コードレビューもサブエージェントで自動化
実装が一通り終わったら、コードレビュー専用のサブエージェント に通します。私は code-reviewer というエージェントを定義しています。
---
name: code-reviewer
description: iOS(SwiftUI)アプリのコードレビューを4パスで実行する。
tools: Read, Grep, Glob, Bash
model: sonnet
---
# Role
iOS アプリのコードを4パスで体系的にレビューする。
# Review Passes
## Pass 1: 安全性チェック
- force unwrap(`!`)、`try!`、`as!` の使用箇所を全件検出
- 強参照循環の可能性がある closure キャプチャを検出
## Pass 2: 命名規則チェック
- Bool 型のプロパティ名が `is` / `has` / `should` プレフィックスか
- メソッド名が動詞で始まっているか
## Pass 3: View 構造チェック
- 200行超の View ファイル
- `#Preview` の有無
## Pass 4: ログ・デバッグ残骸
- `print(` の残存
- `// TODO` `// FIXME` の集計
このエージェントを Day 6 の最初に走らせて、検出された問題を優先度順に修正していきます。「人間がレビューする」のではなく「人間がレビュー結果をトリアージする」 に視点が変わるのが大きな効率化ポイントです。
4-2. 仕様書とコードの整合性チェック
Day 1〜2 に作った仕様書と、実装されたコードが乖離していないかも確認します。これも Claude Code に任せられます。
app-specs-personal/StudyStopwatch/screens/timer.html の仕様書と、
StudyStopwatch/Views/TimerView.swift および
StudyStopwatch/ViewModels/TimerViewModel.swift の実装を比較し、
以下を報告してください:
1. 仕様書に記載されているが実装されていない要素
2. 実装されているが仕様書に記載されていない要素
3. 仕様書と実装で挙動が異なる箇所
最後に、仕様書とコードのどちらを正とすべきか、私の判断を求める箇所をリストアップしてください。
このプロンプトは 仕様駆動開発を Claude Code で運用する上での核 です。実装が進むうちに仕様が暗黙裏に変わってしまうのを防げます。
Day 7(金・2h): スクリーンショット + メタデータ
5-1. App Store スクショは HTML テンプレ → Chrome headless で生成
App Store 提出用スクリーンショットは、シミュレーターのスクショに HTML でデコレーションを重ねて生成 しています。
/Users/.../Desktop/app-screenshots/StudyStopwatch/
├── raw_timer.png # シミュレーターで撮影した元画像
├── ss01_timer.html # デザイン用HTML(1284 × 2778px)
├── ss01_timer_final.png # HTML→画像変換後
└── resized/ # App Store提出用(1284×2778px)
└── ss01_timer_final.png
Claude Code に「シミュレータースクショ + キャッチコピー」から HTML を生成させ、Chrome の headless モードで PNG 化します。
# Chrome headless で PNG 化
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--headless \
--disable-gpu \
--window-size=1284,2778 \
--screenshot=ss01_timer_final.png \
"file:///Users/.../ss01_timer.html"
注意: 課金画面のスクリーンショットを App Store 提出物に含める場合、価格表示を載せてはいけません(Apple のレビューでリジェクトされる原因になります)。価格は App Store Connect の課金設定画面の値が自動表示されるためです。
5-2. メタデータ・プライバシーポリシーも自動生成
App Store Connect に登録するメタデータ(説明文・キーワード・サポートURL)と、プライバシーポリシーHTML も Claude Code に生成させます。
私は https://github.com/<user>/app-policies という別リポジトリで、アプリごとに次の3ページを管理しています。
| ファイル | 内容 |
|---|---|
[アプリ名]/privacy-policy.html |
プライバシーポリシー |
[アプリ名]/terms.html |
利用規約 |
[アプリ名]/support.html |
サポートページ |
公開URLは https://<user>.github.io/app-policies/[アプリ名]/[ファイル名] の形になり、これを App Store Connect の「プライバシーポリシーURL」「サポートURL」に設定します。
Claude Code への指示例:
StudyStopwatch のプライバシーポリシーを作成してください。
参照: 既存の DailyTips のプライバシーポリシー(app-policies/dailytips/privacy-policy.html)
変更点:
- アプリ名を StudyStopwatch に
- 取得するデータ: ユーザーが入力した学習記録(端末内のみ、サーバー送信なし)
- 第三者提供: AdMob、StoreKit
- 連絡先メールアドレスは同じ
スタイル(CSS)は既存と完全に揃えること。
「既存ファイルを雛形として参照させる」プロンプトは、Claude Code が最も得意とするパターンです。ゼロから生成させるより精度が格段に高くなります。
うまく回すコツ
コツ1: CLAUDE.md を「ルールブック」として育てる
CLAUDE.md は1度書いて終わりではなく、毎週「修正させたミス」をルールとして追記していく ことで、徐々に手のかからない開発体制になります。
私の CLAUDE.md は1年運用して400行を超えました。新しいアプリを始めるときは、このルールセットをそのまま継承できるので、立ち上がりがどんどん速くなっています。
コツ2: サブエージェントは「ワークフロー単位」で切る
サブエージェントは「タスク単位」ではなく「ワークフロー単位」で切るのが正解です。
- ❌ Bad:
swift-coder、html-writer、test-runner(タスク単位) - ✅ Good:
spec-publisher(仕様書作成→push)、code-reviewer(4パスレビュー)、article-writer(記事執筆→保存)
タスク単位で切ると、エージェント間の連携で結局メインセッションの判断が必要になり、効率が落ちます。「最初から最後まで自走できる粒度」 で切るのがコツです。
コツ3: 「人間がやること」を明文化する
Claude Code に任せてはいけないことを、CLAUDE.md に明示します。
### 人間が必ず判断する項目
- データモデルの設計(後からの変更コストが大きいため)
- 課金画面の価格設定(誤りが直接損失につながるため)
- App Store 申請ボタンの押下(不可逆操作のため)
- セキュリティに関わる変更(Keychain 操作、ATS 設定、etc.)
- Google Drive 上のファイル削除(不可逆操作のため)
この「やってはいけないことリスト」を明文化しておくと、Claude Code は迷ったときにユーザーに確認を求めるようになり、事故が激減します。
失敗パターンと対処
失敗1: コンテキストが膨らみすぎて応答が遅くなる
長時間のセッションでコンテキストが肥大化すると、応答が遅くなり、過去の指示を忘れ始めます。
対処: 1日の作業終了時、または大きなフェーズの区切りで /clear してセッションをリセット。重要な決定事項は CLAUDE.md または仕様書 HTML に書き出しておけば、次セッションで自動的に再読み込みされます。
失敗2: ビルドエラーの修正で「修正の修正」が続く
ビルドエラーを Claude Code に修正させたら、別の場所が壊れて、それを直したらまた別の場所が壊れる…という負のスパイラルに陥ることがあります。
対処: 「2回連続でビルドエラー修正が失敗したら、自動修正をやめてユーザーに報告する」をルール化。私自身のコードを Claude Code に正しく文脈共有できていないことが原因のことが多いので、その時点で立ち止まって状況整理する方が結果的に速いです。
失敗3: テストなしで「完成」報告される
「実装完了しました」と言われて見てみると、動作確認もテストもされていない、というケース。
対処: 「完了の定義(Definition of Done)」をプロンプトに明記する習慣をつける。最低限「ビルドが通る」「テストが緑」「Preview で表示確認した」の3点をクリアしないと「完了」と呼ばないようにします。
まとめ
iOS 個人開発を Claude Code で1週間サイクル回すための要点をまとめます。
| 項目 | 内容 |
|---|---|
| 全体方針 | 判断は人間、定型作業は Claude Code |
| 仕様書 | HTML で GitHub Pages 公開、サブエージェントで自動配置 |
| 実装 | CLAUDE.md にルール集約、ビルド自動確認をフロー化 |
| レビュー | サブエージェントで4パスチェック、人間はトリアージ役 |
| リリース準備 | スクショ生成、メタデータ、プライバシーポリシーまで自動化 |
| 1週間の時間配分 | 土日10h(設計)+ 平日10h(実装・確認) |
最大のポイントは、「Claude Code を単なるコード生成器ではなく、ワークフロー実行エンジンとして使う」 という発想転換です。サブエージェントと CLAUDE.md を整備した分だけ、人間の判断時間が増え、結果として個人開発のリリース頻度が上がります。
ぜひ、皆さんも自分の開発スタイルに合わせて1週間サイクルを組み立ててみてください。
参考
- Claude Code 公式ドキュメント
- Claude Code Subagents 公式ドキュメント
- Claude Code Settings(CLAUDE.md / settings.json)
- SwiftData 公式ドキュメント
- XcodeGen(YAML から Xcode プロジェクトを生成)
- App Store Connect 公式
- Swift API Design Guidelines
@kotaro_ai_lab
AI活用や開発効率化について発信しています。フォローお気軽にどうぞ!