
SwiftUIでアプリ内課金を扱いたいときに避けて通れないのが Product.purchase() の使い方です。
このメソッドは、StoreKit2においてユーザーに課金処理を提供するための中心的な役割を担っており、ボタンタップひとつで購入処理を開始できます。
この記事では、Product.purchase() の基本的な意味や使い方、実装例、成功・失敗時の挙動、注意点までわかりやすく解説します。
Product.purchase() とは?
Product.purchase() は、StoreKit2 における Product 型(=購入可能なアイテム)に用意されているメソッドで、ユーザーが実際に商品を購入する際に使います。
例えば、アプリ内で「プレミアムプランにアップグレード」や「追加コンテンツの購入」といったボタンを用意し、そこから purchase() を呼び出すことで、Appleの標準的な課金ダイアログが表示されます。
以前の StoreKit1 では課金フローを複雑にコーディングする必要がありましたが、StoreKit2 では purchase() を使うだけで非常にシンプルに書けるのが特徴です。
基本的な使い方
StoreKit2を使うには、まず Product を取得し、そこから .purchase() を呼び出す形になります。
以下は基本的な課金処理の流れを示すシンプルな例です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
import SwiftUI import StoreKit struct ContentView: View { @State private var product: Product? @State private var purchaseResult: Product.PurchaseResult? var body: some View { VStack { if let product = product { Button("購入する: \(product.displayName)") { Task { do { let result = try await product.purchase() purchaseResult = result } catch { print("購入エラー: \(error.localizedDescription)") } } } } else { Text("商品情報を読み込み中...") } if let purchaseResult = purchaseResult { switch purchaseResult { case .success(let verificationResult): if case .verified(_) = verificationResult { Text("購入成功!").foregroundColor(.green) } else { Text("購入の検証に失敗しました") } case .userCancelled: Text("購入をキャンセルしました") case .pending: Text("購入は保留中です") @unknown default: Text("未知の購入結果です") } } } .task { await loadProduct() } } func loadProduct() async { do { let storeProducts = try await Product.products(for: ["com.example.productid"]) product = storeProducts.first } catch { print("商品取得エラー: \(error.localizedDescription)") } } } |
このコードのポイントは以下の通りです。
Product.products(for:)で販売するアイテムを取得(引数は App Store Connect で登録した Product ID)product.purchase()を呼び出して購入処理を開始- 戻り値(
Product.PurchaseResult)を検証して購入結果を処理 .verified(_)を通過したときのみ課金完了と見なす
purchase() の戻り値の型は Product.PurchaseResult で、購入状況に応じて success(成功)、userCancelled(キャンセル)、pending(保留)などが返ってきます。
成功しても検証(.verified)に通らない場合は、処理を完了させてはいけない点に注意が必要です。
各ケースの処理と注意点
purchase() の結果は、以下のようなケースに分かれます。
| ケース | 内容 | 処理例 |
|---|---|---|
.success(.verified) |
購入が成功かつAppleによる検証を通過 | アンロックやサブスク解放などを実行 |
.success(.unverified) |
検証に失敗 | ユーザーに「検証失敗」と表示し、課金は行わない |
.userCancelled |
ユーザーが課金ダイアログをキャンセル | 特に何もしない(UI更新程度) |
.pending |
ファミリー共有や承認待ちなどで保留 | 状況に応じて「保留中」と通知する |
特に .verified でない限り、購入したと見なしてはいけないのが非常に大切なポイントです。
この場合の.successは単にpurchaseの実行が成功して結果を取得できたことを示し、購入自体が成功しているかを指しているわけではないからです。
検証の処理がなければ、アプリのセキュリティに重大な欠陥が生じてしまいます。
非同期処理(async/await)とTaskの使い方
purchase() は非同期関数(async throws)であり、Swift Concurrency(async/await)を使って呼び出します。
そのため、ボタン内の処理は Task {} で囲む必要があります。
これはUIスレッドで非同期処理を安全に行うためです。
|
1 2 3 4 5 6 |
Button("購入する") { Task { try await product.purchase() } } |
古い DispatchQueue ベースではなく、Swiftのモダンな非同期処理スタイルで実装するのが推奨されています。
Product.purchase() の活用シーン
purchase() を使うのは、以下のような場面が考えられます。
- プレミアム機能の解除(非消耗型)
- 消耗型アイテムの購入(例:ゲーム内通貨)
- サブスクリプション(月額/年額プラン)
- 機能別に複数の商品を販売したい場合
このように、アプリ内でユーザーに何かを「購入させる」すべてのシーンで登場します。
逆に、購入情報を取得したり復元したりする場合は Transaction.currentEntitlements や Transaction.latest(for:)、TransactionListener の活用が必要です。
Product.purchase() を使うときの注意点
以下の点を押さえておくと、失敗の少ない実装ができます。
- StoreKit2はiOS15以降が対象(旧バージョンでは動作しない)
- App Store Connectに商品を登録し、審査が通った商品IDを使うこと
purchase()後は必ず.verifiedか確認し、不正な課金を防ぐ- ユーザーがキャンセル・保留になるケースも考慮する
- UI上は課金中であることや成功・失敗を明示的にフィードバックする
また、実機検証は「サンドボックス環境」でのみ可能です。
シミュレータでは課金UIが表示されないため注意してください。
まとめ
今回は Swift の Product.purchase() について、初心者向けに使い方を詳しく解説しました。
purchase()は StoreKit2の課金処理の中心となるメソッド- 非同期で呼び出し、結果に応じて
.verifiedを確認するのが基本 - キャンセルや保留など複数のケースに備える必要がある
- UIに課金結果をしっかりフィードバックするのが実装のコツ
- iOS15以降・App Store商品登録済みでのみ動作する
StoreKit2によって課金実装はかなりシンプルになりましたが、実際のアプリで安心して使えるようにするには、結果の検証やUIの工夫も欠かせません。
ぜひ、自分のアプリに purchase() を取り入れて、スムーズな課金体験を提供してみてくださいね!

