シリーズ:iOS版『見開きPDFビューア』開発記録
この記事は、ChatGPTと壁打ちしながら作成しました。また、Gitの変更履歴・ソースコードの調査と、筆者が提供したApp Store Connectの履歴をもとにしています。
第7回では、サンプルPDFをアプリ内から開く仕組みと、公開ページの紹介素材を準備した経緯を取り上げました。今回は、無料・広告なしの方針で用意した任意の応援と、購入復元の実装、購入テストでつまずいた点を振り返ります。
10月1日の審査通知をきっかけに、購入後も使い続けるサポーターアイコンと商品設定の整合を見直しました。通知とCodexの作業ログを照合し、何を変更し、どこまで確かめられたのかをまとめます。
図1 開発者への任意の応援として用意したBronze・Silver・Gold。実際の商品画像と、購入後に利用できる対応アプリアイコンを並べました。PDF閲覧の主要機能は購入の有無で制限していません。
無料・広告なしのアプリに、任意の応援を用意する
このアプリは、無料かつ広告なしで使えることを目標に開発しました。ただし、App Storeでの公開にはApple Developer Programの年会費がかかります。ここでは年間12,980円を費用の目安として考えています。公式案内は年間99米ドルまたは現地通貨の同等額で、実際の金額は登録・更新時の表示を確認する必要があります。Appleの登録料の説明
そこで、無料・広告なしの方針を保ちながら、任意の開発者支援(チップ/投げ銭)を受けられるようにしてみることにしました。動画サイトで投げ銭を送るのと似たイメージです。ただし、今回は費用を回収できなくても構わない、という考えで進めています。
この時は、継続的な支払いではなく、1回ごとに購入する商品を試すことにしました。最初は繰り返し購入できる消耗型で登録しましたが、購入後に使い続けられるサポーターアイコンとの組み合わせについて、審査で指摘を受けました。今回、同じ応援商品を何度も購入してもらう必要はないと判断し、非消耗型の商品へ変更して、購入復元の仕組みを追加しました。
購入商品の画像と、復元方法を指摘された
10月1日の通知は、バージョン1.0.3(build 7)への指摘でした。主な内容は次の2点です。
| 指摘 | 通知の内容 | 対応 |
|---|---|---|
| Guideline 2.3.2 | Bronze/Silver/Goldの商品プロモーション画像に価格表記がある | 画像を価格表記のないものへ差し替え、商品名・説明からも価格表記を削除 |
| Guideline 3.1.1 | 消耗型として登録した商品に、非消耗型として機能する内容が含まれるのに復元手段がない | 非消耗型へ変更し、購入復元を追加 |
図2 実際に使用したBronze・Silverの画像素材による修正前後の比較と、購入復元への対応。
今回の商品では、購入後も使い続けるアイコンと、消耗型という商品設定、復元方法の整合を取る必要がありました。
通知には任意のユーザー登録による復元案もありましたが、筆者は非消耗型の商品とStoreKitによる復元を選びました。以下は当時の依頼と回答で、プロンプトは全文、回答は抜粋です。
筆者のプロンプト(全文)
- アイコンを非消耗型商品として販売する の案で進めるので、計画を立ててください。
Codexの回答(抜粋)
非消耗型IAPへの移行計画を作成しました。StoreKit 2の購入権利を唯一の正とし、起動時の自動復元と明示的な「購入を復元」の両方を実装します。アカウント登録や独自サーバーは不要です。
この計画では、既存の消耗型商品の種類を書き換えるのではなく、非消耗型の商品を新たに用意する方針が示されています。独自のアカウント登録は追加せず、Apple AccountとStoreKitで購入状態を扱う方法にしました。
購入状態をStoreKitから読み直す
アプリ内購入自体は8月16日のGit履歴にあります。その後、10月2日の購入復元に関する変更では、サポーターアイコンの購入状態を扱う実装と説明が更新されています。
10月20日に確認したコードでは、PDF閲覧の主要機能は購入の有無で制限せず、任意の応援として非消耗型の商品を用意しています。Bronze、Silver、Goldは代替アプリアイコンに対応します。10月11日には開発版(ブランチtesting)にKoyomiバッジも追加されています。
同じ時点の TipManager は、起動時に Transaction.currentEntitlements で検証済みの購入状態を調べます。明示的な復元操作では AppStore.sync() を呼び、その後で購入状態を読み直します。アプリ内の表示や保存したフラグだけを根拠にせず、StoreKitから権利を確認する実装にしました。
購入状態を読み直す処理にも、復元操作が失敗した場合の扱いがあります。restorePurchases()では、処理前の購入状態を保持してから同期し、失敗した場合には元の状態へ戻してエラーを表示します。また、復元中に同じ処理を重ねて開始しないためのフラグもあります。購入できることに加えて、再確認の途中で失敗したときの状態も扱う必要がありました。
図3 起動時の確認と、ユーザー操作による復元を分けた処理の流れ。購入状態の再確認と、復元失敗時の扱いを整理しました。
購入テストは、環境を切り替えながら確認した
購入機能のテストにも、いくらか苦労しました。同じ購入・復元ボタンでも、Xcode内のローカルテストと、Appleのサーバーを使うSandboxでは、設定や購入履歴の扱いが異なります。当時のチャット履歴に残っていたのは、この二つの環境を切り替えながら確認した過程でした。
Appleの公式資料でも、ローカルテストはApp Storeのサーバー接続なしで実行でき、SandboxはApp Storeの仕組みを利用しつつ実際の課金はしない環境として説明されています。Appleのテスト環境の説明
図4 ローカルテスト、Sandbox、本番環境の違い。履歴に残った取引作成・削除や購入履歴リセットの案内を整理しています。
10月2日早朝(日本時間)には、ローカル設定での購入とアイコン変更に成功した後、再インストール後の復元でつまずきました。
筆者のプロンプト(全文)
アプリを削除・再インストールした後にRestore Purchasesを実行すると、購入結果が復元されません。
Codexの回答(抜粋)
確認方法は次の2通りです。
- ローカルStoreKitで復元処理だけ確認する
- 再インストール後にアプリを起動
- Xcodeで Debug → StoreKit → Manage Transactions
- 対象アプリを選択
- 「+」からBronze/Silver/Goldの取引を作成
- アプリで
Restore Purchasesを実行- 購入済みアイコンが表示されることを確認
このローカルの手順は、取引を作成して復元処理を確認するものです。削除前の取引がそのまま戻ることを確認した試験とは区別します。回答では、もう一つの方法として、StoreKit ConfigurationをNoneにしてSandboxで購入・再インストール・復元を確認する手順も案内されました。ローカルの取引管理画面を使う手順は確認できた一方、Sandboxのサインインでは再びつまずき、最後に実機で「復元が成功しました」と報告しています。これはテスト環境での記録です。
購入履歴をリセットして、購入をやり直した記憶もあります。履歴には、Sandboxの購入履歴を消す案内と、10月11日のバッジ確認時に、XcodeのTransaction Managerから該当する取引を削除して再テストする案内が残っていました。どの環境で実際に履歴を削除したかまでは確認できませんでしたが、購入済みの状態を作るだけでなく、未購入の状態に戻して試し直すことも必要なテストでした。
購入できることと、購入状態を戻せること
任意の応援として始めた機能でしたが、購入後に使い続けるアイコンを用意したことで、商品種別と復元方法をそろえる必要がありました。再インストール後の復元でつまずいた経験から、購入済みと未購入の状態を行き来しながら試す必要性も学びました。
Codexには実装とテスト手順の相談をしましたが、案内された手順と、実際に成功を確認できた範囲は分けて記録しています。次回は、修正内容を審査担当者へ伝えた返信文と、並行して進めた契約・登録情報の手続きを取り上げます。
ソースコードはGitHubのtyamada/MihirakiPDFViewerで公開しています。本稿のコードの基準は、2026年10月20日時点のtestingブランチの91f0853です。



