本文へスキップ
NextCode
開発記録

App Storeの審査提出を、ブラウザにログインせず公開APIだけで終えた。同じ日の昼は、3回ログインさせていた

App Storeの審査提出を、ブラウザにログインせず公開APIだけで終えた。同じ日の昼は、3回ログインさせていた

画面の赤い枠を読むために、昼はログインしていた。夜は、APIの返事の中に同じ項目名が入っていた。

2026年10月5日は、同じ日の中で、App Store Connect の作業のやり方を2つ試した日でした。昼は、Appleのアカウントに自分でログインしてもらう方式で、結果として3回頼んでしまった。夜は、ブラウザを一度も開かずに、審査の提出まで終わった。

対象は、iPhoneアプリ「ロト・ナンバーズ予測研究」です。夜のほうは、ビルド36の紐付けから審査の提出、TestFlightの配布まで、ログイン0回でした。この記事は、その夜にやったことと、できなかったことの記録です。

1昼に3回ログインさせた

昼にやっていたのは、配信する国を日本だけにすることと、App Privacy(集めるデータの申告)を公開すること。どちらも、画面を操作する方式でやりました。

ところが、この方式は途中で何度も切れます。ブラウザを作業のたびに起動し直すと、ログインが切れて401が返る。画面を出さない設定で動かすと、Appleが別のブラウザとして扱って、これも401。ログインできたかをURLで判断したら、一瞬だけ通った画面を拾って「成功」と誤判定したこともありました。

結果、3回頼みました。あとで言われたのは、「今日は全然必要なかったですよね」でした。

2夜は、409の返事を読んだだけ

19時20分ごろ、「申請して」と頼んで、審査の提出の箱に版を入れる処理を呼びました。返ってきたのは409。「審査用に追加できません」という意味の、よくあるエラーです。

昼は、このエラーの理由を知りたくて、画面の赤い枠を読むためにログインしていました。夜は、APIの返事の中身を丸ごと出してみただけです。errors[0].meta.associatedErrors に、足りない項目の名前が入っていました。contentRightsDeclaration、つまりコンテンツの権利の申告です。

画面を開く前に、まずここを読む。これが、いちばん効いた手順でした。

3「設定済み」は、空だった

記録には「コンテンツの権利は設定済み。第三者のコンテンツは不使用」と書いてありました。APIで読み直したら、空でした。保存されていなかった。

しかも、「不使用」という内容自体が、事実と違いました。アプリのコードを調べると、ロト6・ロト7・ミニロトの当せん番号は、他の人が運営しているサイトのCSVを、端末から直接取っていたんです。自前のサーバーが返していたのは、ナンバーズ3と4だけ。アプリ工場(AIにアプリを作らせる自作の仕組み)が作った時に選んだ取得先で、自分は知りませんでした。

そこで、取得先を自前のサーバーに差し替えました。コードは3行の変更で、CSVの列は同じだったので、読み込みの処理はそのままです。申告は「第三者のコンテンツあり」に改め、審査メモも書き換えました。「API の返事を読んでいなければ、この事実と違う申告のまま出していた」と思うと、少し怖い話です。

4提出までの流れ

夜にやったことを、APIの呼び出しの順に並べます。全部、公開されているAPIです。

2026年10月5日の夜にやったこと(すべて公開API)

やること呼び出し
今の状態を読むGET /v1/reviewSubmissions?filter[app]=…(アプリ名のあとに reviewSubmissions を付ける形は400になる)
版にビルドを紐付けるPATCH /v1/appStoreVersions/{id}/relationships/build → 204
審査メモを書き換えるPATCH /v1/appStoreReviewDetails/{id}
コンテンツの権利を申告するPATCH /v1/apps/{id}(申告の前に、アプリの取得先のURLを検索して決める)
提出の箱に版を入れるPOST /v1/reviewSubmissionItems → 201
◎提出する◎PATCH /v1/reviewSubmissions/{id} に submitted: true → 200
読み直す版の状態が WAITING_FOR_REVIEW になったのを確かめてから「提出しました」と書く

提出の記録を読むと、提出した時刻は19時40分14秒でした。最後の「読み直す」は省きません。200が返ってきたことを、「提出済み」と書く根拠にはしないと決めているからです。

TestFlightの社内配布も、同じやり方です。テスト内容を書いて、ビルドをテスターのグループに入れて、状態が IN_BETA_TESTING になるまで見る。アップロードしただけでは、テスターには届きません。

5道具は50行だった

呼び出しに使っているのは、asc-client.mjs という50行のファイルです。Apple が決めた形式の署名つきの入場券(JWT)を、Node の標準機能だけで作っています。有効期間は1200秒、つまり20分。外部のライブラリは足していません。

このファイルには、直した跡が2つあります。9月7日に、鍵のある場所が一時フォルダ(再起動で消える場所)を指していたのを、永続の場所へ直しました。9月14日には、タイ語の説明文が文字化けしていたのを直しています。返事を文字列に足していくと、1文字が複数バイトの文字がデータの境目で割れる。バイトのまま集めて、最後に1回だけ読むようにしました。

6公開APIに無いものが、7つある

全部がAPIでできるわけではありません。10月5日にAPIを叩いて、実際に確かめた限界です。

ブラウザ(画面)が要る7項目と、APIの返事

項目APIの返事要るとき
新しいアプリの登録POST /v1/apps が403新規アプリのときだけ
App Privacy関係する窓口が全部404新規のときと、集めるデータを変えたとき
✕配信国の変更✕PATCH が403(作成と読むだけ可)✕作ったあとで変えるとき
却下理由の本文Resolution Center の窓口が404却下されたとき
医療機器の申告404健康・医療のカテゴリで、EEA・UK・US に出すとき
初めての課金を版と一緒に審査へ公開APIでは使えない課金ありの初回だけ
Apple の契約更新への同意APIなしApple が契約を更新したとき

表の3行目は、特に罠でした。配信国は、最初の POST でしか決められません。一度作ると、あとから変えるのは画面だけ。日本だけのアプリは、最初の呼び出しで175か国中、日本の1国だけを available: true にして作る。最初に決め切れていないと、あとはログインして画面で直すしかありません。

ブラウザが要るときのルールも決めました。始める前に「何のために、何回」を1行で伝える。要る項目を全部まとめて、1回のログインで済ませる。パスワードは、チャットに書かせずに画面で打ってもらう。

7提出の箱は、5つまで

ほかに、別のアプリで引っかかったことを。提出の下書きの箱は、1つのアプリで同時に5つまでです。途中で失敗する処理を繰り返すと、空の箱が溜まる。5つになると、新しい箱を作る呼び出しが409になり、箱を消す呼び出しも403です。結局、空の箱を使い回す以外に道が無い。

8間違えたこと

この日、1つ間違った説明もしています。「却下の理由はAPIで読める」と伝えたのですが、実際は、却下されたこと自体はAPIで分かる(箱の状態が UNRESOLVED_ISSUES になる)一方で、理由の本文は画面にしかありません。「できること」を広げすぎて話してしまいました。

9その後の審査

10月15日に読み直したら、提出の箱の状態は COMPLETE で、アプリの版は READY_FOR_SALE(販売中)でした。10月5日の提出が通ったことになります。何日かかったかは、記録を取っていなかったので分かりません。

10正直、まだ分からないこと

この日は、すでに登録済みのアプリの提出でした。新規アプリの登録から提出までを、ログイン1回で通せるかは、まだ試していません。計算の上では、登録・App Privacy・必要なら課金を1回にまとめて、配信国は最初の呼び出しで決める、で足りるはずです。

外部テスターの追加も、APIでできる作りに見えますが、まだ実測していません。

次に新しいアプリを出すときに、ログインの回数を数えて書きます。ほかの開発の記録はブログに、作ったアプリは制作実績にあります。App Store の提出で詰まっている話があれば、お問い合わせから教えてください。

この記事を書いた人

NextCode(沖縄県宜野湾市)は、iOSアプリ・ホームページ/LP・NFCデジタル名刺・AI業務自動化を作っている開発スタジオです。ここで書いているのは、実際に作って公開したものと、その過程で詰まったことだけです。

お問い合わせフォームは24時間受け付けています。

この記事が役に立ったらシェアしてください

いま何に困っているかを伺うところから始めます。フォームは24時間受け付け、お返事は2営業日以内です。

無料で相談する