個人開発アプリを App Store に出すまで —
iOS / macOS 対応の「うちのレシピ」を App Store でリリースしました。
かみさんが40年ぶんためこんだ紙のレシピ——雑誌の切り抜き、料理教室のプリント、手書きのメモ——を写真に撮って AI でデジタル化するアプリです。SwiftUI + SwiftData + CloudKit のマルチプラットフォーム構成で、実データは387件が入っています。
開発そのものより、リリース手続きのほうが消耗しました。TestFlight で無音の同期不具合に悩まされ、審査で一度リジェクトされ、スクリーンショットを作るために別のツールを自作する羽目になりました。同じところで詰まる人がいるはずなので、踏んだ地雷を順番に残しておきます。
1. TestFlight —— ここで一番大きな穴が空いていた
外部テスターは姪っ子2名。自分の端末では完璧に動いていたので、配布すればそのまま感想が返ってくるものと思っていました。
返ってきた報告は「サンプルレシピ1件しか入っていない」でした。
CloudKit のスキーマは Production に自動では行かない
30分待っても、アプリを入れ直しても同期しない。Apple ID は同じで、純正メモアプリは瞬時に同期している。アカウント側は正常です。
原因は CloudKit Dashboard の Production 環境にレコードタイプが存在しなかったことでした。Users だけがあって、CD_Recipe がない。
SwiftData と CloudKit を連携させると、モデルに対応するレコードタイプが自動生成されます。ただしそれは Development 環境にだけです。Xcode から直接ビルドした自分の端末は Development を見るので正常に動き、TestFlight ビルドは Production を見るので保存先が存在せず、エラーも出さずに黙って失敗していました。
対処は CloudKit Dashboard → Development → Deploy Schema Changes のみ。
覚えておくべき点は3つあります。
- TestFlight / App Store 配布の前に必ずデプロイする
@Modelを追加した回は毎回デプロイが要る(課金の台帳モデルを追加したときも同様でした)- デプロイは一方向。Production に入った定義は削除できない
症状が「片方の端末だけ空」「アカウントは正常」なら、まず Production のスキーマを疑う。 これが今回いちばん高くついた教訓です。
テスターの指摘は、作った本人には見えない
機能面のフィードバックも的確でした。たとえば「取り込み時に確認チェックを付け忘れると、後から直せない」。編集画面に材料・手順のチェックトグルを追加して解決しました。
自分は取り込みのフローを何百回も通っているので、間違えるという発想がなくなっています。この種の指摘は自分では絶対に出てきません。
使い方は、作った側の想定を超えてくる
もっと意外だったのは、何を便利だと感じるかが人によって違ったことです。
ひとりは「買い物中に材料が見られるのが便利」でした。こちらはキッチンで台の上に置いて使う画面として設計していたので、店の通路でスマホを見ている絵は想定になかった。言われてみれば、レシピが手元にある価値は調理中だけではありません。
もうひとりは、X(旧Twitter)のスクリーンショットを5枚ためこんでいた。SNSで流れてきたレシピを保存しても、あとで探せず埋もれる。それを取り込み先として使っていたわけです。紙のスクラップをデジタル化するアプリのつもりが、デジタルの断片を整理する用途で使われていました。
このふたつは、自分がいくら使い込んでも出てこなかった発見です。テスターは機能の不具合だけでなく、アプリの居場所を教えてくれます。
2. スクリーンショットのために、ツールを1本作った
申請作業でいちばん時間を持っていかれたのがスクリーンショットです。
App Store のスクショはサイズが厳密に決まっていて、6.9インチ iPhone と13インチ iPad は必須。小さいサイズで代用はできません。しかも、ただ画面を撮っただけでは商品ページとして成立しません。キャッチコピーを載せ、端末フレームに収め、背景を整える必要があります。
市販のスクショ生成サービスは月額です。1本のアプリのために契約するのは割に合わないし、次のアプリでも同じ作業が発生します。
そこで SlideShot という単一 HTML のツールを自作しました。
- Safari で開くだけ。インストールもアカウントも不要
- スクショをドラッグ&ドロップで配置
- 文言はその場で直接編集
- 端末フレーム、背景コラージュ、トーンパレット、レイアウト調整
- 複数デバイス分のプロジェクトを JSON で保存
- Safari のウィンドウ座標から
screencaptureコマンドを自動生成するので、規定ピクセルちょうどで書き出せる
ピクセルずれが起きないのがポイントです。手で切り出すと必ず1〜2px ずれて、App Store Connect に弾かれます。
BITriseOne の全アプリで使い回す前提で作ったので、次のアプリからはこの工程がゼロになります。市販ツールを使わなかった判断は正解でした。

ただし、失敗もしました
リリース後に気づいた反省点です。App Store のスクリーンショットは拡大できません。 手元の Mac で作っていると大きく見えるので油断しますが、実機の商品ページ、まして検索結果の一覧ではさらに縮みます。
文字が小さくて読めませんでした。手元で「大きすぎる」と感じるくらいが適正です。SlideShot 側にフォントサイズの下限を持たせる改修を予定しています。
3. 審査リジェクト —— Guideline 3.1.1
v1.3 を提出して、返ってきたのはリジェクトでした。
Guideline 3.1.1(In-App Purchase)違反。理由はプロモコード引き換え機能。
このアプリの課金は、初回10回無料、以降は変換100回チケット(消費型)と、自分の API キーを使うモードの解放(非消費型)の2本立てです。それとは別に、不具合やクレームの救済手段としてプロモコードを実装していました。Vercel + Supabase でサーバー検証まで組んだ、それなりの自信作です。
Apple の判断は明確でした。IAP 以外の手段でアプリの機能を解放する仕組みは、意図に関係なく不可。
「サポート用」「救済措置」という目的は考慮されません。その機能が存在すること自体が事由になります。
対処
引き換え画面と AI 設定画面からの導線を削除して再提出。それだけです。サーバー側の API とテーブルはアプリから呼ばなくなれば審査対象外なので、いったん残置しました(後日整理)。
無償で提供したい場合の正規ルートは2つあります。
- Offer Codes(App Store Connect から消費型 IAP に対して発行)
- TestFlight 配布
審査ノートは英語で、毎回書く
リジェクト歴があると、次から審査は丁寧に見られます。審査ノートには毎回英語で「購入なしで初回10回ぶん全機能を検証できる」と明記するようにしました。レビュアーが課金の壁で止まらないようにするためです。
なお、このときの審査環境は iPad Air 11-inch (M3) でした。
4. 再提出、そして macOS 版は「別アプリ」だった
プロモコードを削除して再提出。同じタイミングで macOS 版も初回提出しました。
ここで誤解していたことがあります。App Store Connect では iOS と macOS がプラットフォームごとに完全に分かれます。
- ビルド、スクリーンショット、説明文、キーワードはすべて独立
- 提出もそれぞれ個別、審査も別々に走る
- ただし IAP はアプリ単位なので共通。追加設定も再添付も不要
「同じアプリに2つ目のビルドとして入る」と思い込んでいて、実際の画面と食い違って混乱しました。
macOS 版の作り込みでは、シートのレイアウトが崩れる問題に .formStyle(.grouped) を当て、最小高さを調整して対処しています。Mac 対応は「ビルドターゲットを増やすだけ」では終わりません。ウィンドウサイズ、サンドボックス、ファイル書き出しの権限まわりで、それぞれ個別の戦いがありました。
そして iOS・macOS 両方が承認。ここまでで、初回提出から約1週間です。
5. v1.31 で踏んだ、地味だが致命的な2つ
コメントの編集・削除機能を足した v1.31 で、また2つ引っかかりました。
Build 番号はアプリ全体の通し番号
それまで「Version を上げたら Build を1にリセット」という運用をしていました。アップロードでこう弾かれます。
The value for key CFBundleVersion [1] must contain a higher versionthan that of the previously uploaded version [2]
App Store Connect は Version + Build の組ではなく、Build 単体の単調増加で判定します。バージョンを上げてもリセットしてはいけません。
Development Language が English のままだった
App Store の商品ページに「EN English」と表示されていました。App Store Connect 側は日本語で設定しているのに、です。
原因は Xcode プロジェクトの **Development Language が English(Xcode のデフォルト)**のまま放置されていたこと。
修正手順は、PROJECT(TARGET ではありません)→ Info → Localizations → 日本語を追加 → Set Default。
これはプロジェクト作成の初日にやっておくべき設定でした。リリース済みのアプリで直すと、反映は次の審査通過まで待つことになります。
6. ASO —— 最初のバージョンでやっておくべきだった
リリース後、サブタイトルが未設定であることを営業メールで指摘されました。内容は事実だったので、v1.31 で手を入れています。
- サブタイトルは30文字。アプリ名と同じくらい検索に効きます。詩的なコピーは検索に効かないので、世界観はスクショ1枚目に寄せ、ここは機能の説明に振りました
- キーワード欄は100文字。カンマの後にスペースを入れない(スペースも1文字としてカウントされます)
- アプリ名に含まれる語をキーワードに入れない。Apple は「名前+サブタイトル+キーワード」を結合して索引化するので、重複はただの無駄です
- 競合アプリ名を入れるのは規約違反
- キーワード欄は iOS 版・macOS 版で別管理。両方に入れる必要があります
ついでに書いておくと、ASO で拾えるのは「すでに探している人」だけです。需要そのものは作れません。そこはブログやメディアの仕事です。
無料の ASO レポートを送ってくる業者もいますが、App Store Connect の API キーだけは絶対に渡さないでください。 メタデータ・価格・リリース状態まで書き換えられるキーです。検索ボリュームを知りたいだけなら、Apple Search Ads のキーワードプランナーが無料で、しかも Apple 自身の実データです。
7. 申請前チェックリスト
今回の経験を、次に自分が使う形でまとめます。
配布前に必ず
- [ ] CloudKit スキーマを Production にデプロイ(
@Modelを追加した回は毎回) - [ ] Xcode の PROJECT → Info → Localizations で日本語を追加し Set Default
- [ ] Build 番号は通し番号。リセットしない
App Store Connect まわり
- [ ] 有料 App 契約、銀行口座、税務フォーム(W-8BEN)
- [ ] Small Business Program(手数料15%)の申請
- [ ] プライバシーポリシーのページを用意して URL 登録、アプリのプライバシー申告
- [ ] サポート URL / マーケティング URL(自サイトに固定ページを用意)
- [ ] 入力欄にチェックマークなどの記号を入れない(無効文字として弾かれます。説明文とテスト内容欄で2回やりました)
スクリーンショット
- [ ] 6.9インチ iPhone と13インチ iPad は必須。代用不可
- [ ] 文字は大きく。App Store では拡大できない
- [ ] 機能を削除したら、その UI が写ったスクショも差し替える(「コードを引き換える」ボタンが残ったまま公開していました)
課金
- [ ] IAP 以外で機能を解放する仕組みを入れない(Guideline 3.1.1)
- [ ] 審査ノートは英語。購入なしで検証できる手段を明記
- [ ] IAP 商品の審査は初回のみ。名称や説明を変更しなければ、そのまま次バージョンに紐づく
おわりに
アプリ本体の開発が9割で、リリースは残りの1割——という見積もりは完全に外れました。TestFlight から承認まで、審査の待ち時間を含めて3週間ほどかかっています。
ただ、詰まったところはほぼすべて設定と手続きで、コードの問題ではありませんでした。逆に言えば、一度覚えれば次のアプリでは繰り返さずに済むものばかりです。スクショツールを自作したのも同じ理屈で、2本目からは効いてきます。
「うちのレシピ」は App Store で公開中です。紙のレシピが家に積んである方に届けばと思います。

コメントを残す