---
title: "iOS SDKでFlow Builderを使ってアプリ内課金を有効にする"
description: "Adapty Flow Builderでアプリ内課金を有効にするためのクイックスタートガイド。"
---

アプリ内課金を有効にするには、3つの重要な概念を理解する必要があります。
- [**プロダクト**](product) – ユーザーが購入できるもの（サブスクリプション、消耗型アイテム、永続アクセス）
- [**フロー**](adapty-flow-builder) – ノーコードのフロービルダーで作成した、プロダクトをユーザーに提示するための画面シーケンス。SDKは`getFlow`を使って取得します。独自のコードでUIを構築したい場合は、代わりにペイウォールを使用してください — [ペイウォールを手動で実装する](ios-quickstart-manual)を参照。
- [**プレースメント**](placements) – アプリ内でフローをどこで・いつ表示するか（`main`、`onboarding`、`settings`など）。ダッシュボードでフローをプレースメントに紐付け、コード内ではプレースメントIDで取得します。これにより、A/B テストの実施や、ユーザーごとに異なるフローの表示が簡単に行えます。
Adapty では、アプリ内での購入を有効にする方法を3つ提供しています。アプリの要件に応じて、いずれかを選択してください。
| 実装方法 | 複雑さ | 使用する場面 |
|---|---|---|
| Adapty フロービルダー | ✅ 簡単 | [ノーコードビルダーで購入対応済みのフローを作成します](quickstart-paywalls)。Adapty が自動的にレンダリングし、購入フロー・レシート検証・サブスクリプション管理をすべて裏側で処理します。 |
| 手動作成のペイウォール | 🟡 普通 | アプリコードでペイウォール UI を実装しますが、プロダクトラインナップの柔軟性を保つために Adapty からフローオブジェクトを取得します。[ガイド](ios-quickstart-manual)をご覧ください。 |
| オブザーバーモード | 🔴 難しい | 独自の購入処理インフラが既にあり、それを引き続き使用したい場合に選択します。オブザーバーモードには Adapty における制限事項があります。[こちらの記事](observer-vs-full-mode)をご覧ください。 |
:::important
**以下の手順は、Adapty フロービルダーで作成したフローを実装する方法を説明しています。**

ペイウォールの UI を自分でビルドしたい場合は、[ペイウォールを手動で実装する](ios-quickstart-manual)を参照してください。
:::

Adapty フロービルダーで作成したフローを表示するには、アプリのコードで以下の作業を行うだけです。

1. **フローを取得する**: Adapty からフローを取得します。
2. **表示する（購入処理は Adapty が担当）**: アプリにビューを表示します。
3. **ボタンアクションを処理する**: ユーザーの操作に対するアプリの応答を関連付けます。たとえば、ユーザーがボタンをタップしたときにリンクを開いたり、フローを閉じたりします。
## 始める前に \{#before-you-start\}

始める前に、以下の手順を完了してください：

1. Adapty ダッシュボードで[アプリを App Store に接続する](initial_ios)。
2. Adapty で[プロダクトを作成する](create-product)。
3. [フローを作成してプロダクトを追加する](create-paywall)。
4. [プレースメントを作成してフローを追加する](create-placement)。
5. アプリのコードに [Adapty SDK をインストールして有効化する](sdk-installation-ios)。このガイドでは Adapty iOS SDK v4 の API を使用します。

## 1. フローを取得する \{#1-get-the-flow\}

フローはダッシュボードで設定されたプレースメントに関連付けられています。プレースメントを使うと、オーディエンスごとに異なるフローを表示したり、[A/B テスト](ab-tests)を実施したりすることができます。

Adapty フロービルダーで作成したフローを取得するには、以下の手順を行います。

1. `getFlow` メソッドを使って[プレースメント](placements) ID から `flow` オブジェクトを取得し、ビュー設定が含まれているか確認します。
2. `getFlowConfiguration` メソッドを使ってビュー設定を取得します。ビュー設定には、フローを表示するために必要な UI 要素とスタイリング情報が含まれています。
```swift

func loadFlow() async {
    let flow = try await Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID")

    guard flow.hasViewConfiguration else {
        print("Flow doesn't have a view configuration")
        return
    }

    flowConfiguration = try await AdaptyUI.getFlowConfiguration(forFlow: flow)
}
```

## 2. フローを表示する \{#display-the-flow\}

フローの設定が完了したら、あとは数行のコードを追加するだけでフローを表示できます。

<Tabs groupId="current-os" queryString>

<TabItem value="swiftui" label="SwiftUI" default>

SwiftUI でフローを表示する際は、イベントの処理も必要です。`didFinishPurchase`、`didFailPurchase`、`didFinishRestore`、`didFailRestore`、`didReceiveError` は必須です。テスト時は、以下のスニペットのコードをそのままコピーしてイベントをログに記録できます。
:::tip
フローは購入が成功しても自動的に閉じません。`didFinishPurchase` で表示バインディングを `false` に設定して閉じるか、何もしないでフローを次の画面に進めることができます。
:::
```swift
.flow(
    isPresented: $flowPresented,
    flowConfiguration: flowConfiguration,
    didFinishPurchase: { product, purchaseResult in
        print("Purchase finished successfully")
        flowPresented = false // or do nothing to let the flow continue
    },
    didFailPurchase: { product, error in
        print("Purchase failed: \(error)")
    },
    didFinishRestore: { profile in
        print("Restore finished successfully")
    },
    didFailRestore: { error in
        print("Restore failed: \(error)")
    },
    didReceiveError: { error in
        flowPresented = false
        print("Flow error: \(error)")
    }
)
```
</TabItem>
<TabItem value="uikit" label="UIKit" default>

```swift

func presentFlow(with config: AdaptyUI.FlowConfiguration) {
    let flowController = try AdaptyUI.flowController(
        with: config,
        delegate: self
    )
    present(flowController, animated: true)
}
```
`AdaptyFlowControllerDelegate` を実装してイベントを処理します。デフォルト実装がない4つのメソッドは最低限実装してください。購入が成功してもコントローラーは自動的に閉じないことに注意してください。`didFinishPurchase` で閉じるか、フローが次の画面に進むようにそのままにしてください。
```swift
extension YourViewController: AdaptyFlowControllerDelegate {
    func flowController(_ controller: AdaptyFlowController,
                        didFinishPurchase product: AdaptyPaywallProduct,
                        purchaseResult: AdaptyPurchaseResult) {
        if !purchaseResult.isPurchaseCancelled {
            controller.dismiss(animated: true) // or do nothing to let the flow continue
        }
    }

    func flowController(_ controller: AdaptyFlowController,
                        didFailPurchase product: AdaptyPaywallProduct,
                        error: AdaptyError) {
        print("Purchase failed: \(error)")
    }

    func flowController(_ controller: AdaptyFlowController,
                        didFinishRestoreWith profile: AdaptyProfile) {
        print("Restore finished successfully")
    }

    func flowController(_ controller: AdaptyFlowController,
                        didFailRestoreWith error: AdaptyError) {
        print("Restore failed: \(error)")
    }
}
```
</TabItem>
</Tabs>
:::info
フローの表示方法の詳細については、[ガイド](ios-present-paywalls)をご参照ください。
:::
## 3. ボタンアクションの処理 \{#handle-button-actions\}

ユーザーがボタンをタップすると、iOS SDK は購入・復元・フローのクローズ・リンクの開閉を自動的に処理します。

ただし、カスタムまたは事前定義された ID を持つボタンについては、コード側でアクションを処理する必要があります。また、デフォルトの動作をオーバーライドしたい場合もあるでしょう。

たとえば、閉じるボタンの処理方法を以下に示します。UIKit では、`.close` が発火した際に SDK が自動的にコントローラーを閉じます。カスタムの動作が必要な場合のみオーバーライドしてください。SwiftUI では、`isPresented` バインディングを自分で `false` に設定する必要があります。
:::tip
ボタンの[アクション](handle-paywall-actions)や[イベント](ios-handling-events)の処理方法についてはガイドをご覧ください。
:::

<Tabs groupId="current-os" queryString>

<TabItem value="swiftui" label="SwiftUI" default>
```swift
.flow(
    isPresented: $flowPresented,
    flowConfiguration: flowConfiguration,
    didPerformAction: { action in
        switch action {
            case .close:
                flowPresented = false // dismiss the flow when the user taps close
            default:
                break
        }
    },
    didFinishPurchase: { product, purchaseResult in flowPresented = false },
    didFailPurchase: { product, error in /* handle the error */ },
    didFinishRestore: { profile in /* check access level and dismiss */ },
    didFailRestore: { error in /* handle the error */ },
    didReceiveError: { error in flowPresented = false }
)
```
</TabItem>
<TabItem value="uikit" label="UIKit" default>

```swift
extension YourViewController: AdaptyFlowControllerDelegate {
    func flowController(_ controller: AdaptyFlowController,
                        didPerform action: AdaptyUI.Action) {
        switch action {
            case .close:
                controller.dismiss(animated: true) // default behavior — override only if needed
            default:
                break
        }
    }
}
```
</TabItem>
</Tabs>
## 次のステップ \{#next-steps\}

:::tip
ご質問やお困りのことがあれば、[サポートフォーラム](https://adapty.featurebase.app/)をご覧ください。よくある質問への回答を見つけたり、ご自身の質問を投稿することができます。チームとコミュニティがサポートいたします！
:::

フローをアプリで表示する準備ができました。[サンドボックスモードで購入をテスト](test-purchases-in-sandbox)して、テスト購入が正常に完了できることを確認してください。

次に、適切なユーザーにフローを表示したり有料機能へのアクセスを付与したりするために、[ユーザーのアクセスレベルを確認](ios-check-subscription-status)する必要があります。

## 完全なコード例 \{#full-example\}

このガイドのすべての手順をアプリに統合する方法を以下に示します。

<Tabs groupId="current-os" queryString>

<TabItem value="swiftui" label="SwiftUI" default>
```swift

struct ContentView: View {
    @State private var flowPresented = false
    @State private var flowConfiguration: AdaptyUI.FlowConfiguration?
    @State private var isLoading = false
    @State private var hasInitialized = false

    var body: some View {
        VStack {
            if isLoading {
                ProgressView("Loading...")
            } else {
                Text("Your App Content")
            }
        }
        .task {
            guard !hasInitialized else { return }
            await initializeFlow()
            hasInitialized = true
        }
        .flow(
            isPresented: $flowPresented,
            flowConfiguration: flowConfiguration,
            didPerformAction: { action in
                switch action {
                case .close:
                    flowPresented = false
                default:
                    break
                }
            },
            didFinishPurchase: { product, purchaseResult in
                print("Purchase finished successfully")
                flowPresented = false // or do nothing to let the flow continue
            },
            didFailPurchase: { product, error in
                print("Purchase failed: \(error)")
            },
            didFinishRestore: { profile in
                print("Restore finished successfully")
            },
            didFailRestore: { error in
                print("Restore failed: \(error)")
            },
            didReceiveError: { error in
                print("Flow error: \(error)")
                flowPresented = false
            }
        )
    }

    private func initializeFlow() async {
        isLoading = true
        defer { isLoading = false }

        await loadFlow()
        flowPresented = true
    }

    private func loadFlow() async {
        do {
            let flow = try await Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID")
            guard flow.hasViewConfiguration else {
                print("Flow doesn't have a view configuration")
                return
            }
            flowConfiguration = try await AdaptyUI.getFlowConfiguration(forFlow: flow)
        } catch {
            print("Failed to load: \(error)")
        }
    }
}
```
</TabItem>
<TabItem value="uikit" label="UIKit" default>
```swift

class ViewController: UIViewController {
    private var flowConfiguration: AdaptyUI.FlowConfiguration?

    override func viewDidLoad() {
        super.viewDidLoad()

        Task {
            await initializeFlow()
        }
    }

    private func initializeFlow() async {
        do {
            flowConfiguration = try await loadFlow()

            if let flowConfiguration {
                await MainActor.run {
                    presentFlow(with: flowConfiguration)
                }
            }
        } catch {
            print("Error initializing: \(error)")
        }
    }

    private func loadFlow() async throws -> AdaptyUI.FlowConfiguration? {
        let flow = try await Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID")

        guard flow.hasViewConfiguration else {
            print("Flow doesn't have a view configuration")
            return nil
        }

        return try await AdaptyUI.getFlowConfiguration(forFlow: flow)
    }

    private func presentFlow(with config: AdaptyUI.FlowConfiguration) {
        guard let flowController = try? AdaptyUI.flowController(
            with: config,
            delegate: self
        ) else { return }

        present(flowController, animated: true)
    }
}

extension ViewController: AdaptyFlowControllerDelegate {
    func flowController(_ controller: AdaptyFlowController,
                        didFinishPurchase product: AdaptyPaywallProduct,
                        purchaseResult: AdaptyPurchaseResult) {
        if !purchaseResult.isPurchaseCancelled {
            controller.dismiss(animated: true) // or do nothing to let the flow continue
        }
    }

    func flowController(_ controller: AdaptyFlowController,
                        didFailPurchase product: AdaptyPaywallProduct,
                        error: AdaptyError) {
        print("Purchase failed for \(product.vendorProductId): \(error)")

        guard error.adaptyErrorCode != .paymentCancelled else { return }

        let message = switch error.adaptyErrorCode {
        case .paymentNotAllowed:
            "Purchases are not allowed on this device."
        default:
            "Purchase failed. Please try again."
        }

        let alert = UIAlertController(title: "Purchase Error", message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }

    func flowController(_ controller: AdaptyFlowController,
                        didFinishRestoreWith profile: AdaptyProfile) {
        print("Restore finished successfully")
        controller.dismiss(animated: true)
    }

    func flowController(_ controller: AdaptyFlowController,
                        didFailRestoreWith error: AdaptyError) {
        print("Restore failed: \(error)")
    }

    func flowController(_ controller: AdaptyFlowController,
                        didReceiveError error: AdaptyUIError) {
        print("Flow error: \(error)")
        controller.dismiss(animated: true)
    }
}
```
</TabItem>
</Tabs>