WWDC Quick Look 💓 By SwiftGGTeam
What's new in StoreKit 2 and StoreKit Testing in Xcode

What's new in StoreKit 2 and StoreKit Testing in Xcode

元の動画を見る

ハイライト

StoreKit 2 では、プロモーション購入管理 API、Transaction/RenewalInfo データ モデル拡張機能、WWDC2023 の SwiftUI 専用製品表示ビュー (ProductView/StoreView/SubscriptionStoreView)、およびマルチデバイスのオフライン デバッグ、シミュレートされたエラー挿入、カスタム サブスクリプション更新レートをサポートする Xcode 15 の StoreKit テスト ツール チェーンが追加されました。

主な内容

購入を促進するための完全なリンク

ユーザーが App Store の製品ページでプロモーションされたアプリ内購入アイテムを見てクリックして購入すると、アプリはこのインテントをリッスンしてトランザクションを完了する必要があります。以前は、開発者は受動的に待つことしかできませんでしたが、現在では、プロモーションされた製品の表示順序と可視性を積極的に管理できるようになりました。

ユーザーが App Store からプロモーション製品を購入するとき、アプリはまだ準備ができていない可能性があります (レベルなどで)。PurchaseIntent は遅延処理をサポートしており、後で完了するためにインテントを保存します。購入した商品を非表示にしたり、ゲームレベルの進行状況の表示順序を動的に調整したりできます。

データモデルだけでようやく十分

トランザクション モデルでは、storefront (店舗エリア)、storefrontcountryCode (国コード)、および理由 (購入理由: ユーザーが積極的に購入した、または自動的に更新した) が追加されます。RenewalInfo に nextRenewalDate (次回の更新日) が追加されました。

これらのフィールドは、ユーザーがどの地域の店舗から来たのかを判断し、初回購入と更新を区別し、次回の控除時期を事前にユーザーに通知するなど、長期的な問題点を解決します。

テストはもはやブラックボックスではありません

Xcode 15 の StoreKit テストでは、デバイスがアプリを実行していないときにトランザクションを直接作成し、1 年前のサブスクリプション購入をシミュレートし、自動更新を観察することがサポートされています。ネットワーク エラーや製品の利用不可などの例外を挿入して、アプリケーションのフォールト トレラント ロジックを検証できます。サブスクリプションの更新レートを 1 分に 1 回に調整して、長期サブスクリプションのステータスを迅速に確認できます。

##詳細

プロモーションの購入意向を監視する

01:42

import StoreKit

let promotedPurchasesListener = Task {
    for await promotion in PurchaseIntent.intents {
        let product = promotion.product

        do {
            let result = try await product.purchase()
            // 購入結果を処理
        } catch {
            // エラーを処理するか、インテントを保存して後で再試行
        }
    }
}

キーポイント:

  • PurchaseIntent.intentsこれは非同期シーケンスであり、ユーザーが App Store でプロモーション購入をクリックするたびに新しい値を生成します。
  • システムは、アプリが実行されていないときもインテントを保持し、起動直後にインテントを受信します。
  • 保存できるpromotionユーザーにとって適切なタイミングで呼び出されるオブジェクトpurchase()
  • アプリケーションのライフサイクル中、リスナーを実行し続ける必要がある

プロモーションされた製品の注文と可視性を管理する

02:57

import StoreKit

// 現在のプロモーション順序を取得
do {
    let promotions = try await Product.PromotionInfo.currentOrder

    if promotions.isEmpty {
        // ローカルの上書きがないため、App Store Connect で設定した順序を使用
    }

    for promotion in promotions {
        let productID = promotion.productID
        let visibility = promotion.visibility
    }
} catch {
    // エラーを処理
}

// カスタム順序を設定
let newOrder = ["acorns.individual", "nectar.cup", "sunflowerseeds.pile"]
do {
    try await Product.PromotionInfo.updateProductOrder(byID: newOrder)
} catch { }

// 特定の製品を非表示にする
do {
    try await Product.PromotionInfo.updateProductVisibility(.hidden, for: "acorns.individual")
} catch { }

// またはオブジェクト経由で変更
var firstPromotion = promotions.first!
firstPromotion.visibility = .hidden
try await firstPromotion.update()

キーポイント:

  • currentOrder空は、ローカル カバレッジがなく、App Store Connect のデフォルト構成が使用されることを意味します。
  • visibility次の 3 つの状態があります。.visible.hidden.default(App Store Connect設定に従ってください)
  • 順序と可視性の変更は、現在のデバイスにのみ影響します
  • ゲームの進行状況やユーザーのステータスに基づいてプロモーション表示を動的に調整するのに適しています

Transaction および RenewalInfo の新しいフィールド

05:00

// Transaction の新しいフィールド
for await result in Transaction.currentEntitlements {
    if case .verified(let transaction) = result {
        let storefront = transaction.storefront
        let countryCode = transaction.storefrontCountryCode
        let reason = transaction.reason // .purchase または .renewal

        print("ストア: \(storefront), 理由: \(reason)")
    }
}

// RenewalInfo の新しいフィールド
if let subscriptionInfo = product.subscription {
    let renewalInfo = try await subscriptionInfo.status
    for status in renewalInfo {
        if case .verified(let info) = status.renewalInfo {
            if let nextDate = info.nextRenewalDate {
                print("次回更新: \(nextDate)")
            }
        }
    }
}

キーポイント:

  • storefrontそしてstorefrontCountryCodeユーザーがどこにいるかを特定するのに役立ちます
  • reasonユーザーのアクティブな購入を区別する (.purchase) および自動更新 (.renewal
  • nextRenewalDateRenewalInfoで次回の更新処理時間を伝える
  • Xcode 15 で構築されたアプリが利用可能で、ほとんどのフィールドは古いシステムと上位互換性があります

請求に関する問題メッセージの処理

06:12

for await message in Message.messages {
    switch message.reason {
    case .billingIssue:
        // サブスクリプションの更新に失敗したため、請求問題シートを表示
        // StoreKit が自動表示し、アプリは延期または抑制を選択可能
        await message.display()
    default:
        break
    }
}

キーポイント:

  • .billingIssueiOS 16.4 で導入され、請求の問題によりサブスクリプションの更新が失敗したときにトリガーされます
  • StoreKit は請求問題シートを自動的に表示するため、ユーザーはアプリを離れることなく問題を解決できます。
  • アプリケーションはメッセージを延期したり (ユーザーが対話中である場合)、メッセージを抑制したりできます。
  • サンドボックス環境が有効になっており、事前にテストできます

SwiftUI 製品表示ビュー

08:32

import SwiftUI
import StoreKit

// 単一製品ビュー
struct BirdFoodShop: View {
    let productID: String

    var body: some View {
        ProductView(id: productID) {
            BirdFoodProductIcon(for: productID)
        }
        .productViewStyle(.large)
    }
}

// ストアビュー(複数製品)
struct StorePage: View {
    let productIDs: [String]

    var body: some View {
        StoreView(ids: productIDs) { product in
            BirdFoodIcon(productID: product.id)
        }
    }
}

// サブスクリプションストアビュー
struct SubscriptionPage: View {
    let groupID: String

    var body: some View {
        SubscriptionStoreView(groupID: groupID)
    }
}

// サブスクリプション管理シート
.manageSubscriptionsSheet(
    isPresented: $showManageSheet,
    subscriptionGroupID: groupID // 指定したサブスクリプショングループへ直接移動
)

キーポイント:

  • ProductView製品情報を自動的に読み込み、ローカライズされたタイトル、説明、プロモーション画像を表示します。
  • StoreViewわずか数行のコードで製品コレクションを表示し、完全なストアを構築します
  • SubscriptionStoreViewサブスクリプション グループのすべてのレベルを表示するには、subscriptionGroupID
  • .manageSubscriptionsSheet新しいsubscriptionGroupIDパラメータ、指定されたグループに直接ジャンプします
  • すべてのビューは、背景、フォントなどの SwiftUI 標準カスタマイズをサポートしています。

Xcode トランザクション マネージャーのオフライン デバッグ

11:12

Xcode 15 のトランザクション マネージャーは以下をサポートします。

  • 接続されているすべてのデバイスとエミュレータにわたる StoreKit アプリのトランザクション履歴を表示
  • アプリを実行せずに購入履歴を表示
  • Mac から直接新しい購入を作成し、購入パラメータ (数量、割引コード、購入日) を設定します -自動更新設定とサブスクリプションの履歴レビューをサポート

操作パス:Debug > StoreKit > Manage Transactions

StoreKit エラーをシミュレートする

18:54

StoreKit 構成ファイルの構成設定で:

  • Purchase API の [エラーをシミュレート] オプションをオンにします。
  • エラーのタイプを選択します。.productUnavailable(ユーザースイッチストア)
  • 構成は自動的に保存され、実行中のデバイスに同期されます
  • アプリを再実行せずにテストします

サポートされている API とエラー タイプには、製品の読み込み、購入、受領書検証、トランザクション検証、返金リクエストなどが含まれます。

単体テストで購入とバグを模擬する

21:09

import StoreKit
import StoreKitTest
import XCTest

func testSubscriptionRenewal() async throws {
    let session = try SKTestSession(configurationFileNamed: "Store")

    // 1 年前のサブスクリプション購入をシミュレート
    let oneYear: TimeInterval = 365 * 24 * 60 * 60
    let transaction = try await session.buyProduct(
        identifier: "birdpass.individual",
        options: [.purchaseDate(Date.now - oneYear)]
    )

    // 取引状態を検証
    XCTAssertNotNil(transaction)
}

func testLoadProductsError() async throws {
    let session = try SKTestSession(configurationFileNamed: "Store")

    // ネットワークエラーを注入
    session.setSimulatedError(.generic(.networkError), forAPI: .loadProducts)

    do {
        _ = try await Product.products(for: ["product1"])
        XCTFail("ネットワークエラーがスローされるはずです")
    } catch StoreKitError.networkError {
        // 期待されるエラー
    }

    // シミュレーションエラーをクリア
    session.setSimulatedError(nil, forAPI: .loadProducts)
}

func testFastRenewal() async throws {
    let session = try SKTestSession(configurationFileNamed: "Store")

    // 1 分ごとに更新し、長期サブスクリプション状態をすばやく検証
    session.timeRate = .oneRenewalEveryMinute

    let transaction = try await session.buyProduct(identifier: "birdpass.individual")

    // 更新動作を観察するために待機
}

キーポイント:

  • SKTestSessionStoreKit 構成ファイルからテスト環境を作成する
  • buyProduct(options:)サポートpurchaseDateテスト固有のオプションを待つ
  • setSimulatedError(_:forAPI:)指定された API のエラーを挿入します
  • timeRateサブスクリプションの更新速度を制御し、.oneRenewalEveryMinute最速の
  • 他のテストに影響を与えないように、テスト後にシミュレーション エラーをクリアします。

重要ポイント

  1. ダイナミック プロモーション ストア
  • 何をすべきか: ユーザーのゲームの進行状況に基づいて、App Store で宣伝された製品の表示順序と可視性を動的に調整します。
  • 実行する価値がある理由:Product.PromotionInfoプロモーション シーケンスのローカル カバレッジを許可し、ユーザーがレベルをクリアした後に高度な小道具を自動的に表示し、購入した小道具を自動的に非表示にします。
  • 開始方法: モニタリングPurchaseIntent.intentsプロモーション購入を処理するには、次を使用します。updateProductOrderそしてupdateProductVisibility試合状況に応じて調整
  1. サブスクリプション ステータス ダッシュボード
  • 対処方法: 次回の更新日、ストアの地域、更新の理由など、ユーザーのサブスクリプションの詳細をアプリ内に表示します。
  • 実行する価値がある理由:nextRenewalDate事前に控除時間をユーザーに通知し、storefrontCountryCode地域の価格設定の問題を特定するのに役立ちます
  • 開始方法: トラバーサルTransaction.currentEntitlements読むstorefrontそしてreason、からProduct.SubscriptionInfo得るrenewalInfo
  1. 請求に関する問題のセルフサービス修復
  • 対処法: サブスクリプションの更新が失敗した場合は、解約を減らすために支払い方法を修復するようにユーザーを積極的に案内します。
  • 実行する価値がある理由:Message.messages.billingIssueユーザーが離れる前にアプリが問題を解決できるようにすると、StoreKit が自動的に修復シートを表示します
  • 開始方法: モニタリングMessage.messages、受け取る.billingIssueタイミングを判断して電話をかけるmessage.display()あるいは適切な時期まで延期する
  1. ゼロコードストアページ
  • やるべきこと: StoreKit SwiftUI ビューを使用してアプリ内購入ページをすばやく作成します
  • 実行する価値がある理由:StoreViewそしてSubscriptionStoreViewわずか数行のコードで完全なストアを実装し、読み込み、表示、購入プロセスを自動的に処理します。
  • 開始方法: 作成する製品 ID 配列を渡しますStoreView、または使用しますSubscriptionStoreView(groupID:)サブスクリプション オプションを表示するには、次を使用します。.productViewStyleそして.containerBackground外観をカスタマイズする

関連セッション

コメント

GitHub Issues · utterances