App Store Connect API の自動化で「成功したのに何も起きていない」3箇所
altool のアップロードが成功してもビルドが通ったとは限らない。審査提出が成功しても初回の課金アイテムは審査に乗っていない。レポートが「未提供」に見えても実際は存在している。どれも API は 200 を返すので、成功を確認したつもりで先へ進んでしまう。
App Store Connect API でリリース周りを自動化すると、レスポンスは成功なのに実際には何も起きていない箇所がいくつかある。例外が飛ばないので、スクリプトは最後まで走りきって「完了」と出す。実際に踏んだ3つを残す。
1. altool のアップロード成功は、ビルドが通った意味ではない
事象
altool --upload-app がこう返る。
UPLOAD SUCCEEDED with no errors
それなのに、数十分たっても App Store Connect のビルド一覧にそのビルドが出てこない。API でビルド一覧を取っても無い。TestFlight にも現れない。
原因
アップロードの後に Apple 側の処理が走り、そこでバイナリが弾かれることがある。このときビルドレコードそのものが作られない。一覧にも API にも、最初から存在しなかったことになる。理由が届くのはメールだけで、ITMS-90xxx の形で来る。
区別を難しくしているのが、処理中のビルドが API から見えないことがある点だ。自分の環境では、成功したビルドも VALID になるまで一覧に出てこなかった。そうなると「まだ処理中」と「無音で拒否された」が API からは同じ状態に見える。ポーリングで待っても永久に来ない。
解決策
API では区別できないので、時間で切る。アップロードが成功してから1〜2時間たってもビルドが現れなければ、メールを見に行く。
自動化するなら、ポーリングにタイムアウトを入れて「タイムアウト=要メール確認」を出力させる。無限に待つ実装にすると、失敗が失敗として出てこない。
2. 初回の課金アイテムは、版と一緒に出さないと審査に乗らない
事象
買い切りの課金アイテムを用意し、審査提出(reviewSubmission)を API で作って提出した。レスポンスは成功。アプリの審査も通り、公開される。それなのに本番で購入が通らない。
原因
初回の課金アイテムは、アプリの新バージョンと一緒に審査される。審査提出に版の item だけを足して課金アイテムの item を足していないと、課金アイテムは審査に乗らない。アプリだけが通り、課金アイテムは未審査のまま残る。
自動化スクリプトでいちばん起きやすい形だ。版の追加は成功し、提出も成功し、審査も通る。動いているように見えて、肝心の一手だけが抜けている。
解決策
reviewSubmissionItems に、版と課金アイテムの両方を足してから提出する。提出前に、その submission に紐づく item を一覧して数を確認するのが確実だ。
その手前で MISSING_METADATA から抜けられない場合は、API で全項目を照合すると欠落が特定できる。自分の場合、表示名も価格も提供地域も埋まっていて、足りないのは審査用スクリーンショット1点だけだった。
このスクリーンショットにも罠がある。課金アイテムの審査用スクショは端末スクショの縦横比が要る。中身だけ切り出した正方形寄りのクロップは弾かれる。
IMAGE_INCORRECT_DIMENSIONS
assetDeliveryState: FAILED
標準サイズ(1242×2688 など)に合わせ、足りない縦は白余白で埋めれば通る。ここでもアップロードの HTTP 自体は成功して見えるので、投げっぱなしにせず assetDeliveryState を必ず読む。
なお、シミュレータで価格がドル表示になるのはストアフロントが米国だからで、価格スケジュール側の基準地域とは別の話だ。審査スクショの通貨表記は問題にならなかった。
3. レポートの「未提供」は、たいてい自分の照合ミス
事象
Analytics のレポートを自動取得するスクリプトが、あるレポートを数週間ずっと「未提供」と表示し続けた。実際にはそのレポートは存在していた。
原因
3つの別々の理由が、同じ「データが取れない」という顔で出てくる。
1つ目は照合ミスだ。レポート名を部分一致で installations and deletions(複数形)と照合していたが、実名は App Store Installation and Deletion Standard(単数形)だった。自分のコードが出す「未提供」を、API 側の状態だと思い込んでいた。
2つ目は、そもそも存在しないレポートを探していた場合だ。継続率(D30)を取ろうとしてカタログを156件すべて列挙したところ、retention や cohort に相当するレポートが1つも無かった。継続率は Web の App Analytics 画面にしか無く、API からは取れない。名前が似ている Retention Messaging は再エンゲージメント配信の話で別物だ。
3つ目は閾値だ。ボリュームが小さいと、ダウンロードやセッションの instance がゼロで返る。Apple のプライバシー閾値で、5ユーザー未満の行が落とされるからだ。これは setup の失敗ではないので、setup をやり直しても直らない。
解決策
取得対象を推測で書かない。一度カタログを全列挙して、実名で照合する。
# レポート名を推測せず、まず全部出す
curl -s -H "Authorization: Bearer $JWT" \
"https://api.appstoreconnect.apple.com/v1/analyticsReportRequests/$ID/reports" \
| jq -r '.data[].attributes.name' | sort
このエンドポイントはページングされる。既定の返却件数では足りないので、links.next を辿り切ってから照合する。1ページだけ見て「無い」と判断すると、今回と同じ穴に落ちる。
そして、自作コードが出す「未提供」「データなし」は、まず自分の照合ミスを疑う。API 側の状態だと決めつけると、今回のように数週間溶ける。
補足
分析レポートを前後比較に使うなら、あと2つ知っておくと誤読を減らせる。
データの反映は約2日遅れる。施策の直後に叩くと after 側が空になり、「効果ゼロ」と読んでしまう。取得スクリプトに遅延の注記を出させておくといい。
analyticsReportInstances/{id}/segments は散発的に HTTP 500 を返す。1インスタンスの失敗で集計全体を落とさず、日単位でスキップして続行する耐性が要る。
3つに共通しているのは、成功レスポンスが「やりたかったことが起きた」証拠になっていない点だ。アップロードの成功はビルドの成立を保証しないし、提出の成功は中身の網羅を保証しない。自動化するときは、投げた後に結果側の状態を1回読むステップを必ず挟むようにしている。これを入れてから、静かに抜ける事故は減った。