---
title: "Unity SDKでフローからデータを処理する"
description: "AdaptySDKを使用して、ユーザーがUnityアプリのフローで入力したデータを保存・活用します。"
---

> **AI agents**: to search Adapty docs faster and with fewer tokens, install the Adapty skill. Claude Code (self-updating via plugin): `claude plugin marketplace add adaptyteam/adapty-skills && claude plugin install adapty-skills@adapty` — other tools: `npx skills add adaptyteam/adapty-skills --all`

ユーザーが[フロー](adapty-flow-builder)の入力フィールドに入力したり、クイズに回答したり、トグルを切り替えたりすると、SDKはその値をアナリティクスコールバックを通じてアプリに渡します。

アプリでこのデータが使われる主なケースは次のとおりです：

- **ユーザーを自社バックエンドに登録する**: ユーザーがオンボーディングフローで入力したメールアドレスと名前を取得し、フローが閉じたタイミングでアカウントを作成します。
- **回答と設定を保存する**: ユーザーが選択した内容を記録し、後でアプリが活用できるようにします — たとえば、[Adapty プロファイルにカスタム属性として書き込む](unity-setting-user-attributes)など。
- **将来のフローをカスタマイズする**: クイズの回答をカスタム属性として保存し、後のプレースメントでセグメントごとに異なるフローや異なるペイウォールを表示するようにターゲティングします。
- **サードパーティの分析プラットフォームに連携する**: 回答を Amplitude、Mixpanel、またはご利用中のプロダクト分析ツールに転送します。

インプットとセレクタブルグループは、値を自動的にレポートします。コード内でインプットを区別するために、ビルダーで各インプットに意味のある **Element ID** を、各セレクタブルグループに **Group ID** を設定してください。

## 始める前に

以下が必要です：

- **Adapty SDK v4以降**: フローのコールバックは以前のバージョンには存在しません。
- **[フロー＆ペイウォールビルダー](adapty-flow-builder)で作成されたフロー**: コールバックを通じて入力値を報告するのはフローのみです。
- **最近公開されたフローのバージョン**: フローがこの機能が利用可能になった後に公開された場合にのみ、入力値を報告します。アプリに何も届かない場合は、フローの新しいバージョンを公開して再試行してください。

## 入力値を受け取る \{#receive-input-values\}

入力値は、フローからの他のすべてのアナリティクスイベントと同じコールバックに、イベント名 `flow_user_input` として届きます。`Adapty.SetFlowsEventsListener` で登録したリスナーに `FlowViewDidReceiveAnalyticEvent` を実装してください：

```csharp showLineNumbers title="Unity"
public void FlowViewDidReceiveAnalyticEvent(
    AdaptyUIFlowView view,
    string name,
    IReadOnlyDictionary<string, object> parameters
) {
    HandleFlowInput(name, parameters);
}
```

`FlowViewDidReceiveAnalyticEvent` コールバックは、[画面表示](unity-flow-screen-views)を含むフローのすべての分析イベントを配信します。
- `name` パラメーターにはイベント名が含まれます。ユーザー入力イベントをフィルタリングするには、`name` を `flow_user_input` と比較してください。
- `element_type` パラメーターには要素のカテゴリ名が含まれます。
- 入力の値は、要素タイプに応じて異なるパラメーターに格納されます：
     - テキストフィールド、ピッカー、トグルはユーザーの入力を `value` に格納します
     - 選択グループはアクティブなオプションを `item_ids` と `item_titles` で報告します

```csharp showLineNumbers title="Unity"
private void HandleFlowInput(string name, IReadOnlyDictionary<string, object> parameters) {
    if (name != "flow_user_input") return;

    var elementId = parameters["element_id"] as string;

    // The screen the input sits on. Pair it with elementId to tell apart
    // two fields that share an Element ID on different screens.
    var screenId = parameters["instanceId"] as string;

    switch (parameters["element_type"] as string) {
        case "text_input":
        case "email_input":
        case "number_input":
        case "phone_input":
            var text = parameters["value"] as string;
            break;
        case "date_picker":
        case "time_picker":
        case "date_time_picker":
            // Unix time in milliseconds.
            var millis = Convert.ToInt64(parameters["value"]);
            break;
        case "single_choice":
            var optionId = (parameters["item_ids"] as JArray)?.First?.ToString();
            break;
        case "multi_choice":
            var optionIds = (parameters["item_ids"] as JArray)?.ToObject<List<string>>();
            break;
        case "toggle":
            var isOn = (bool)parameters["value"];
            break;
    }
}
```

コールバックが実行されることを確認するには、自分のアプリのテストビルドで入力を操作してください。コールバックハンドラーがイベントを受信しない場合は、[前提条件](#before-you-start)を確認してください。この機能が利用可能になった後にフローが公開されていることを確認してください。

## アプリがインプットを受け取るタイミング \{#when-your-app-receives-the-input\}

:::important
デフォルトの入力値や選択値は、このコールバックを通じてアプリに届くことはありません。ユーザーが **Set as default** とマークされたオプションをそのままにして次に進んだ場合、イベントは発火しません。イベントがないことを「回答なし」と解釈しないでください — ユーザーはデフォルト値をそのままにしただけです。
:::

**以下の要素がこのイベントをトリガーします：**

- テキスト、メール、数値、電話フィールド
- 日付、時刻、日時ピッカー
- 単一選択・複数選択のセレクタブルグループ、トグル

**以下の要素はトリガーしません：**

- パスワードフィールド、プロダクト選択、タブ切り替え
- 複数画面で共有される Header 要素内のインプット
- 重複または欠落したオプションの Element ID を持つセレクタブルグループ、あるいは別の画面で Group ID が再利用されているグループ。このようなグループは、部分的な回答ではなく、何も送信しません。

**イベントがトリガーされるタイミング：**

- フィールドがフォーカスを失ったとき。クリアされたフィールドは空文字列を報告し、ユーザーが一度も編集しなかったフィールドは何も報告しません。プレースホルダーは値ではありません。ユーザーがフィールドに戻って編集し、再びフォーカスを外した場合、2回目のイベントが発生します。
- ユーザーが新しい値を選択した後にピッカーを閉じたとき。値を変えずに閉じた場合は何も報告されません。
- ユーザーがオプションまたはトグルをタップしたとき。複数選択イベントはすべての選択済みオプションを列挙するため、最後の1つを選択解除すると2つの空の配列が送信されます。

**イベントがトリガーされないタイミング：**

- ユーザーが入力中のとき。キーストリームは存在せず、フォーカスが外れた時点でフィールドが保持している値のみが送信されます。
- ユーザーがフローを送信または閉じたとき。その時点で編集中の値は失われる可能性があります。[配信の制限事項](#delivery-and-limitations) セクションでは、最後の画面をこれに合わせて設計する方法を説明しています。
- ユーザーの操作なしに値が設定されたとき。**Set as default** とマークされたオプションは画面が開いたときに事前選択されており、**Set Variable** アクションにより別の操作からオプションを選択したり入力フィールドを埋めたりできます。どちらもイベントを送信しません。事前入力済みの入力は、ユーザーが編集した時点で初めて報告されます。

## 受け取る内容 \{#what-you-receive\}

コールバックは2つのイベントを配信します。`name` でフィルタリングして `flow_user_input` イベントのみを表示してください。レスポンスのJSONペイロードは次のようになります。

```json
{
  "name": "flow_user_input",
  "instanceId": "scr_registration",
  "isBackendEvent": false,
  "isCustomerEvent": true,
  "element_id": "email",
  "element_type": "email_input",
  "value": "jane@example.com"
}
```

| パラメーター      | 説明 |
|:------------------|:------------|
| `name`            | 入力イベントの場合は `flow_user_input`、画面表示の場合は `flow_screen_showed`。 |
| `instanceId`      | 入力要素が属する画面のID。要素IDはフロー全体ではなく、画面内で一意です。フロー内の複数の画面に入力がある場合は、イベントのフィルタリングに `instanceId` と `element_id` を組み合わせて使用してください。 |
| `element_id`      | [入力](builder-inputs-and-forms)の **Element ID**、または[選択可能なグループ](flow-selectable-elements)の **Group ID**。 |
| `element_type`    | イベントを送信した要素の種類。以下のパラメーターのうちどれが入力値を保持するかを決定します。 |
| `value`           | **テキストフィールド、ピッカー、トグルのみ。** 入力値：テキストフィールドの場合は文字列、ピッカーの場合は整数、トグルの場合はブール値。 |
| `item_ids`        | **単一選択・複数選択の選択可能グループのみ。** 選択されたオプションの **Element ID**（ビルダーでの表示順）。単一選択グループでは1件、複数選択グループでは任意の件数。 |
| `item_titles`     | **単一選択・複数選択の選択可能グループのみ。** `item_ids` に記載されたオプションのタイトル（同じ順序）。空になることはありません。タイトルのないオプションはIDが代わりに報告されます。 |
| `isCustomerEvent` | ユーティリティフラグ。このイベントでは常に `true`。SDKがコールバックに配信するイベントを示します。1つのハンドラーがすべてのフローイベントをアナリティクスに転送し、`name` ではなくこのフラグで分岐する場合に便利です。 |
| `isBackendEvent`  | ユーティリティフラグ。このイベントでは常に `false`。AdaptyがAdapty自身のアナリティクス用に記録するイベントを示します。`false` は、ユーザーが入力した内容がアプリにのみ届き、他には渡らないことを確認します。Adaptyはその内容を受信・保存しません。 |

各要素が報告する内容：

| ビルダー上の要素 | `element_type` | 値を格納するパラメーター | 保持する内容 |
|:---------------|:---------------|:----------------------------|:--------------|
| **Text**、**Number**、**Phone number** 入力 | `text_input`、`number_input`、`phone_input` | `value` | ユーザーが入力した生の文字列。数値も文字列として届きます（数値型ではありません）。 |
| **E-mail** 入力 | `email_input` | `value` | ユーザーが入力した生の文字列。ビルダーの書式バリデーションに失敗した場合も含みます。使用前にアプリ側でバリデーションしてください。 |
| **Password** 入力 | なし | なし | イベントを送信しません。 |
| **Date** 入力 | `date_picker` | `value` | 選択された日付のローカル時刻の深夜0時をUnix時間（ミリ秒、整数）で表した値。 |
| **Time** 入力 | `time_picker` | `value` | Unix時間（ミリ秒、整数）で、分未満を切り捨てた値。 |
| **Date & Time** 入力 | `date_picker` と `time_picker` | `value` | 日付ピッカーと時刻ピッカーの2要素。それぞれが個別のイベントを送信します。 |
| **Type** ドロップダウンで **Date & Time** に切り替えた入力 | `date_time_picker` | `value` | Unix時間（ミリ秒、整数）で、分未満を切り捨てた値。 |
| **Single choice** グループ | `single_choice` | `item_ids`、`item_titles` | 2つの配列。`item_ids`：選択されたオプションのElement IDを含む配列。`item_titles`：そのオプションのタイトルを含む配列。 |
| **Multi-choice** グループ | `multi_choice` | `item_ids`、`item_titles` | 2つの配列。`item_ids`：選択されたすべてのオプションのElement ID（ビルダーでの表示順）。`item_titles`：同じ順序でのタイトル。何も選択されていない場合、両方の配列は空になります。 |
| **Toggle** グループ | `toggle` | `value` | ブール値。 |

回答で分岐する場合は、`item_titles` ではなく `item_ids` で比較してください。タイトルは派生値です。設定されていればオプションの **Element Title**、なければデフォルトロケールのテキスト、それもなければElement IDが使用されます。別の言語でフローを閲覧したユーザーには異なるテキストが表示されます。

## イベントの例 \{#event-examples\}

これらの例は、各イベントで使用できるプロパティを示しており、コメントに例示的な値が含まれています。

<Details>
<summary>テキスト、メール、数値、電話入力（クリックして展開）</summary>

```csharp
public void FlowViewDidReceiveAnalyticEvent(
    AdaptyUIFlowView view,
    string name,
    IReadOnlyDictionary<string, object> parameters
) {
    var eventName       = name;                          // "flow_user_input"
    var paramName       = parameters["name"];            // "flow_user_input"
    var screenId        = parameters["instanceId"];      // "scr_J260KU5q"
    var isCustomerEvent = parameters["isCustomerEvent"]; // true
    var isBackendEvent  = parameters["isBackendEvent"];  // false
    var elementId       = parameters["element_id"];      // "email"
    var elementType     = parameters["element_type"];    // "email_input"
    var value           = parameters["value"];           // "jane@example.com"   (string)
}
```
</Details>

<Details>
<summary>日付、時刻、日時ピッカー（クリックして展開）</summary>

```csharp
public void FlowViewDidReceiveAnalyticEvent(
    AdaptyUIFlowView view,
    string name,
    IReadOnlyDictionary<string, object> parameters
) {
    var elementId   = parameters["element_id"];   // "birthday"
    var elementType = parameters["element_type"]; // "date_picker"
    var value       = parameters["value"];        // 645408000000   (long — Unix milliseconds, 1990-06-15, local midnight)
}
```
</Details>

<Details>
<summary>単一選択（クリックして展開）</summary>

```csharp
public void FlowViewDidReceiveAnalyticEvent(
    AdaptyUIFlowView view,
    string name,
    IReadOnlyDictionary<string, object> parameters
) {
    var elementId   = parameters["element_id"];   // "experience"
    var elementType = parameters["element_type"]; // "single_choice"
    var itemIds     = parameters["item_ids"];     // ["pro"]   (JArray)
    var itemTitles  = parameters["item_titles"];  // ["I train professionally"]
}
```
</Details>

<Details>
<summary>複数選択（クリックで展開）</summary>

```csharp
public void FlowViewDidReceiveAnalyticEvent(
    AdaptyUIFlowView view,
    string name,
    IReadOnlyDictionary<string, object> parameters
) {
    var elementId   = parameters["element_id"];   // "interests"
    var elementType = parameters["element_type"]; // "multi_choice"
    var itemIds     = parameters["item_ids"];     // ["sports", "music"]   (JArray)
    var itemTitles  = parameters["item_titles"];  // ["Sports", "Music"]
}
```
</Details>

<Details>
<summary>Toggle (Click to expand)</summary>

```csharp
public void FlowViewDidReceiveAnalyticEvent(
    AdaptyUIFlowView view,
    string name,
    IReadOnlyDictionary<string, object> parameters
) {
    var elementId   = parameters["element_id"];   // "reminders"
    var elementType = parameters["element_type"]; // "toggle"
    var value       = parameters["value"];        // true   (bool)
}
```
</Details>

## 配信と制限事項 \{#delivery-and-limitations\}

:::warning
フローは生の値（メールアドレス、電話番号、ユーザーが入力したその他の情報）を送信します。アナリティクスコールバックが届けるものはすべて個人データとして扱い、メールアドレスを書き残さないような場所には保存しないでください。
:::

- **最後の値が優先されます**: フィールドごとに1つのイベントが届き、キーストロークのストリームではなくユーザーが最終的に入力した値が含まれます。フィールドを編集した場合、最後のバージョンのみが届きます。
- **送信の保証はありません**: ユーザーがフローを進むにつれて値が届きますが、ユーザーはどの時点でも離脱できます。一連の回答が完成したと判断する前に、フローが閉じるのを待ってください。
- **ベストエフォート方式での配信**: フィールドがフォーカスされているときやピッカーが開いているときにユーザーがフローを閉じたりアプリをバックグラウンドに移動したりすると、その値が失われる場合があります。

最後のフィールドの値を確実に受け取るには、入力フィールドもピッカーもない画面でフローを終了し、その画面が表示されたときに自動的に閉じるのではなく、明示的なユーザー操作によってフローを閉じてください。最後の画面に移動すると前のフィールドからフォーカスが外れ、それが値の送信をトリガーします。

リスナーが受け取った入力値はそれぞれ保存しておき、フローが閉じるときに完全なセットを送信してください。そのタイミングを捉えるには、同じリスナーに `FlowViewDidDisappear` を実装します。このメソッドは、ユーザーがフローを最後まで完了した場合でも途中で閉じた場合でも、フロービューが非表示になった時点で実行されます。

## ユースケース \{#use-cases\}

### バックエンドへのユーザー登録 \{#register-users-on-your-backend\}

フローが閉じたときに一度のリクエストで完全な回答セットを送信できるよう、値が届き次第収集しておきましょう。

ユーザーがフローを完了した場合でも、途中で離脱した場合でも、ビューは消えます。バックエンドを呼び出す前に、必要なフィールドが揃っているか確認してください。

フローはバックエンドからのエラーを表示できません。コールバックに戻り値はなく、実行中のフローにデータを送り込むSDKのメソッドも存在しません。たとえばメールアドレスがすでに使用中でregistration に失敗した場合は、フローが閉じた後に独自のUIでエラーを表示してください。

```csharp showLineNumbers title="Unity"
private readonly Dictionary<string, string> flowAnswers = new Dictionary<string, string>();

public void FlowViewDidReceiveAnalyticEvent(
    AdaptyUIFlowView view,
    string name,
    IReadOnlyDictionary<string, object> parameters
) {
    if (name != "flow_user_input") return;
    if (!parameters.TryGetValue("value", out var raw) || !(raw is string value)) return;

    var elementId = parameters["element_id"] as string;
    flowAnswers[elementId] = value;
}

public void FlowViewDidDisappear(AdaptyUIFlowView view) {
    if (!flowAnswers.ContainsKey("email")) return;

    // Send flowAnswers to your backend here to create the account.

    flowAnswers.Clear();
}
```

### ユーザープロファイルにデータを紐付ける \{#enrich-user-profiles-with-data\}

ユーザーが入力した内容をプロファイルに紐付け、同じ情報を二度聞かないようにするには、値が入力されるたびに[ユーザープロファイルを更新](unity-setting-user-attributes)してください。

たとえば、フローにElement ID `name` のテキスト入力とElement ID `email` のメール入力がある場合：

```csharp showLineNumbers title="Unity"
private void HandleFlowInput(string name, IReadOnlyDictionary<string, object> parameters) {
    if (name != "flow_user_input") return;
    if (!parameters.TryGetValue("value", out var raw) || !(raw is string value)) return;

    var builder = new AdaptyProfileParameters.Builder();

    switch (parameters["element_id"] as string) {
        case "name":
            builder = builder.SetFirstName(value);
            break;
        case "email":
            builder = builder.SetEmail(value);
            break;
        default:
            return;
    }

    Adapty.UpdateProfile(builder.Build(), (error) => {
        if (error != null) {
            // handle the error
        }
    });
}
```

### 後から表示するフローをカスタマイズする \{#customize-flows-shown-later\}

クイズの回答によって、後のタイミングで表示される[プレースメント](placements)のコンテンツも変えられます — 別のフローや、同じプレースメント内の別のペイウォールを表示できます。

例えば、オンボーディングフローでスポーツの経験について質問し、各グループにそれぞれ異なるプロダクトとコピーを持つフローを表示するといった使い方ができます。

1. フローに[クイズ](onboarding-quizzes)を追加します。[セレクタブルグループ](flow-selectable-elements)のグループIDに `experience` を設定し、各オプションにわかりやすいエレメントIDを付けます。
2. 回答を処理し、ユーザーに[カスタム属性を設定](unity-setting-user-attributes)します。

```csharp showLineNumbers title="Unity"
private void HandleFlowInput(string name, IReadOnlyDictionary<string, object> parameters) {
    if (name != "flow_user_input") return;
    if ((parameters["element_id"] as string) != "experience") return;

    var optionId = (parameters["item_ids"] as JArray)?.First?.ToString();
    if (optionId == null) return;

    var builder = new AdaptyProfileParameters.Builder();
    // Set the custom attribute 'experience' to the option the user selected
    // (beginner, amateur, or pro).
    builder = builder.SetCustomStringAttribute("experience", optionId);

    Adapty.UpdateProfile(builder.Build(), (error) => {
        if (error != null) {
            // handle the error
        }
    });
}
```

3. 各カスタム属性値に対して[セグメントを作成](segments)します。
4. [プレースメントを作成](placements)し、各セグメントに対して[オーディエンス](audience)を追加します。
5. アプリ内でそのプレースメントの[フローを表示](unity-present-paywalls)します。