次の方法で共有


iOS 6 の StoreKit の変更点

iOS 6 では、StoreKit API に 2 つの変更が導入されました。これらはアプリ内から iTunes (および App Store/iBookstore) 製品を表示する機能と、Apple がダウンロード可能なファイルをホストする新しいアプリ内購入オプションです。 このドキュメントでは、Xamarin.iOS でこれらの機能を実装する方法について説明します。

iOS6 の StoreKit の主な変更点は、次の 2 つの新機能です。

  • アプリ内コンテンツの表示と購入 - ユーザーはアプリを離れることなく、アプリ、音楽、書籍、その他の iTunes コンテンツを購入してダウンロードできます。 独自のアプリにリンクして購入を促進し、レビューや評価を奨励することもできます。
  • アプリ内購入のホストされたコンテンツ - Apple はアプリ内購入製品に関連付けられているコンテンツを保存して配信します。これにより、ファイルをホストするための別のサーバーが不要になり、バックグラウンドでのダウンロードが自動的にサポートされ、記述するコードが少なくて済みます。

StoreKit API の詳細については、アプリ内購入ガイドを参照してください。

要件

このドキュメントで説明する StoreKit の機能には、Xamarin.iOS 6.0 と共に iOS 6 と Xcode 4.5 が必要です。

アプリ内コンテンツの表示と購入

iOS の新しいアプリ内購入機能を使用すると、ユーザーは製品情報を表示し、アプリ内から製品を購入またはダウンロードできます。 以前のアプリケーションでは、iTunes、App Store、または iBookstore をトリガーする必要がありました。その結果、ユーザーは元のアプリケーションから離れることになりました。 この新機能は、完了時にユーザーをアプリに自動的に戻します。

購入後にアプリに自動的に戻る

これを使用する方法の例を次に示します。

  • ユーザーにアプリの評価を促す - App Store ページを開いて、ユーザーがアプリを離れずに評価およびレビューできるようにします。
  • 相互販売促進アプリ - 公開した他のアプリをユーザーに表示し、すぐに購入またはダウンロードできるようにします。
  • ユーザーがコンテンツを見つけてダウンロードしやすくする - アプリが検索、管理、収集するコンテンツをユーザーが購入できるようにします (音楽関連のアプリは曲のプレイリストを提供し、各曲をアプリ内から購入できるようにするなど)。

SKStoreProductViewController が表示されたら、ユーザーは iTunes、App Store、または iBookstore にあるのと同様に製品情報を操作できます。 ユーザーは次のことができます。

  • スクリーンショットの表示 (アプリの場合)
  • サンプルの曲またはビデオ (音楽、テレビ番組、映画の場合)
  • レビューの読み取り (および書き込み)
  • 全面的にビュー コントローラーと StoreKit 内で発生する購入とダウンロード。

SKStoreProductViewController 内の一部のオプションでは、ユーザーは引き続きアプリを離れ、関連するストア アプリを開きます ([関連製品] やアプリの [サポート] リンクをクリックするなど)。

SKStoreProductViewController

任意のアプリ内で製品を表示する API は単純です。SKStoreProductViewController を作成して表示するだけで済みます。 製品を作成して表示するには、次の手順に従います。

  1. コンストラクターの StoreProductParameters など、ビュー コントローラーにパラメーターを渡す productId オブジェクトを作成します。
  2. SKProductViewController をインスタンス化します。 クラス レベルのフィールドに割り当てます。
  3. ビュー コントローラーのイベントにハンドラーを割り当てます。これには、ビュー コントローラー Finished を閉じる必要があります。 このイベントは、ユーザーがキャンセルを押したときに呼び出されます。それ以外の場合は、ビュー コントローラー内のトランザクションを終了処理します。
  4. LoadProduct と完了ハンドラーで渡す StoreProductParameters メソッドを呼び出します。 完了ハンドラーは、製品要求が正常に完了したことを確認し、成功した場合はモーダルで SKProductViewController を提示する必要があります。 製品を取得できない場合は、適切なエラー処理を追加する必要があります。

この記事の StoreKit サンプル コードの ProductView プロジェクトは、製品の Apple ID を受け入れ、Buy を表示する SKStoreProductViewController メソッドを実装します。 次のコードは、特定の Apple ID の製品情報を表示します。

void Buy (int productId)
{
    var spp = new StoreProductParameters(productId);
    var productViewController = new SKStoreProductViewController ();
    // must set the Finished handler before displaying the view controller
    productViewController.Finished += (sender, err) => {
        // Apple's docs says to use this method to close the view controller
        this.DismissModalViewControllerAnimated (true);
    };
    productViewController.LoadProduct (spp, (ok, err) => { // ASYNC !!!
        if (ok) {
            PresentModalViewController (productViewController, true);
        } else {
            Console.WriteLine (" failed ");
            if (err != null)
                Console.WriteLine (" with error " + err);
        }
    });
}

実行中のアプリは次のスクリーンショットのようになります。ダウンロードまたは購入は、全面的に SKStoreProductViewController 内で行われます。

実行中のアプリは次のようになります

以前のオペレーティング システムのサポート

このサンプル アプリケーションには、以前のバージョンの iOS で App Store、iTunes、または iBookstore を開く方法を示すコードが含まれています。 OpenUrl メソッドを使用して、適切に作成された itunes.com URL を開きます。

次に示すように、バージョン チェックを実装して、実行するコードを決定できます。

if (UIDevice.CurrentDevice.CheckSystemVersion (6,0)) {
    // do iOS6+ stuff, using SKStoreProductViewController as shown above
} else {
    // don't do stuff requiring iOS 6.0, use the old syntax
    // (which will take the user out of your app)
    var nsurl = new NSUrl("http://itunes.apple.com/us/app/angry-birds/id343200656?mt=8");
    UIApplication.SharedApplication.OpenUrl (nsurl);
}

エラー

使用する Apple ID が無効な場合、次のエラーが発生します。これは、ネットワークや認証といった種類の問題があることを示すため、混乱を招く可能性があります。

Error Domain=SKErrorDomain Code=5 "Cannot connect to iTunes Store"

Objective-C ドキュメントの読み取り

Apple の開発者ポータルで StoreKit について読んでいる開発者には、プロトコル (SKStoreProductViewControllerDelegate - この新しい機能に関する説明) が表示されます。 デリゲート プロトコルには、1 つのメソッド (productViewControllerDidFinish) しかありません。これは、Xamarin.iOS の FinishedSKStoreProductViewController イベントとして公開されています。

Apple ID の判断

SKStoreProductViewController 必要な Apple ID は数値です。"com.xamarin.mwc2012" のようなバンドル ID と混同しないでください。 次に示すように、表示する製品の Apple ID を確認する方法はいくつかあります。

iTunes Connect

公開するアプリケーションのために、iTunes Connect で Apple ID を簡単に見つけられます。

iTunes Connect で Apple ID を見つける

API の検索

Apple では、App Store、iTunes、および iBookstore のすべての製品に対してクエリを実行する動的検索 API を提供しています。 検索 API にアクセスする方法に関する情報は Apple のアフィリエイト リソースにありますが、API は、登録されたアフィリエイトだけでなく、誰にでも公開されます。 結果の JSON を解析して、trackId と一緒に使用する Apple ID である SKStoreProductViewController を検出できます。

この結果には、アプリで製品をレンダリングするために使用できる表示情報やアートワーク URL など、他のメタデータも含まれます。

次に例をいくつか示します。

エンタープライズ パートナー フィード

Apple は、承認されたパートナーに、ダウンロード可能なデータベース対応フラット ファイルの形式で、すべての製品の完全なデータ ダンプを提供します。 エンタープライズ パートナー フィードにアクセスする資格がある場合は、そのデータセット内の任意の製品の Apple ID を確認できます。

エンタープライズ パートナー フィードの多くのユーザーは、製品の販売時にコミッションを獲得できるアフィリエイト プログラムのメンバーです。 SKStoreProductViewController は、この執筆時点ではアフィリエイト ID をサポートしていません。

製品の Apple ID は、その iTunes プレビュー URL リンクから推論できます。 iTunes 製品リンク (アプリ、音楽、書籍用) で、URL の id から始まる部分を探し、その後の番号を使用します。

たとえば、iBooks への直接リンクは次のようになります。

http://itunes.apple.com/us/app/ibooks/id364709193?mt=8

Apple ID は 364709193 です。 MWC2012 アプリの場合も同様に、直接リンクは次のようになります。

http://itunes.apple.com/us/app/mwc-2012-unofficial/id496963922?mt=8

Apple ID は 496963922 です。

アプリ内購入のホストされたコンテンツ

アプリ内購入がダウンロード可能なコンテンツ (書籍、その他のメディア、ゲーム レベルのアートと構成、その他の大きなファイルなど) で構成されている場合、これらのファイルは Web サーバーでホストされていたために、購入後に安全にダウンロードするためのコードをアプリに組み込む必要がありました。 iOS 6 以降、Apple は自社サーバー上でファイルをホストし、開発者は別のサーバーを必要としません。 この機能は、非コンシューマブル製品 (コンシューマブル製品またはサブスクリプションではない) でのみ使用できます。 Apple のホスティング サービスを使用する利点は次のとおりです。

  • ホスティングと帯域幅のコストを節約します。
  • おそらく、現在使用中のサーバー ホストよりもスケーラブルです。
  • サーバー側の処理をビルドする必要がないため、記述するコードが少なくなります。
  • バックグラウンド ダウンロードが実装されています。

注意: iOS シミュレーターでホストされたアプリ内購入コンテンツのテストはサポートされていないため、実際のデバイスでテストする必要があります。

ホストされたコンテンツの基本

iOS 6 より前は、2 つの方法で製品を提供していました (Xamarin のアプリ内購入に関するドキュメントで詳述)。

  • 組み込み製品 - 購入によって "ロック解除" されているが、アプリケーションに組み込まれている機能 (コードまたは埋め込みリソースのいずれか)。 組み込み製品の例としては、ロック解除されたフォト フィルターやゲーム内のパワーアップなどがあります。
  • サーバー配信製品 - 購入後、アプリケーションは、開発者が運用するサーバーからコンテンツをダウンロードする必要があります。 このコンテンツは購入時にダウンロードされ、デバイスに保存され、製品の提供の一部としてレンダリングされます。 たとえば、書籍、雑誌の発刊、背景アートと構成ファイルで構成されるゲーム レベルなどがあります。

iOS 6 では、Apple はサーバー配信製品のバリエーションを提供しています。コンテンツ ファイルは Apple サーバー上でホストされます。 これにより、別のサーバーを運用する必要がないため、サーバー配信製品を簡単にビルドできます。また、StoreKit には、以前は自分で記述する必要があったバックグラウンド ダウンロード機能が用意されています。 Apple のホスティングを活用するには、新しいアプリ内購入製品のコンテンツ ホスティングを有効にし、それを利用するように StoreKit コードを変更します。 その後、Xcode を使用して製品コンテンツ ファイルがビルドされ、レビューとリリースのために Apple のサーバーにアップロードされます。

ビルドと配信のプロセス

App Store を使用して、ホストされたコンテンツ内でアプリ内購入を提供するには、次の設定と構成が必要です。

  • iTunes Connect - Apple が開発者の代理で集めた資金を送金できるように、銀行と税金に関する情報を Apple に提供しておく必要があります。 その後、販売する製品を構成し、サンドボックス ユーザー アカウントを設定して購入をテストできます。 また、Apple でホストする非コンシューマブル製品のホスト コンテンツも構成する必要があります。
  • iOS プロビジョニング ポータル - アプリ内購入をサポートするアプリケーションの場合と同様に、バンドル識別子を作成し、アプリの App Store アクセスを有効にします。
  • StoreKit - 製品の表示、製品の購入、トランザクションの復元のためのコードをアプリに追加します。 iOS 6 StoreKit では、製品コンテンツのダウンロードもバックグラウンドで管理され、進行状況が更新されます。
  • カスタム コード - 顧客による購入を追跡し、購入した製品またはサービスを提供します。 SKDownload などの新しい iOS 6 StoreKit クラスを利用して、Apple がホストするコンテンツを取得します。

次のセクションでは、この記事のサンプル コードを使用して、パッケージの作成とアップロードから購入およびダウンロード プロセスの管理まで、ホストされたコンテンツを実装する方法について説明します。

サンプル コード

HostedNonConsumables サンプル プロジェクト (StoreKitiOS6.zip 内) では、ホストされたコンテンツを使用します。 このアプリは、Apple のサーバーで、ホストされたコンテンツである販売用の 2 つの「本の章」を提供しています。 コンテンツはテキスト ファイルと画像で構成されていますが、実際のアプリケーションでははるかに複雑なコンテンツを使用できます。

購入前、購入中、購入後のアプリは次のようになります。

購入前、購入中、購入後のアプリは次のようになります

テキスト ファイルとイメージがダウンロードされ、アプリケーションのドキュメント ディレクトリにコピーされます。 アプリケーション ストレージに使用できるさまざまなディレクトリの詳細については、ファイル システムのドキュメントを参照してください。

iTunes Connect

Apple のコンテンツ ホスティングを使用する新しい製品を作成する場合は、製品の種類として必ず非コンシューマブルを選択します。 その他の製品の種類では、コンテンツ ホスティングはサポートされていません。 また、販売する既存の製品に対してコンテンツ ホスティングを有効にしないでください。新しい製品のコンテンツ ホスティングのみを有効にする必要があります。

非消耗品製品の種類を選択する

製品 ID を入力します。 この ID は、後でこの製品のコンテンツを作成するときに必要になります。

製品 ID を入力します

コンテンツ ホスティングは [詳細] セクションで設定します。 アプリ内購入が公開される前に、キャンセルする場合 (テスト コンテンツをアップロードした場合でも) [Host Content with Apple]\(Apple でコンテンツをホストする\) チェックボックスをオフにします。 ただし、アプリ内購入がライブになった後は、コンテンツ ホスティングを削除できません。

Apple を使用したコンテンツのホスティング

コンテンツ ホスティングを有効にすると、製品はアップロード待機中状態になり、次のメッセージを表示します。

製品は [アップロード待機中] の状態に入り、このメッセージが表示されます

コンテンツ パッケージは Xcode で作成し、アーカイブ ツールを使用してアップロードする必要があります。 コンテンツ パッケージを作成する手順については、次の「.PKG ファイルの作成」セクションを参照してください。

.PKG ファイルの作成

Apple にアップロードするコンテンツ ファイルは、次の制限を満たしている必要があります。

  • サイズが 2 GB を超えることはできません。
  • 実行可能コード (またはコンテンツの外部を指すシンボリック リンク) を含めることはできません。
  • 適切にフォーマットされており (.plist ファイルを含む)、.pkg ファイル拡張子を持っている必要があります。 Xcode を使用してこれらの手順に従うと、この処理が自動的に行われます。

これらの制限を満たしている限り、さまざまなファイルや種類のファイルを追加できます。 コンテンツはアプリケーションに配信する前に zip に圧縮され、コードがこれにアクセスする前に、StoreKit によって解凍されます。

コンテンツ パッケージをアップロードした後は、新しいコンテンツに置き換えられます。 通常のプロセスを使用して、レビューまたは承認のために新しいコンテンツをアップロードして送信する必要があります。 更新されたコンテンツ パッケージの ContentVersion フィールドを増分して、新しい内容であることを示します。

Xcode アプリ内購入コンテンツ プロジェクト

アプリ内購入製品のコンテンツのパッケージを作成するには、現在 Xcode が必要です。 OBJECTIVE-C のコーディングは一切必要ありません。Xcode には、ファイルと plist のみを含むこれらのパッケージ用の新しいプロジェクトタイプがあります。

サンプル アプリケーションには、販売のための書籍の章があります。各章のコンテンツ パッケージには次のものが含まれます。

  • テキスト ファイル、および
  • 章を表す画像。

まず、メニューから [ファイル] >[新しいプロジェクト] を選択し、[In-App Purchase Content]\(アプリ内購入コンテンツ\) を選択します。

アプリ内購入コンテンツの選択

製品名会社識別子を、バンドル識別子がこの製品のために iTunes Connect で入力した製品 ID と一致するように入力します。

名前と識別子を入力する

これで、空のアプリ内購入コンテンツ プロジェクトが作成されます。 右クリックし、[ファイルの追加] を選択するか、それらをプロジェクト ナビゲーターにドラッグします。 ContentVersion が正しいことを確認します (1.0 から開始する必要がありますが、後でコンテンツを更新する場合は、これを増分する必要があります)。

このスクリーンショットは、プロジェクトに含まれるコンテンツ ファイルとメイン ウィンドウに表示される plist エントリを含む Xcode を示しています。

このスクリーンショットは、プロジェクトに含まれるコンテンツ ファイルと、plist エントリがメイン ウィンドウに表示された Xcode を示しています

すべてのコンテンツ ファイルを追加したら、このプロジェクトを保存し、後でもう一度編集するか、アップロード プロセスを開始できます。

.PKG ファイルのアップロード

コンテンツ パッケージをアップロードする最も簡単な方法は、Xcode アーカイブ ツールを使用することです。 メニューから [製品] >[アーカイブ] を選択して開始します。

[アーカイブ] を選択する

コンテンツ パッケージが、次に示すようにアーカイブに表示されます。 この行を示すアーカイブの種類とアイコンは、アプリ内購入コンテンツ アーカイブです。 [検証] をクリックし、アップロードを実際に実行せずに、コンテンツ パッケージのエラーを確認します。

パッケージを検証する

iTunes Connect の資格情報でログインします。

iTunes Connect の資格情報でログインする

適切なアプリケーションとアプリ内購入を選択して、このコンテンツを次に関連付けます。

このコンテンツを次に関連付ける適切なアプリケーションとアプリ内購入を選択する

次のスクリーンショットのようなメッセージが表示されます。

問題なしのメッセージの例

次に、同様のプロセスを実行しますが、[配布] をクリックすると、実際にコンテンツがアップロードされます。

アプリの配布

最初のオプションを選択して、コンテンツをアップロードします。

コンテンツをアップロードする

もう一度サインインします。

ログイン

コンテンツをアップロードする適切なアプリケーションとアプリ内購入レコードを選択します。

アプリケーションとアプリ内購入レコードを選択する

ファイルがアップロードされるまで待ちます。

コンテンツのアップロード ダイアログ

アップロードが完了すると、コンテンツが App Store に送信されたことを示すメッセージが表示されます。

成功したアップロード メッセージの例

それが完了すると、iTunes Connect の製品ページに戻ったときに、パッケージの詳細が表示され、送信準備完了状態になります。 製品がこの状態になると、サンドボックス環境でテストを開始できます。 サンドボックスでテストするために製品を「送信」する必要はありません。

iTunes Connect では、パッケージの詳細が表示され、[送信の準備完了] 状態になります

アーカイブのアップロードと iTunes Connect の状態が更新される間に、しばらく時間がかかる場合があります (数分など)。 製品を個別にレビュー用に提出することも、アプリケーション バイナリと組み合わせて送信することもできます。 Apple が正式コンテンツを承認した後にのみ、コンテンツはアプリ内で購入するために実稼働 App Store で利用できるようになります。

PKG ファイル形式

Xcode とアーカイブ ツールを使用してホストされたコンテンツ パッケージを作成してアップロードしても、パッケージ自体のコンテンツをそれ以後見られなくなるわけではありません。 サンプル アプリ用に作成されたパッケージ内のファイルとディレクトリは、次のスクリーンショットのようになります。ルートに plist ファイルがあり、Contents サブディレクトリに製品ファイルがあります。

ルート内の plist ファイルと Contents サブディレクトリ内の製品ファイル

デバイス上のパッケージからファイルを抽出するには、この情報を理解する必要があるため、パッケージのディレクトリ構造 (特に Contents サブディレクトリ内のファイルの場所) に注意してください。

パッケージ コンテンツの更新

コンテンツが承認された後にコンテンツを更新する手順は次のとおりです。

  • Xcode でアプリ内購入コンテンツ プロジェクトを編集します。
  • バージョン番号の数値を上げます。
  • iTunes Connect にもう一度アップロードします。 後続の購入者は自動的に最新バージョンを取得しますが、以前バージョンを既に持っているユーザーは通知を受け取りません。
  • ユーザーに通知し、新しいバージョンのコンテンツを取得するようユーザーに促す役割は、アプリが担います。 アプリでは、StoreKit の復元機能を使用して、新しいバージョンをダウンロードする関数もビルドする必要があります。
  • 新しいバージョンが存在するかどうかを判断するには、アプリに機能を組み込んで SKProducts をフェッチし (製品価格の取得などに使用されるのと同じプロセス)、ContentVersion プロパティを比較します。

購入の概要

このセクションを読む前に、既存のアプリ内購入ドキュメントを確認してください。

ホストされたコンテンツを含む製品を購入してダウンロードしたときに発生するイベントのシーケンスを次の図に示します。

ホストされているコンテンツを含む製品を購入してダウンロードするときに発生するイベントのシーケンス

  1. 新しい製品は、ホストされたコンテンツが有効になっている iTunes Connect で作成できます。 実際のコンテンツは Xcode で個別に構築され (ファイルをフォルダーにドラッグするのと同様に簡単)、アーカイブされて iTunes にアップロードされます (コーディングは不要)。 その後、各製品は承認のために提出され、承認後購入できるようになります。 サンプル コードでは、これらの製品 ID はハードコーディングされていますが、iTunes Connect に新しい製品とコンテンツを送信するときに更新できるように、使用可能な製品リストをリモート サーバーに格納する場合、Apple でコンテンツをホストする方が柔軟性が高くなります。
  2. ユーザーが製品を購入すると、トランザクションが処理のために支払キューに配置されます。
  3. StoreKit は、購入要求を処理のために iTunes サーバーに転送します。
  4. iTunes サーバーでトランザクションが完了し (顧客が課金されるなど)、領収書がアプリに返され、ダウンロード可能かどうか (およびダウンロード可能な場合はファイル サイズやその他のメタデータ) を含む製品情報が添付されます。
  5. コードでは、その製品がダウンロード可能かどうかを確認し、可能な場合、支払いキューにも配置されるコンテンツのダウンロード要求を行う必要があります。 StoreKit は、この要求を iTunes サーバーに送信します。
  6. サーバーはコンテンツ ファイルを StoreKit に返します。ここから、ダウンロードの進行状況と残りの推定時間をコードに返すコールバックが提供されます。
  7. 完了すると、通知を受け取り、Cache フォルダー内のファイルの場所を渡します。
  8. コードでは、ファイルをコピーして検証し、製品が購入されたことを記録するためのすべての状態を保存する必要があります。 この機会に、新しいファイルにバックアップ フラグを正しく設定します (ヒント: ファイルがサーバーから取得され、ユーザーが編集していない場合は、ユーザーは Apple のサーバーからいつでも取得できるため、バックアップはおそらくスキップできます)。
  9. FinishTransaction を呼び出します。 このステップは、支払いキューからトランザクションを削除する場合に重要です。 また、キャッシュ ディレクトリからコンテンツをコピーするまでは、FinishTransaction を呼び出さないことも重要です。 FinishTransaction を呼び出すと、キャッシュされたファイルはすぐに消去される可能性があります。

ホストされたコンテンツ購入の実装

次の情報は、完全なアプリ内購入ドキュメントと併せて読む必要があります。 このドキュメントの情報では、ホストされたコンテンツと以前の実装の違いに焦点を当てています。

クラス

iOS 6 でホストされたコンテンツをサポートするために、次のクラスが追加または変更されました。

  • SKDownload - 進行中のダウンロードを表す新しいクラス。 API は、1 つの製品につき複数許可されますが、最初は 1 つだけが実装されています。
  • SKProduct - 新しいプロパティが追加されました: DownloadableContentVersionContentLengths 配列。
  • SKPaymentTransaction - 新しいプロパティ Downloads が追加されました。この製品がダウンロード可能なコンテンツをホストしている場合は、このプロパティが、SKDownload オブジェクトのコレクションを格納します。
  • SKPaymentQueue - 新しいメソッド StartDownloads が追加されました。 ホストされたコンテンツをフェッチするには、SKDownload オブジェクトを使用してこのメソッドを呼び出します。 ダウンロードは、バックグラウンドで行われる可能性があります。
  • SKPaymentTransactionObserver - 新しいメソッド UpdateDownloads が追加されました。 StoreKit は、現在のダウンロード操作に関する進行状況情報を使用してこのメソッドを呼び出します。

新しい SKDownload クラスの詳細:

  • Progress - ユーザーに達成率インジケーターを表示するために使用できる 0 から 1 の値。 ダウンロードが完了したかどうかを検出するために、Progress == 1 を使用しないでください。State == Finished を確認してください。
  • TimeRemaining - ダウンロード残り時間の見積もり (秒単位)。 -1 は、まだ見積もり計算中であることを意味します。
  • State - アクティブ、待機中、完了、失敗、一時停止、取り消し済み。
  • ContentURL - コンテンツがディスクに配置された Cache ディレクトリ内のファイルの場所。 ダウンロードが完了した後にのみ設定されます。
  • Error - 状態が「失敗」の場合、このプロパティを確認します。

サンプル コード内のクラス間の相互作用を次の図に示します (ホストされたコンテンツ購入に固有のコードは緑色で示されています)。

この図では、ホストされているコンテンツの購入が緑色で表示されている

これらのクラスが使用されているサンプル コードを、このセクションの残りの部分で示します。

CustomPaymentObserver (SKPaymentTransactionObserver)

ダウンロード可能なコンテンツを確認するように、既存の UpdatedTransactions のオーバーライドを変更し、必要に応じて StartDownloads を呼び出します。

public override void UpdatedTransactions (SKPaymentQueue queue, SKPaymentTransaction[] transactions)
{
    foreach (SKPaymentTransaction transaction in transactions) {
        switch (transaction.TransactionState) {
        case SKPaymentTransactionState.Purchased:
            // UPDATED FOR iOS 6
            if (transaction.Downloads != null && transaction.Downloads.Length > 0) {
                // Purchase complete, and it has downloads... so download them!
                SKPaymentQueue.DefaultQueue.StartDownloads (transaction.Downloads);
                // CompleteTransaction() call has moved after downloads complete
            } else {
                // complete the transaction now
                theManager.CompleteTransaction(transaction);
            }
            break;
        case SKPaymentTransactionState.Failed:
            theManager.FailedTransaction(transaction);
            break;
        case SKPaymentTransactionState.Restored:
            // TODO: you must decide how to handle restored transactions.
            // Triggering all the downloads at once is not advisable.
            theManager.RestoreTransaction(transaction);
            break;
        default:
            break;
        }
    }
}

オーバーライドされた新しいメソッド UpdatedDownloads を次に示します。 StoreKit は、StartDownloadsUpdatedTransactions がトリガーされた後、このメソッドを呼び出します。 このメソッドは、ダウンロードの進行状況を提供するために不確定な間隔で複数回呼び出され、ダウンロードが完了したときに再度呼び出されます。 各メソッド呼び出しでキュー内の複数のダウンロードの状態を提供できるように、このメソッドが SKDownload オブジェクトの配列を受け入れることに注目してください。 以下の実装に示すように、ダウンロードの状態は毎回チェックされ、適切なアクションが実行されます。

// ENTIRELY NEW METHOD IN iOS6
public override void PaymentQueueUpdatedDownloads (SKPaymentQueue queue, SKDownload[] downloads)
{
    Console.WriteLine (" -- PaymentQueueUpdatedDownloads");
    foreach (SKDownload download in downloads) {
        switch (download.DownloadState) {
        case SKDownloadState.Active:
            // TODO: implement a notification to the UI (progress bar or something?)
            Console.WriteLine ("Download progress:" + download.Progress);
            Console.WriteLine ("Time remaining:   " + download.TimeRemaining); // -1 means 'still calculating'
            break;
        case SKDownloadState.Finished:
            Console.WriteLine ("Finished!!!!");
            Console.WriteLine ("Content URL:" + download.ContentUrl);

            // UNPACK HERE! Calls FinishTransaction when it's done
            theManager.SaveDownload (download);

            break;
        case SKDownloadState.Failed:
            Console.WriteLine ("Failed"); // TODO: UI?
            break;
        case SKDownloadState.Cancelled:
            Console.WriteLine ("Canceled"); // TODO: UI?
            break;
        case SKDownloadState.Paused:
        case SKDownloadState.Waiting:
            break;
        default:
            break;
        }
    }
}

InAppPurchaseManager (SKProductsRequestDelegate)

このクラスには、各ダウンロードが正常に完了した後に呼び出される新しいメソッド SaveDownload が含まれています。

ホストされたコンテンツが正常にダウンロードされ、Cache ディレクトリに解凍されました。 .PKG ファイルの構造には、すべてのファイルが Contents サブディレクトリに保存されている必要があります。そうすることで、次のコードは、Contents サブディレクトリ内からファイルを抽出します。

このコードは、コンテンツ パッケージ内のすべてのファイルを反復処理し、それらを Documents ディレクトリ内の ProductIdentifier 用に名付けられたサブフォルダにコピーします。 最後に、CompleteTransaction を呼び出し、ここで、支払キューからトランザクションを削除するための FinishTransaction が呼び出されます。

// ENTIRELY NEW METHOD IN iOS 6
public void SaveDownload (SKDownload download)
{
    var documentsPath = Environment.GetFolderPath (Environment.SpecialFolder.Personal); // Documents folder
    var targetfolder = System.IO.Path.Combine (documentsPath, download.Transaction.Payment.ProductIdentifier);
    // targetfolder will be "/Documents/com.xamarin.storekitdoc.montouchimages/" or something like that
    if (!System.IO.Directory.Exists (targetfolder))
        System.IO.Directory.CreateDirectory (targetfolder);
    foreach (var file in System.IO.Directory.EnumerateFiles
             (System.IO.Path.Combine(download.ContentUrl.Path, "Contents"))) { // Contents directory is the default in .PKG files
        var fileName = file.Substring (file.LastIndexOf ("/") + 1);
        var newFilePath = System.IO.Path.Combine(targetfolder, fileName);
        if (!System.IO.File.Exists(newFilePath)) // HACK: this won't support new versions...
            System.IO.File.Copy (file, newFilePath);
        else
            Console.WriteLine ("already exists " + newFilePath);
    }
    CompleteTransaction (download.Transaction); // so it gets 'finished'
}

FinishTransaction が呼び出されると、ダウンロードしたファイルが Cache ディレクトリ内に存在することが保証されなくなります。 すべてのファイルは、FinishTransaction を呼び出す前にコピーする必要があります。

その他の注意事項

上記のコード例は、ホストされたコンテンツ購入の非常に単純な実装を示しています。 考慮する必要があるその他の点がいくつかあります。

更新されたコンテンツの検出

ホストされたコンテンツ パッケージを更新することは可能ですが、StoreKit には、製品を既にダウンロードして購入した後のユーザーに、これらの更新プログラムをプッシュするメカニズムはありません。 この機能を実装するために、コードは新しい SKProduct.ContentVersion プロパティ (SKProductDownloadable の場合) を定期的にチェックし、値が増分されているかどうかを検出できます。 または、プッシュ通知システムを構築することもできます。

更新されたコンテンツ バージョンのインストール

上記のサンプル コードでは、ファイルが既に存在する場合、ファイルのコピーはスキップされます。 コンテンツの新しいバージョンがダウンロードされることをサポートする場合は、この方法は適切ではありません。

別の方法として、コンテンツをバージョンの名前が付いたフォルダー (NSUserDefaults 内、または完了した購入レコードを保存する場所など) にコピーし、現在のバージョンを追跡することもできます。

トランザクションの復元

SKPaymentQueue.DefaultQueue.RestoreCompletedTransactions が呼び出されると、StoreKit はユーザーの以前のすべてのトランザクションを返します。 大量のアイテムを購入した場合、または各購入に大きなコンテンツ パッケージがある場合、すべてが一度にダウンロードのためにキューに登録されるため、復元によって多くのネットワーク トラフィックが発生する可能性があります。

関連するコンテンツ パッケージの実際のダウンロードとは別に、製品が購入されたかどうかを追跡することを検討してください。

ダウンロードの一時停止、再起動、取り消し

サンプル コードではこの機能を示していませんが、ホストされたコンテンツのダウンロードを一時停止して再起動することができます。 SKPaymentQueue.DefaultQueue は、PauseDownloadsResumeDownloadsCancelDownloads のメソッドを持っています。

ダウンロードが FinishTransaction される前に、コードが支払いキューで Finished を呼び出した場合、そのダウンロードは自動的に取り消されます。

ダウンロードしたコンテンツへの SKIP-Backup フラグの設定

Apple の iCloud バックアップ ガイドラインでは、サーバーから簡単に復元できるユーザー以外のコンテンツは、バックアップするべきではありません (iCloud ストレージが不必要に使い果たされるため)。 バックアップ属性の設定の詳細については、ファイル システムに関するドキュメントを参照してください。

まとめ

この記事では、iOS6 の StoreKit の 2 つの新機能を紹介しました。これらは iTunes やその他のコンテンツをアプリ内から購入し、Apple のサーバーを使用して独自のアプリ内購入をホストするためのものです。 この概要は、StoreKit 機能の実装を完全に網羅するためには、既存のアプリ内購入ドキュメントと併せて読む必要があります。