この記事は「7 年前に作ったきり放置している iOS アプリを最新 Xcode / Swift で動かし直してリリースしたい個人開発者」向け。自作蔵書管理アプリ「My Books」を Claude Code とペアで 6 日間モダナイズし、Xcode 26 / Swift 6 / iOS 26 対応で v3.0.0 として TestFlight 18 ビルド経由で App Store 公開(2026-05-02)するまでの全記録。コンパイル成功の後で踏んだ実機の罠も全部書く。
この記事は何の話か
- 2016 年初版・My Books(蔵書管理アプリ)の 7 年放置で塩漬けになった Swift / CocoaPods / Realm / 各種 SDK をまとめて現行スタックに置換し、2026-05-02 に App Store 公開した過程
- WSL2 で Claude Code、ビルドは別マシンの Mac に SSH 投げ、TestFlight 配信まで 1 コマンド化
- コンパイルが通ったあとの 実機 TestFlight クラッシュ連戦 の中身(DGCharts vtable・楽天 API 廃止・Google Books クォータ枯渇など)
- AI ペアプロで「人間がやるべきこと」と「AI に任せられること」がどう分かれたか
数字でみるモダナイズ
| 項目 | 値 |
|---|---|
| 作業期間 | 2026-04-25 〜 2026-05-01(6 日) |
| コミット数 | 約 60 |
| TestFlight ビルド数 | 18 |
| 追加したテスト | 20(AppUtilTests / BookTests / ItemEnumsTests / ViewUtilTests) |
| リリースバージョン | v3.0.0 / Build 18 |
| App Store 公開 | 2026-05-02 |
| 対象環境 | Xcode 26 / Swift 6 / iOS 17+ / 旧 master ブランチは凍結 |
全体の流れ
大きく 3 フェーズに分かれた。前半 6 日のうち 5 日がモダナイズ、半日が配信パイプ整備、残りが TestFlight 配信中の障害対応と App Store 申請。Build 18 がそのまま審査を通過して 2026-05-02 に公開された。
CocoaPods → SPM、Realm @Persisted、async/await、ViewController 分割、シークレット管理、テスト追加
WSL → SSH → Mac → archive → IPA → altool → TestFlight
Build 5 → 18、実機クラッシュ・API 障害を潰したあと Build 18 で審査提出 → 2026-05-02 に v3.0.0 公開
① モダナイズの中身
計画 → 削除 → API 移行 → Realm 刷新 → ViewController 分割 → シークレット整理 → テスト追加、の順。
- CocoaPods → SPM(Realm 20.0.4 / Firebase iOS SDK 11.15.0 / Google Mobile Ads 11.13.0 / DGCharts 5.1.0)
- Amazon PA-API 関連を全削除: 旧
RWMAmazonProductAdvertisingManager/HMAC.m/ Bridging Header / BarcodeCacher 補助アプリも一掃 - deprecated API 撲滅:
arc4random_uniform/statusBarOrientation/UILocalNotification/kCLLocationAccuracyKilometer/AVCaptureConnection.videoOrientationなど総 30 箇所超 - async/await 化:
ProductSearchをwithTaskGroupベースの並列に。Google Books / 楽天 / OpenBD の 3 プロバイダをasync throws統一。delegate プロトコル群を全廃 - Realm モダナイズ:
@objc dynamic→@Persisted、旧マイグレーション破棄してdeleteRealmIfMigrationNeeded = true+ 「データをリセットしました」アラート方式に切替 - ViewController 分割: 1135 行 → 471 行、4 つの extension(Camera / ProductSearch / Library / Speech)に責務分離
- シークレット管理:
Config/Secrets.template.xcconfigを commit、実値のSecrets.xcconfigは gitignore。Info.plist は$(VAR)経由で展開
② TestFlight 配信パイプ
WSL2 でコード編集 → GitHub push → 別マシンの Mac に SSH → archive → IPA → TestFlight アップロードを 1 コマンドで完了させたかった。最終的にスクリプト 3 本で運用している。
scripts/setup_build_keychain.sh # 1 度だけ実行、専用 build keychain に証明書を複製
scripts/archive.sh # Release 構成で xcodebuild archive を非対話実行
scripts/upload.sh # archive 再利用で IPA → xcrun altool で TestFlight
App Store Connect API Key(.p8)認証で altool に渡す。fastlane も併用しているがテスト・ビルド検証用で、配信本体は素の xcodebuild + altool。
③ TestFlight 連戦
ここが一番時間を食った。Build 5 → 18 で 1 日半。コンパイルが通ることと 実機 iOS 26 で動くことの間に、思った以上に距離がある。
Build 18 を App Store 提出に回し、Apple 審査は追加質問なしで通過。2026-05-02 に v3.0.0 として App Store 公開。バージョン番号と Build 番号は TestFlight 最終ビルドのまま据え置き。
個人的に印象に残った踏み込み 5 件
1. Xcode 26 のビルドサンドボックス
死んでいた scripts/update_storyboard_strings.sh(コメント 1 行のみ)と Run Script Phase が残っていた。Xcode 26 のビルドサンドボックスは 空 Run Script Phase でもパスが解決できないとエラーに昇格させる。コメントだけ残ったスクリプトでも sandbox がエラー判定するため、Run Script Phase ごと削除する必要があった。
2. 楽天ウェブサービスが 13 日後に廃止
書影が出ない症状を切り分けていく中で、楽天ブックス API が 2026-05-14 に旧 app.rakuten.co.jp を完全停止して新 openapi.rakuten.co.jp に移行する真っ最中であることが判明。残り 13 日。
- 必須パラメータが
applicationId+accessKeyに増加 - アプリケーションタイプが Webアプリケーション(許可ウェブサイト)か バックエンドサービス(許可IP)の 2 択
- モバイルアプリは Webアプリケーション一択。
Originヘッダ必須、無いと403 REQUEST_CONTEXT_BODY_HTTP_REFERRER_MISSING
新ドメインで Webアプリケーション型として再登録 → Secrets.xcconfig に新 ApplicationId / AccessKey / 許可ドメインを格納 → ProductSearchByRakuten.swift で Origin / Referer ヘッダを毎回付与する形に書き換え。楽天ウェブサービスを使っているアプリは早めの移行を強く勧める。
3. Google Books の匿名共有クォータ枯渇
無認証で Google Books API を叩くと 429 Quota Exceeded。エラー JSON の project_number を見ると自分のものではない。仕様を読み直すと、API キー無し呼び出しは世界中の anonymous caller が同じ匿名共有プロジェクトの 20M/day クォータを取り合うとのこと。常時枯渇。自分の GCP project で API キーを発行 → ?key= クエリで送る形に修正。
4. DGCharts vtable で 4 ビルド消費 → 自前 CoreGraphics 実装に置換
Build 5–8 が同じ症状で連続クラッシュ。ShelfViewController.setChart() 内の PieChartView.data = ... setter が EXC_BAD_INSTRUCTION (brk #1)。.ips を atos で symbol 解決すると、DGCharts の DGChartsDynamic.framework で 647 個の symbol が同じ stub に aliasされていた。Xcode 26 + Swift 6 + SPM の dead-strip が暴走している疑い。
静的→動的の切替も KVC 経由の objc_msgSend 逃げ道も効かず、最終的にチャート機能(3 スライス + 中央テキストのドーナツ図)を BookStatusPieView: UIView(約 60 行)として CoreGraphics で自前実装に置換した。Storyboard の customClass を差し替えて完了。
5. SSH 経由の codesign と TestFlight 配信パイプ
Mac に SSH して xcodebuild archive しても codesign が login keychain にアクセスできず失敗する問題(login keychain は SSH で unlock しても codesign から触れない仕組み)と、専用 build keychain で ACL を緩和してパイプを 1 コマンド化した話は分量が多くなったので別記事に切り出した。WSL2 → SSH → Mac → archive → IPA → altool までスクリプト 3 本で組む全手順は 「WSL2 から SSH で Mac の Xcode を archive して TestFlight に上げる最短手順」を参照。
AI ペアプロの分担
6 日通して回した結果、人間と AI がそれぞれ得意な領域がはっきりした。
| 担当 | タスク |
|---|---|
| Claude Code |
crash log(.ips)の atos symbol 解決 / curl での API 切り分け / コード修正 /
SSH 経由の archive・upload / docs と memory の同期更新 / コミットメッセージ生成
|
| 人間 | 実機操作 / App Store Connect の各種同意・申請 / GCP / 楽天 Developers のブラウザ操作 / 物理的な耳と目(音量・スピーカー挙動・バナー表示の確認) |
AI に向かないタスクを無理に押しつけない切り分けが効率に直結した。逆に crash log の symbol 解決と API の curl 切り分けは AI が極端に速い。
学び
- コンパイル成功 ≠ 動作: 7 年ぶんの API 変更を Xcode が静的に拾える範囲には限界がある。実機 TestFlight に流して初めて出る不具合(DGCharts vtable / Storyboard の幽霊 outlet / ATS / ATT / AVAudioSession)が大半
- サードパーティ API は独立進化する: Google Books の匿名共有クォータ枯渇 / 楽天の旧 API 13 日後廃止 / AdMob SDK 11+ の
adSize必須化、いずれもアプリのコードと無関係に進んでいた変更 - テストパイプ整備が前提条件: WSL → SSH → Mac → TestFlight の 1 コマンド化が無ければ Build 1 → 18 を 1 日で回せなかった
- Xcode 26 の罠:
ENABLE_DEBUG_DYLIB/ sandbox / phantom scheme / SPM Embed Frameworks。test target 立ち上げと配信パイプ構築の双方で踏む - 「実機で 1 日触って何も起きない」までやって完了: コード変更は前段、TestFlight crash 対応が時間的にはほぼ同等の工数になる
※本記事の作業は執筆時点(2026 年 5 月)の Xcode 26 / Swift 6 / iOS 17–26 で行った。サードパーティ API の仕様変更や SDK のバージョンによってはそのまま再現しない箇所があります。気づきがあれば コメント欄でお知らせください。
まとめ
7 年放置していた個人 iOS アプリでも、Xcode 最新版と Claude Code を組み合わせれば 6 日で v3.0.0 を App Store まで届けられる。ただし「コンパイルが通る」と「実機で動く」は別物で、TestFlight に流してからの障害対応が想像以上に時間を食う。サードパーティ API の独立進化と Xcode 26 のビルドサンドボックスは、コードに触らなくても勝手に効いてくる。配信パイプを早めに整えておくとそこから速く回せる。
App Store: My Books(蔵書管理アプリ)(v3.0.0 / 2026-05-02 公開)。役に立ったら X(Twitter)でシェアしてもらえると喜びます。
関連記事
参考
- Xcode Build System(sandbox の挙動)
- Realm Swift Documentation
- 楽天ウェブサービス API リファレンス(新ドメイン)
- Google Books API
- OpenBD
- カーリル図書館 API
※この記事は Claude Code を使った自動更新を試しています。
0 件のコメント:
コメントを投稿