正直に書くと、私はアプリを「作る」のは好きなのに「出す」のが本当に苦手です。
コードを書いてテストが緑になるところまではいい。でもそのあとの、証明書だのプロビジョニングプロファイルだの審査メタデータだの課金商品の登録だの……あのへんで途端に手が止まる。過去には「あとはリリースだけ」の状態で半年放置したアプリもあります。
今回作ったのは「そいね」という寝落ち用の朗読アプリ(Swift 6 / SwiftUI / iOS 17+)。例によってコードとテストは終わっていて、例によって「あとはリリースだけ」で止まりかけていました。そこで今回は Claude Code(AIエージェント)に最後まで付き合ってもらうことに。やってみて思ったのは、AI駆動開発の話題は「実装をどう任せるか」ばかりだけど、本当にありがたかったのは実装より後の“出す作業”だったということです。
「コードを書く」より地味で面倒な工程
残っていたのは、App Store Connectへの登録とメタデータ、課金商品(月額480円/年額3,800円)の登録、プライバシーポリシーの公開、Archiveとアップロード、スクショ、そして審査提出。どれも難しくはない。ただ一つひとつが微妙に面倒で、手順を忘れがちで、ググり直すのがしんどい。この“面倒くささの総和”が、私がリリースを後回しにする原因でした。
結論から言うと、ここをAIと分担したのは大正解でした。
プライバシーポリシーは「別リポジトリで公開」が正解だった
審査にはプライバシーポリシーと利用規約のURLが要ります。でもアプリのソースは公開したくない。どこに置くかで少し悩みました。
Claudeに相談したら、「ソースは非公開のまま、法務ページだけ別のpublicリポジトリでGitHub Pages公開」という分け方を提案され、そのまま手を動かしてくれました。HTMLを書き、gh CLIでリポジトリを作り、Pagesを有効化するところまで一気に。
gh repo create soine-pages --public
# push 後、Pages を REST API で有効化
gh api repos/<ユーザー名>/soine-pages/pages --method POST --input - <<'EOF'
{"source":{"branch":"main","path":"/"}}
EOF
数分で https://<ユーザー名>.github.io/soine-pages/privacy.html が公開されました。当たり前の構成なんですが、一人だと「Notionでいいか…」と妥協しがちなところです。
署名で詰んだ夜と、その抜け道
一番きつかったのは署名でした。Archiveしようとすると、こう言われて止まる。
Communication with Apple failed: Your team has no devices from
which to generate a provisioning profile.
Communication with Apple failed に引っ張られて、最初は「Appleの障害?」「ログインし直す?」と見当違いの方向に行きかけました。過去の自分なら、ここで確実に心が折れて放置コースです。
Claudeはまず、感情を挟まず現状をコマンドで並べました。キーチェーンを見るとDevelopment証明書しかなくDistributionが無い、プロファイルは0件。そして、GUIだと曖昧なエラーしか出ないからと、コマンドラインのArchiveで“本当のエラー文”を取りに行きました。
xcodebuild -project Soine.xcodeproj -scheme Soine \
-destination 'generic/platform=iOS' \
-archivePath /tmp/Soine.xcarchive \
-allowProvisioningUpdates archive
ここで腑に落ちました。Communication failed は飾りで、本当に言いたいのは次の一文 ―― 「チームに登録デバイスが1台もないから、プロファイルを作れない」。自動署名は、デバイスがゼロだとそもそもプロファイルを発行できないんですね。
ここは私の出番。手元のiPhoneを繋ぎ、UDIDをポータルに手動登録する。デバイスを1台足した瞬間、頑として通らなかったArchiveが、何事もなかったように成功しました。
** ARCHIVE SUCCEEDED **
正直、ガッツポーズしました。誤誘導されるエラー文に振り回されずに済んだのは、機械的な事実(証明書ゼロ・デバイスゼロ)を淡々と突きつけてもらえたから。一人だと感情でハマるところを、横で事実だけ整理してくれる相方の価値を一番感じた場面でした。
ついでに、輸出コンプライアンスの質問を毎回出さずに済むよう Info.plist に一行(ITSAppUsesNonExemptEncryption = false)足してもらいました。
スクショの自動化は、結局「正攻法を捨てた」
スクショを各画面ぶん撮りたくて、最初は「シミュレータを自動操作して撮ろう」としたんですが、これがことごとくダメでした。座標クリックもAppleScript経由のクリックもイベント送信も、全部macOSのアクセシビリティ権限で弾かれる。おまけにシミュレータのウィンドウがマルチモニタの画面外に飛んでしまい、私自身もマウスで触れない、という間抜けな状況に。
ここでClaudeの方針転換が効きました。「タップを自動化するから権限の壁に当たる。アプリ側に“この画面を直接開く”入口を作って simctl で撮ればいい」と。幸いルーター構造だったので、#if DEBUG で起動引数を見て遷移先を差し替えるだけで済みました。
xcrun simctl launch "iPhone 17 Pro Max" com.tkdr.Soine \
--debug-start-on-category mukashi
xcrun simctl io "iPhone 17 Pro Max" screenshot 02_category.png
課金画面だけはStoreKitに繋がっていないと価格が出ないので、--debug-mock-products でデバッグ時だけダミー価格を流し込みました(もちろん #if DEBUG の中だけで、本番ビルドには一切入りません)。結果、iPhoneと13インチiPadのスクショを、権限の壁を一度も越えずに揃えられました。UI自動化が権限で詰んだら、外から操作しようとするより、アプリの中に観測点を作るほうが早い ―― これは今後も使える教訓になりました。
審査メタデータは「次に何を埋めるか」を言ってくれるだけで助かる
サブスクの登録や審査提出は、ひたすらブラウザのフォームを埋める作業です。操作は人間がやるしかないんですが、「いま何が足りていないか」を都度リストにしてもらえるだけで、体感の負担がまるで違いました。
審査直前の必須項目 ―― 課金とバージョンの紐付け、データ収集の有無、輸出コンプライアンス、年齢区分。そして地味に見落としがちなのが、iPad対応にしていると13インチiPadのスクショまで必須になる罠です。一人なら確実に提出直前で足止めでした。
おまけ:出したあとに、声を丸ごと作り直した
審査に出してから自分のアプリを聴いて、「朗読がいかにも機械音声だな」と気になってしまいました。元はmacOSの say の声。寝落ちアプリなのに声が硬いのは致命的です。
そこでAzureのNeural TTSに全面的に切り替えました(詳細は別記事に)。落語は男性、それ以外は女性とカテゴリで声を振り分け、SSMLで速度を落として句読点に“間”を入れる。長い話はAPI上限に引っかかるので分割して ffmpeg で繋ぐ。おもしろかったのは、ここだけは**「どの声がいいか」が完全に人間の仕事**だったこと。Claudeが候補を量産し、私が耳で選ぶ。役割分担がいちばんはっきり出た場面でした。
やってみて分かった、AIと人間の境目
走り終えて思うのは、AIが得意なところと人間がやるしかないところの境目が、思っていたよりずっとくっきりしている、ということです。
| AIに任せて速かったこと | 自分でやるしかなかったこと |
|---|---|
| CLI操作・ログ解析・原因の切り分け | ブラウザ / Xcode のGUI操作 |
| HTMLやスクリプト、設定ファイルの生成 | Apple ID・デバイス登録などの本人確認 |
| 「次に何が足りないか」の整理 | 提出ボタンを押す最終判断 |
| スクショや音声の大量生成 | 声・トーンの良し悪しの評価 |
「コードはAIが書く時代」とよく言われますが、私にとって価値が大きかったのは、むしろ実装が終わったあとの、あの腰の重い“出す作業”でした。リリース直前で止まっているアプリが手元にある人ほど、一度AIと並走してみる価値はあると思います。少なくとも私は、今回ようやく一本、ちゃんと審査まで出せました。
続編として、署名エラーの詳しい話、sayからAzure TTSへの音声の作り直し、App Store提出の必須項目まとめ、あたりも書く予定です。
(使ったもの:Claude Code / Xcode・xcodebuild・simctl / gh CLI / Azure Neural TTS・ffmpeg)