メインコンテンツまでスキップ

StoreKit 2 と併用して AppCoins を追加する

このガイドでは、StoreKit 2 を使用しているアプリに AppCoins 課金を追加する方法を説明します。2 つの API は意図的にほぼ同一に設計されています。多くの場合、必要な変更は 3 点のみです。インポート、SDK の初期化呼び出し、そして transaction.id の型の違いです。

概要

AppCoins SDK は、設計上 StoreKit 2 の公開インターフェースと同じ形になっています。Product.products(for:)product.purchase(options:)Transaction.unfinishedtransaction.finish()VerificationResult はいずれも、両方の SDK に同じシグネチャで存在します。

AppCoins SDK は、iOS のバージョンとアプリの配信方法に基づいて有効になります。実行時判定の仕組み を参照してください。

どの方式が自身の構成に適しているかを判断するには、iOS 課金の連携戦略 を参照してください。本ガイドでは両方の方式を扱います。

Xcode の設定

SDK を動作させるには、Xcode のターゲットで次の 3 つの設定が必要です。

  1. Keychain Sharing

    1. Project Navigator(左サイドバー)でプロジェクトを選択します。
    2. TARGETS でターゲットを選択します。
    3. Signing & Capabilities タブを開きます。
    4. + ボタンをクリックして新しい機能を追加します。
    5. Keychain Sharing を検索して選択します。
    6. Keychain Groups フィールドで、既定値を com.aptoide.appcoins-wallet に正確に置き換えます。
  2. URL スキーム

    1. TARGETS でターゲットを選択します。
    2. Info タブに移動します。
    3. URL Types セクションを展開し、+ をクリックします。
    4. URL Schemes$(PRODUCT_BUNDLE_IDENTIFIER).iapRoleEditor に設定します。
  3. MKSellsDigitalGoods

    1. Info タブで Custom iOS Target Properties セクションまでスクロールし、+ をクリックします。
    2. キー MKSellsDigitalGoods を追加し、値を YES(Boolean)に設定します。
⚠️
AppCoins 課金が機能するには、この 3 つの設定がすべて必要です。いずれかが欠けていると購入を処理できません。

3 つの変更点

1. インポートと初期化

// Before
import StoreKit

// After
import AppCoinsSDK

インポートの入れ替えに加えて、すべてのアプリエントリポイントで AppcSDK.initialize()AppcSDK.handle(redirectURL:) を呼び出してください。

SceneDelegate.swift:

import AppCoinsSDK

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
AppcSDK.initialize()
initialize() // your app setup
if AppcSDK.handle(redirectURL: connectionOptions.urlContexts.first?.url) { return }
}

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
AppcSDK.initialize()
if AppcSDK.handle(redirectURL: URLContexts.first?.url) { return }
initialize()
}

AppDelegate.swift:

import AppCoinsSDK

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
AppcSDK.initialize()
initialize()
if let url = launchOptions?[.url] as? URL {
if AppcSDK.handle(redirectURL: url) { return true }
}
return true
}

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
AppcSDK.initialize()
if AppcSDK.handle(redirectURL: url) { return true }
initialize()
return true
}

2. 利用可否の確認(単一ビルドの場合のみ)

Apple 課金と AppCoins 課金の両方に対応する 1 つのバイナリを配布する場合は、SDK の呼び出しをすべて次のガードで囲んでください。

if await AppcSDK.isAvailable() {
// AppCoins billing path
} else {
// StoreKit 2 / Apple billing path
}

ストアフロントごとにビルドを分離している場合は、このガードは省略し、Aptoide 向けビルドでは常に AppCoins SDK の呼び出しを使用してください。

3. transaction.idUInt64 ではなく String

StoreKit 2 では transaction.idUInt64 ですが、AppCoins SDK では String です。これが唯一の型レベルの API の違いです。

トランザクション ID を保存したり、StoreKit 2 の値と比較したりしている場合は、明示的にキャストしてください。

let appcId: String = transaction.id          // AppCoins SDK
let skId: String = String(storeKitTxn.id) // cast from StoreKit 2 UInt64

変わらない点

StoreKit 2AppCoins SDK互換性
Product.products(for:)Product.products(for:)同一
product.purchase(options:)product.purchase(options:)同一
Product.PurchaseResultProduct.PurchaseResult同一
VerificationResultVerificationResult同一
Transaction.updates利用不可AppCoins に相当するものはありません。product.purchase() の戻り値と Transaction.unfinished を使用してください
Transaction.unfinishedTransaction.unfinished同一
Transaction.allTransaction.all同一
transaction.finish()transaction.finish()同一
transaction.productIDtransaction.productID同一
transaction.purchaseDatetransaction.purchaseDate同一
transaction.appAccountTokentransaction.appAccountToken同一
Product.PurchaseOption.appAccountTokenProduct.PurchaseOption.appAccountToken同一
transaction.idUInt64transaction.idString型が異なる

単一ビルドでの移行

Apple と Aptoide の両方の課金を 1 つのアプリバイナリで扱いたい場合は、この方式を使用します。

変更前 — StoreKit 2 のみ:

import StoreKit

class StoreManager {
func loadProducts() async {
do {
let products = try await Product.products(for: ["gas", "turbo"])
// display products
} catch {
print("Failed to load products: \(error)")
}
}

func purchase(_ product: Product) async {
do {
let result = try await product.purchase()
switch result {
case .success(let verification):
if case .verified(let transaction) = verification {
await transaction.finish()
}
case .pending, .userCancelled:
break
@unknown default:
break
}
} catch {
print("Purchase error: \(error)")
}
}
}

変更後 — 単一ビルド用のガードを備えた AppCoins SDK:

import AppCoinsSDK
import StoreKit

class StoreManager {
func loadProducts() async {
if await AppcSDK.isAvailable() {
let products = try? await AppCoinsSDK.Product.products(for: ["gas", "turbo"])
// display products
} else {
let products = try? await StoreKit.Product.products(for: ["gas", "turbo"])
// display products
}
}

func purchase(sku: String) async {
if await AppcSDK.isAvailable() {
guard let product = try? await AppCoinsSDK.Product.products(for: [sku]).first else { return }
do {
let result = try await product.purchase()
switch result {
case .success(let verification):
if case .verified(let transaction) = verification {
await transaction.finish()
}
case .pending, .userCancelled:
break
}
} catch let error as AppCoinsSDKError {
print("Purchase error: \(error)")
}
} else {
guard let product = try? await StoreKit.Product.products(for: [sku]).first else { return }
do {
let result = try await product.purchase()
switch result {
case .success(let verification):
if case .verified(let transaction) = verification {
await transaction.finish()
}
case .pending, .userCancelled:
break
@unknown default:
break
}
} catch {
print("Purchase error: \(error)")
}
}
}
}
⚠️
型の曖昧さ: import StoreKitimport AppCoinsSDK の両方が存在する場合、Swift は ProductTransactionVerificationResult について曖昧さのエラーを出します。必要に応じて、AppCoinsSDK.ProductAppCoinsSDK.TransactionStoreKit.ProductStoreKit.Transaction のようにモジュール名で修飾してください。

ビルド分離での移行

Apple App Store 向けのビルドと Aptoide 向けのビルドを別々に管理する場合は、この方式を使用します。

  1. 既存の StoreKit 2 ターゲットから Aptoide 向けビルドターゲット(またはブランチ)を作成します。

  2. インポートを入れ替えます。

    // Before
    import StoreKit

    // After
    import AppCoinsSDK
  3. 上記の インポートと初期化 のとおり、SceneDelegate.swift または AppDelegate.swiftSDK の初期化を追加します。

  4. 必要であれば、Product.PurchaseResult の switch から @unknown default を削除します。AppCoins の Product.PurchaseResult 列挙型はクローズドであり、未知のケースが追加されることはありません。これは任意であり、残しておいても問題ありません。

  5. transaction.id を読み取っているコードを更新し、型注釈を UInt64 から String に変更します。

    // Before (StoreKit 2)
    let txId: UInt64 = transaction.id

    // After (AppCoins SDK)
    let txId: String = transaction.id

その他のコード変更は不要です。それ以外の呼び出し箇所は、import AppCoinsSDK のままコンパイルできます。