# Connect existing onboarding

Keep your native screens. Register a flow, request an assignment, and measure the moments your users actually see.

## Before you start

Complete the SDK quickstart and verify one real event in the selected application's Charts. Keep native A and B branches in the app binary before starting an experiment. Connecting a flow does not upload or translate arbitrary native source code.

## 1. Register your flow

1. Open Flows → Onboarding for the selected application.
2. Create a flow using Connect existing flow.
3. Register stable step IDs that match the identifiers in your native code.
4. Save and publish the configuration, then copy the flow ID from the saved flow.
5. Keep the flow and SDK key in the same application.

## 2. Choose a shipped branch

### Swift

```
let assignment = try await client.assignment(flowID: "YOUR_FLOW_ID")
// Route to your already-shipped A or B onboarding.
// Retain this assignment for its exposure and subsequent events.
let showVariantB = assignment.variant == "B"
```

### Kotlin

```
// Background thread; retain the result for later callbacks.
val assignment = client.assignment(flowId = "YOUR_FLOW_ID")
val showVariantB = assignment.variant == "B"
```

> **Fetching is not exposure**
> An assignment request does not mean the user saw a variant. Invoke expose only when the assigned onboarding is actually visible.

## 3. Instrument real callbacks

### Swift

```
// When the assigned onboarding is visible:
try await client.expose(assignment)
try await client.track(
    name: "onboarding_step_viewed",
    stepID: "welcome", assignment: assignment
)
// When this step is completed:
try await client.track(
    name: "onboarding_step_completed",
    stepID: "welcome", assignment: assignment
)
// Only when the entire flow is completed:
try await client.track(name: "onboarding_completed", assignment: assignment)
try await client.flush()
```

### Kotlin

```
// On the appropriate real lifecycle callbacks:
client.expose(assignment)
client.track(name = "onboarding_step_viewed", stepId = "welcome", assignment = assignment)
client.track(name = "onboarding_step_completed", stepId = "welcome", assignment = assignment)
client.track(name = "onboarding_completed", assignment = assignment)
// Deliver on a background thread:
client.flush()
```

Pass the same assignment through the journey so experiment_id and variant remain consistent. Guard UI callbacks against unintended duplicate views or completions. SDK transport deduplication reuses persisted event IDs; it cannot infer that two independently tracked callbacks represent the same action.

## 4. Verify before experimenting

- Confirm the flow's step IDs match the app callbacks exactly.
- Open each native branch and verify step views/completions in the right app.
- Ensure no exposure is sent before the assigned UI appears.
- Use a bundled Control A fallback when network and cache are unavailable.
- Only start the experiment after both variants and the primary goal can be measured.

## Next steps

- [Run an experiment](https://aplyna.com/docs/experiments): Traffic allocation, primary goals and results.
- [Event contract](https://aplyna.com/docs/events): Understand required fields and attribution.
