0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GPTを入口にしたiOSアプリ開発の記録 Ashiato Map 2.0と「YAMATO」

0
Posted at

「YAMATO」という仕組みを使い、iPhoneアプリ「Ashiato Map」のVersion 2.0を開発しました。現在はTestFlightで動作を確認できるところまで進んでいます。

Ashiato Mapは、私が公開している「記 -Shirushi-」のチェックイン機能とマップ機能を切り出したスピンオフです。Version 1.5では、Apple Watchから現在地をチェックインとして記録し、タイムラインや地図で確認できます。Version 1.5では、Apple Watchから現在地をチェックインとして記録し、タイムラインや地図で確認できます。

Version 2.0には、iPhoneからのチェックイン、月額330円のPremium、iCloud同期、マップ上でチェックイン履歴を再生するReplayを追加しました。今回変えたのは機能だけではありません。開発の入口をChatGPTに置き、そこから実装や検証へ仕事を渡す流れも試しています。

仕様の整理から実装、実機確認、TestFlightまで、開発中の記録はGitHubへ残しました。

この記事ではAshiato Map 2.0の開発を追いながら、「YAMATO」を使った半自動のアプリ開発がどこまで進んだのかを書いていきます。

複数のAIを使っていた頃の手作業

「YAMATO」を作る前から、アプリ開発にはChatGPT、Codex、Claude Codeを使っていました。

ChatGPTで仕様を考え、その内容をCodexへ渡します。別の確認が必要になればClaude Codeへ依頼し、結果はGitHubへ記録していました。エラーが出たときには内容をChatGPTへ持ち帰り、次の対応を考えます。

コードを書く作業の多くはAIへ渡せます。それでも、AIとAIの間をつなぐ仕事は私の側に残っていました。

前回どこまで進んだのかを確認する。使っているbranchとHEADを調べる。次に使うAIへ必要な情報を説明する。実装が終われば結果を記録し、別のAIへ渡す内容を組み立て直す。

一つのアプリだけを追っている間は、まだ把握できました。ところが複数のアプリを並行して開発するようになると、コードを書く作業とは別に、こうした確認や受け渡しが増えていきます。

そこで、仕事の記録をGitHubへ集める運用に変えました。

Issueには作業内容を残し、コード変更はPull Requestで追います。ChatGPTは相談と判断の窓口に置き、その先の実行を「YAMATO」へつなぐ構成にしました。

「YAMATO」が担当するのは、主に仕事の受け渡しと状態管理です。CodexやClaude Codeへ実装や検証を渡し、Mac上のXcodeで確認した結果をGitHubへ戻します。

人間がAIごとに仕事を運ぶ部分を、できるところから仕組みへ移す。その実仕事として選んだのが、Ashiato Map 2.0でした。

Ashiato Map 2.0で実際に進めた工程

Ashiato Map 2.0の開発では、既存のVersion 1.5を土台にしています。

最初に決めたのは、既存データを壊さないことでした。

Version 1.5では、チェックイン記録をローカルのJSONへ保存しています。Apple Watchから送られてくるデータやバックアップも、この形式を前提にしていました。

Version 2.0ではiCloud同期を追加しますが、既存のCheckInRecordcheckins.jsonはそのまま使う方針にしました。CloudKitを唯一の保存先にはせず、端末内のデータを基準にする構成です。

開発工程は10段階に分けました。

  1. 保存処理の安全性
  2. iPhoneチェックイン
  3. StoreKit 2によるPremium
  4. Cloud同期用の削除情報
  5. CloudKit同期
  6. Replayのモデルと再生処理
  7. ReplayのUI
  8. Premium対象機能の制御
  9. Settingsの整理
  10. 実機E2EとTestFlight

最初の2段階では、Apple Watchから受け取ったチェックインを保存したあとにACKを返す処理と、iPhoneから現在地を記録する処理を扱いました。

iPhone側の位置取得には、連打による多重処理を防ぐ仕組みを入れています。位置情報のキャッシュとタイムアウトも用意し、HomeとMapの両方から使う処理はPhoneCheckInServiceへまとめました。

そのあとに進めたのがStoreKit 2によるPremiumです。

Premiumは月額330円の1商品のみとし、対象機能をiCloud同期とReplayに絞りました。契約状態が変わっても、端末内のチェックイン記録は残します。

Premiumの判定には、検証済みのTransactionだけを使います。画面に表示する金額も固定文字列にはせず、StoreKitから取得したProduct.displayPriceを参照する構成にしました。

この工程ではCodexが実装を担当し、Mac上のXcodeでビルドまで確認しています。

その後もCloudKit、Replay、Premium制御、Settingsを工程ごとに分けて進めました。Version 2.0全体を一つの指示へ詰め込み、まとめて作らせる方法は採っていません。

各工程では、変更してよい範囲と触らない範囲を先に決めます。実装後にはローカルで確認し、結果をGitHubへ記録したうえで次へ進みました。

この進め方にした理由は、途中で問題が出たときに、どこまでが正常なのかを切り分けやすくするためです。

実際、Ashiato Map 2.0の開発では、アプリ側とは別に「YAMATO」側でも何度か処理が止まりました。

開発中に「YAMATO」側の問題も出た

Ashiato Mapは、「YAMATO」を実仕事へ使う最初のアプリとして開発を進めました。

最初の検証では、Ashiato Mapのリポジトリが「YAMATO」の実行対象へ登録されていませんでした。投入した仕事はinvalid_repoで拒否され、まず対象リポジトリを正式に登録する必要があると分かります。

オンボーディングを進める途中でも、read-onlyの仕事に指定したoperation_scopeが実行契約と合わず、一度停止しました。

さらにStoreKit 2の工程では、Codexによる実装とローカルビルドが終わったあと、GitHubへ変更を運ぶhost transportで問題が出ています。

最初に見つかったのは、transport planを置くパスの扱いでした。そこを直したあとには、実行中のleaseが失われて処理が止まるケースも発生します。

調べていくと、GitHubへの状態通知でHTTP 503やJSON以外の応答を受けた際に、監視処理まで終了する経路が見つかりました。

この問題は「YAMATO」側のruntimeで修正しています。修正後にはfocused/npm testsを実行し、701件が通りました。

つまり、Ashiato Mapの開発中にはアプリだけを作っていたわけではありません。「YAMATO」へ実仕事を流し、その途中で見つかった実行や受け渡しの問題も並行して直しています。

この記録を残しておいたことで、アプリ側の不具合と「YAMATO」側の停止を分けて追えるようになりました。

CloudKitではschemaを実機で確認した

Ashiato Map側でも、実機確認まで進めると問題が見つかりました。

iCloud同期のE2Eでは、Premiumの判定が通り、Settingsでも同期が有効になっています。画面にはReady to syncと表示されていました。

ところが最初のCloudKit読み込みで、次のエラーが出ます。

Field 'recordName' is not marked queryable

Ashiato Mapでは、CloudKit Private Databaseに保存したAshiatoCheckInCKQueryで取得します。

ソースとCloudKit側の設定を確認したところ、Development schemaのrecordNameにQUERYABLE indexがありませんでした。

ここではアプリ側へ回避処理を加えず、CloudKit Consoleからindexを追加しています。

その状態でもう一度実機確認を行い、新しいチェックインを作成しました。iCloud同期を有効にした状態で記録件数が増え、アプリを終了して起動し直したあともデータが残っています。

Development環境で確認したschemaは、その後ProductionへDeployしました。

実際の開発では、エラーが表示された場所と原因がある場所が一致するとは限りません。今回のケースでも、同期処理へ修正を入れる前にCloudKit側まで確認したことで、変更する場所を絞れました。

「YAMATO」で実装や検証を進める場合も、この切り分けは必要です。エラーを検出したこと自体を完了条件にはせず、原因を確認してから次の仕事を決めるようにしています。

ReplayはモデルとUIを分けた

CloudKitのあとには、Replayの実装へ進みました。

Replayは、保存済みのチェックインを時系列で再生する機能です。

最初に再生処理のモデルを作り、その後でMap上のUIを追加しています。対象期間はDay、Week、Monthの3種類です。

記録が0件なら空の状態を表示し、1件の場合は地点だけを確認できます。2件以上あるときに再生を有効にする仕様です。

操作にはPlayとPause、seek、0.5倍、1倍、2倍の速度変更を用意しました。

ここで扱っているのは、実際に取得したGPS軌跡ではありません。Ashiato Mapが保存しているのは、チェックインした地点と時刻です。

そのため地点間の移動は、MapKitから取得した推定ルートとして表示しています。ルートを取得できない場合や長距離の移動では、直線表示へ切り替えます。

モデルとUIを分けたことで、再生処理を先に確認してから画面側へ進められました。

実機E2Eでは保存済みの記録を使い、Day、Week、Monthの切り替えから再生、seek、速度変更、ルート表示まで確認しています。

機能確認を終えたあとには、Replayの操作部も見直しました。

最終版ではコントローラをタブバーの上へ移し、地図を広く使える配置に変更しています。この部分は、実機を見ながら私が判断しました。

人が判断した箇所とAIへ渡した作業

今回の開発では、人がコードを書く場面はかなり減りました。ただし、仕様や公開可否に関する判断は残っています。

たとえば月額料金を330円にすること、Premiumの対象をiCloud同期とReplayにすることは私が決めました。

既存データを維持する方針も同じです。

Replayの操作部をどこへ置くか、Settingsの項目をどう並べるかといったUIも、実機を確認しながら調整しています。CloudKit schemaをProductionへDeployする前には、Development環境での同期も確認しました。

一方、実装、ビルド、テスト、差分確認にはCodexやClaude Codeを使っています。

「YAMATO」は、その仕事を各AIへ渡し、進行状態と結果をGitHubへ残す役割です。

私が主に見るのは、何を作るのか、既存データをどう扱うのか、その状態で次へ進めてよいのかという部分になりました。

この分担は、最初から決まっていたものではありません。

Ashiato Mapの開発を実際に流したことで、人が判断した方がよい部分と、AIへ渡せる作業が工程ごとに見えてきました。

Version 2.0をTestFlightへ

Phase 10では、iPhoneとApple Watchを使って実機E2Eを行っています。

iPhoneからのチェックインはTimeline、Map、Analyticsへ反映されました。アプリを終了して起動し直したあとも、記録は保持されています。

Apple Watchからのチェックインも実機で確認済みです。iPhone側への保存と表示まで通り、重複した受信による異常も確認されませんでした。

ReplayではDay、Week、Monthの切り替えに加え、再生、seek、速度変更、ルート表示を確認しています。PremiumとiCloud同期についても、同じく実機で動作を見ました。

ここまで確認したあと、TestFlight向けのrelease preparationを「YAMATO」へ渡しています。

この工程ではVersion 2.0、Build 1、Automatic Signing、iCloud capability、Apple Watchアプリの組み込みなどを確認しました。

最後のArchiveでは、人の操作が一つ残っています。

Claude Codeを動かしていた非対話セッションからxcodebuild archiveを実行する段階で承認が必要となり、処理はapproval_neededで停止しました。

そこで、最終的なRelease ArchiveとApp Store ConnectへのuploadはMacのTerminalから実行しています。

対象HEADは次のものです。

1036c45165cae98968e8a3f971032f66acd4b27e

Versionは2.0、Buildは1でした。

Archiveの結果は次のとおりです。

** ARCHIVE SUCCEEDED **

続けてApp Store Connectへの送信も完了しています。

Upload succeeded.
Uploaded Ashiato
** EXPORT SUCCEEDED **

その後、Ashiato Map 2.0 (1)はTestFlightで確認できる状態になりました。

Version 2.0の開発を始めたのは2026年9月2日です。TestFlightへ到達したのは9月4日でした。

この2日間で、保存処理、iPhoneチェックイン、Premium、CloudKit、Replay、Settings、実機確認まで進めています。その途中では「YAMATO」側のtransportや監視処理にも問題が出ました。

すべてが自動で進んだわけではありません。

それでも、ChatGPTで仕様を相談するところから始まり、GitHubへ仕事を残し、CodexやClaude Codeへ実装や確認を渡しながらTestFlightまで進める流れは、実際のアプリ開発で一度通すことができました。

次のアプリでも同じ入口を使う

Ashiato Mapで確認したかったのは、「YAMATO」という仕組み単体の動作ではありません。

私がChatGPTで作りたいものを相談し、その内容を開発の仕事へ分ける。GitHubへ状態を残しながら、必要な実装や確認をCodexやClaude Codeへ渡す。途中で判断が必要になれば、人のところへ戻す。

この流れを実仕事で使えるかどうかが、今回の確認対象でした。

Ashiato Map 2.0では、TestFlightまで同じ流れを使っています。

Macでの承認や実機確認など、人が担当する工程は残りました。「YAMATO」側にも、今回の開発で見つかった改善箇所があります。

次は別のアプリでも、この開発フローを使う予定です。

アプリごとにCodexやClaude Codeへコンテキストを渡し直す作業を減らし、人間側の入口はChatGPTに置く。実装側の違いは、その先で扱う形を続けます。

この記事は、手書きのメモから始まった

この記事を書くときも、最初に長いプロンプトを用意したわけではありません。

ノートに書いたメモを写真に撮り、そのままChatGPTへ渡しました。まずメモに書いてある内容を読み取ってもらい、そこから何を記事にするのかを会話しながら決めています。

構成が固まったあとは、GitHubに残っているAshiato Mapと「YAMATO」の開発記録を確認しました。実際に起きたことや止まった箇所を拾い直し、それをもとにこの記事を書いています。

私はこのやり方をかなり気に入っています。

最初に人間の頭の中にあるものは、開発用の命令文でも記事の構成案でもありません。ノートに書いた数行のメモだったり、まだ整理できていない考えだったりします。

それをChatGPTへ渡し、会話しながら形を決め、その先の仕事へつないでいく。

Ashiato Map 2.0の開発でも、この記事を書く工程でも、やりかたは同じでした。

App Store

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?