というわけで、StoreKitの挙動について細かく検証してきましたが、少なくとも組み込みプロダクトモデル編については、ここから先はかなり細かい話になってきてしまうので一旦ここでまとめておきます。
なお、この情報は2011年11月現在のiOS 5.0におけるもので、将来的に仕様が変わっている可能性があります。また、公式ドキュメント等には記載されていない独自の検証に基づく見解が含まれます。参考程度に読んでください。そして気づいた点を躊躇せず指摘してください。
StoreKit 組み込みプロダクトモデルに関するポイント
1. オブザーバーをアプリケーション起動直後に登録する
組み込みプロダクトモデルにおいて、最も厄介なのは6. の購入手続きが同時に複数走ってしまう場合です。中断されたトランザクションをなるべく早く再開させるためにも、オブザーバーはアプリケーション起動直後に登録しましょう。公式ドキュメント(In App Purchase Programming Guide)でもそのするように指示されています。
2. アプリケーション側はどのタイミングで購入が完了しても大丈夫なようにしておく
中断されたトランザクションが再開された場合など、ユーザが「購入」アクションをとった直後以外でも購入手続きが完了することがあるので、アプリケーション側はいつ「購入完了」しても大丈夫なように作らなければいけません。
3. 必要に応じてブロック対応のラッパーを作るとよい
StoreKitのAPIは基本デリゲートを使う形のものが提供されています。が、これだと使い勝手が悪いので自前でブロック対応のラッパーを用意すると便利になります。ただし、トランザクション関連でアプリケーション終了によって中断、再起動後に再開されるようなものはブロックの形で作ろうとすると問題がおきるものもあるので、注意。
4. プロダクト情報は起動時と購入直前に取得しにいく
購入手続きをはじめる前には、プロダクト情報を取得しなければいけません。ただし、プロダクト情報の取得は最初の呼び出しにかなり時間がかかり、二回目以降早くなるという性質があるので、起動時に一回呼んでおいた上で購入前に再度呼ぶと良いでしょう。
5. トランザクションの開始と完了を記録しておく
6. の購入手続きの重複を避けるためには、アプリケーション側でも現在購入手続き中かどうかの情報を管理していく必要があります。オブザーバーで、開始と完了のタイミングで進行中のトランザクションの数をカウントアップ/カウントダウンしておけばよいでしょう。ただし、トランザクションは中断して次回起動時に再開されることがあるので、このカウントは永続化しておく必要があります。
6. 購入手続きが複数同時に走らないようにする
購入手続きが複数同時に進行すると、状態遷移が複雑になり色々と面倒な事がおきます。進行中/未完了のトランザクションがある場合は購入ボタンを押せないようにする等しておきましょう。
7. それでもまだまだ戦いは続く
と、ここまでやっても未完了のトランザクションがある状態でAppStoreからサインアウトした場合等、問題が発生してしまうパスがあります。タイムアウトを入れるなど、実際に組み込む場合はさらに対策をいれていく必要があります。また、サーバープロダクトモデルでは、さらに問題が発生するパスが多くなります…。
まとめ
というわけで、細かく見ていくと扱いが大変なStoreKit。残念ながら筆者自身もまだ完全な対策方法が見つかった状況ではないので、良いアイデアがあれば是非教えてもらって共有していければなと思っています。
ここまで詳しくまとめて下さって感謝します。