🉑

生成AIでネイティブiOSアプリをゼロから作ってリリースした体験談・知見

に公開

AIにTypeScript+Next.jsでWebアプリを作らせる、といった事例はよく見聞きするようになった一方で、ネイティブアプリを作った例は全然見ないなーと思ったので、試してみました。

作ったもの

カラオケで歌えるレパートリーのリストを作ったり、採点スコアや歌いやすいキーの設定などを記録しておける "Setori" というアプリを作りました。
https://apps.apple.com/us/app/setori-カラオケで歌う曲や点数をリストで管理/id6744849921

作るにあたって考えたこと

AIがあんまり知らなそうな技術にどのように立ち向かうかを見たい

Webアプリに比べてiOSアプリのコードはAIの学習範囲に相対的に少ないはずで、AIがどこまでいい感じにやってくれるのか知りたかったです。そこで、アプリのフレームワークの中でもネット上に情報が多くなくてAIがあまり学習していなそうなものを取り入れることにしました。具体的には:

  • SwiftData: 曲リストや採点情報の永続化に使用
  • MusicKit: 楽曲検索やメタデータの取得に使用

これらを使ってみることにしました。

AIとの役割分担ルール

自分とAIとでどのように役割を分けるのかについては、自分はまだ答えを持っていません。プロジェクトごとにいろいろルールを変えて試しています。今回のアプリ開発では、以下のようにしました。

  • 人間(私)はプロダクトオーナーとして、
    • 機能の要件定義をする
    • 実装されたものの動作確認をする
    • コードレビューをする
    • リリースにまつわる作業をする
    • コードは書かない、触らない
  • AIはソフトウェアエンジニアとして
    • プロダクトオーナーから与えられた要件定義から詳細な仕様に落とし込む
    • タスクをIssueにする
    • 仕様に従って実装する
    • リポジトリにコミット/プッシュする
    • PRを作る

使うAIとツール

今回のプロジェクトでは、Claude Desktop経由でMCP越しにClaude Codeを動かす構成を取りました。Claude CodeをMCPサーバーとして動かすと、3.7 SonnetがClaudeのサブスクリプションだけで定額で使えるという噂があって試したかったからです(実際、Claude Code側のAPIキーの残高は消費されませんでした)。

事前に準備したこと

Claude DesktopからClaude Code(MCP)を使えるよう設定する

詳しい記事があるのでそちらを参照ください。

https://zenn.dev/ktakayama/articles/6b429daa660a13

Claudeにプロジェクトを作る

チャットのやりとりを一箇所にまとめるためにまずClaude上でプロジェクトを作りました。

プロジェクト内のタスクの共通ルールをプロジェクトナレッジとして蓄積することができ便利です。例えば、iOSアプリをビルドするためのコマンドを指示しておいたり(何も指示しないと $ swift build とかやり始めます)。

他にも、gitのブランチ戦略についての説明や、issueやPRを立てるためのコマンドをまとめたりしてあります。

GitHub Appを作ってGitHub上にClaude用の人格を用意する

gh コマンドを使ってissueやPRを作るように指示すればそのようにやってくれますが、何も設定しないと人間である自分のアカウントでissueやPRが作られてしまって微妙にテンションあがらないので、自分とは別の人格であるということを強調するためにbotアカウントとしてissueやPRを操作してもらうようにしました。

詳細は割愛しますが、GitHub Appを1つ作って、ClaudeがbotとしてissueやPRを作れるようにしました。

botのアイコンはうちの愛猫の写真を元にChatGPTに描かせたイラストです。

実際のタスクの進め方

1つのチャットで長くやり取りしたり読み込むソースコードが増えたりすると、最初の方のやり取りを忘れたり間違えたりと精度が下がっていきます。それを防ぐために、機能開発をいくつかのステップにわけ、いらんことを適度に忘れさせながら進めます。

ざっくり考えたネタを元に具体的な要件定義に落とし込んでもらう

Claudeの新しいチャットに、これから作りたい機能について箇条書きで簡単に説明します。同時に、以下の内容も添えるようにしています。

  • 「要件定義を手伝ってください」
    • これを言わないと勝手に実装を始めることがありました。
  • 「現状の関連するコードを読んで理解してください」
  • 「曖昧な点や矛盾を見つけたらその都度指摘してください」

そして、チャットのやり取りを通じて要件定義を磨いていきます。

要件が煮詰まってこれで良いと思えたら、Issueを立てるように指示します。こんな感じで立ててくれます。議論した内容がよく整理されていて人間にも読みやすいです。

つくったIssueを元に作業するよう指示する

Claudeの新しいチャットを開き、IssueのURLを渡して、「ghコマンドで内容を見て取り組んで」と指示します。MCP越しにClaude Codeを使ってコーディングを始めますが、Clineようにエディタがガチャガチャ動いて目視できるようなものはないので、大人しく完了を待ちます(ここでは私はコードをいじらないプロダクトオーナーですし)。

もし必要を感じたらユニットテストを書かせるようにしています。今回は網羅的にテストを用意することはそこまで意識しませんでした。

PRを作らせる

ビルドも通って動作に問題がなければPRを作らせます。AIが書いたコードを人間が責任持ってチェックするポイントとしてここでコードレビューをします。テストが通ればマージして終わりです。

やってみての所感・知見

チャットを細かく分けるのは有効

要件定義フェーズと実装フェーズにわけ、その境界は要件を整理したIssueにする、というのはかなりよかったと感じます。
試しに1つのチャットで最初から最後までやってみたタスクもあったのですが、要件定義のときに「例えばこんな感じにするとどうなる?」みたいな小さい発言のつもりだったやつを後々の実装のときに思い出して勝手に汲み取って余計なことをしたり、最終的に完成した要件では削られてやらなくていいとなった部分も実装しようとしたりして、より目が離せなくなった印象です。

Issueを指定して取り組ませるときも、大規模な機能追加においてはタスクが長くなってきて同じことをぐるぐる繰り返し始めたりするので、進捗をIssueにコメントさせて、新しいチャットで続きをやらせる、みたいなこともしました。

gitの操作をどこまで任せるのか悩ましい

「意味のある変更をするごとにコミットしてください」みたいなプロジェクトナレッジを書いておいたので、結構細かくコミットしてくれた印象です。「さっき動いてたものにちょっと変更させたらめちゃくちゃ壊れた」みたいなことは結構あるので、チェックポイントとしてのコミットは細かく勝手にやっておいてもらう、で今のところいいかなーと思っています。「さっきの変更する前の状態に戻して」と指示すればやってくれるような気もしますが、構成によってはお金かかるし、上述の通りチャットを細かく分ける戦略を取ったので「さっきの変更する前」を知らない可能性もあり、作業とセットでgitに残しておいてもらうのはある程度妥当かなと思いました。

一方でブランチ操作については人間がやったほうが良いかも、と思っています。チャットを細かく分ける都合上、前のタスクで作ったブランチのことは知らないし、今いるブランチが続きの作業をやるのに適した場なのかの判断はタスクをまたいだ記憶を持っている人間がやるしかなく、作業をしてよいブランチに切り替えてから、AIに指示をだす、という順番を徹底しました。ちなみに、「mainブランチ上で作業してはならない」「今いるブランチ名から明らかに間違ったブランチにいると思ったら人間に尋ねなさい」という指示は出していて、これは引き続きあるといいなと思いました。

マイナーなフレームワークに立ち向かうのは難しい

例えば、SwiftDataは ModelContext というコンテキストオブジェクトを経由してデータを読み書きしますが、同じコンテキストから得たオブジェクト同士でないとリレーションを貼る処理を呼べない、みたいな制約があって実行時にクラッシュするのですが、それをAIはあんまり理解していませんでした。あちこちで局所的にコンテキストを作って、オブジェクト同士をこねこねしようとしてクラッシュ、ということが頻繁にありました。また、UIテストでもない限り、アプリの実行時のクラッシュについてClaude Desktopが詳細を勝手に汲み取ってくれることはないので、クラッシュまでの背景を説明したり、ログをコピペしたりする必要がありました。ClineはWebブラウザを勝手に操作して動作確認してくれるので、それと比べるとまだ不便だと感じました。Xcodeと深く統合したコーディングエージェントが現れるまでこの状況は変わらなそう。

MusicKitについても同じ感じです。フレームワークとしてのMusicKitとは別に、Webに公開されたApple Music APIが存在して、それの仕様と混同しているかのような振る舞いがありました。Web APIにあるけどMusicKitにないプロパティを参照しようとしたり。

UIテストを書くのはとにかく下手

これは使っていたモデル(Claude 3.7 Sonnet)の特色かもしれません(ほかは比較できる材料ない)。

ある機能についてUIテストのテストケースを書くように指示したとき、コードで表現すべきUIの操作の順番がめちゃくちゃで、Viewの実装から必要な操作を推測する能力はまだ低いと感じました。ボタンAを押したら現れるボタンBを押す、というところをいきなりボタンBを押そうとしてfailする、みたいなテストケースを量産されて、めっちゃ手で直しました。

https://x.com/hiragram/status/1915068551054180837

Claude Desktopについて

どうせ自分がコードを読んだりビルドしたりするのはXcode上での作業になるので、Clineのようにエディタが一緒になっている必要はないだろうというのと、Claude 3.7 Sonnetが定額で使えるという魅力から今回の構成を選びましたが、結論から言うとClaude Desktopがあまり手に馴染みませんでした。その理由も説明します。

Auto approveがない

筆者がこれまでよく使ってきたClineにはAuto approveという機能があり、AIがユーザーの許可を求めてくるのを自動で承認するよう設定できます。Claude Desktopにはこれがなく、そのチャットで初めてする操作に対して必ずユーザーの許可を求めてきます(「このチャットの間はずっと許可する」みたいなオプションはある)。これが、Claude CodeがMCP越しに提供しているツールごとに必要になって、毎回チャットの序盤に何度も許可ボタンを押さなければならない。細かくチャットを分けるという今回私が取った戦略とは相性が悪く、結構体験が悪かったです。

bashを実行しようとして許可、ファイルを読もうとして許可、ファイルを編集しようとして許可、…みたいな感じ。

diffを見るのにエディタはあったほうがよかった

Claude Desktop越しにClaude Codeにコードを書かせると、Clineみたいにわかりやすくdiffを見せてくれないのが実は不便だった。gitも勝手にコミットしてねってことにしているのと相性が悪かったです。このプロジェクトにおけるAIが書いたコードのチェックはGitHubのPRでコードレビューで最後に1回やるって感じになってしまってたけど、Clineみたいに都度確認しながら進めたい状況もあるだろうから次はClineで良いかなってなりました。

まとめ

iOSアプリ開発を10年やってきた筆者が、あえて自分の手を動かさずにAIに全任せするとどうなるのかというのを試してみた感想でした。
開発を始めてから2週間、実働は多分8時間くらいで1つのアプリが完成してリリース可能な状態になりました。これはすごいことだと思います。

ツールの選定や開発フローの戦略については、正直これが最終形でベストだという答えは今後もずっと現れないような気がします。日々新しいものが出てくる界隈に食らいついて、その時に良さそうに見える選択ができる柔軟性を維持しないといけないと強く感じました。

生成AIを使ったネイティブiOSアプリの開発についていっぱい議論したいので、よかったらXで繋がりましょう。よろしくお願いします。

https://x.com/hiragram/status/1918566787677053420

Discussion