# Configure Flows Locally and Run Step by Step

This iOS SDK integration pattern gives you full control over the onboarding experience. Instead of passing a single flow to the SDK and waiting for it to finish, you create an onboarding session, split the flow into sections, and run each section with `startOnboardingSection`. Control returns to your app between sections, so you can show your own screens, make decisions, or call your back end before continuing.

Use this pattern when you need to insert your own UI between capture steps, group modules into logical sections, or branch based on intermediate results. If you prefer to define the flow in Dashboard instead, see [Run Flows Configured in Dashboard](https://developer.incode.com/docs/ios-run-flows-configured-in-dashboard). If you want full control over the flow in client code, see [Configure Flows Locally and Run End to End](https://developer.incode.com/docs/ios-configure-locally-end-to-end). For more information about these patterns, see [Integration Approaches](https://developer.incode.com/docs/ios-flow-configuration).

This page covers session creation and section execution only. Ensure you have followed the steps to [install and initialize](https://developer.incode.com/docs/setup-ios) the Incode iOS SDK first.

***

## Set Up This Integration Pattern

Complete the following steps in order.

### Create a New Onboarding Session

Before running any section, create a session with `setupOnboardingSession`. The result is delivered through a completion closure; there is no session listener.

```swift
IncdOnboardingManager.shared.setupOnboardingSession(
    sessionConfig: IncdOnboardingSessionConfiguration(token: "<SESSION_TOKEN>")
) { result in
    // result.token, result.interviewId, result.error
}
```

`OnboardingSessionResult` exposes `token`, `interviewId`, and `error`. You can optionally supply a list of `OnboardingValidationModule` values through `IncdOnboardingSessionConfiguration.validationModules` to determine which modules count toward the onboarding score. If omitted, the default modules `.id`, `.liveness`, and `.faceRecognition` are used.

### Create a Delegate to Receive SDK Results

Conform to `IncdOnboardingDelegate` to receive results from each module and the overall flow. Only `onSuccess()` and `onError(_:)` are required. Every other method has a default empty implementation. Override only the events for the modules your flow includes.

```swift
extension MyViewController: IncdOnboardingDelegate {
    func onOnboardingSessionCreated(_ result: OnboardingSessionResult) {
        // Session created: result.token, result.interviewId
    }

    func onIdProcessed(_ result: IdProcessResult) {
        // ID validation completed
    }

    func onSelfieScanCompleted(_ result: SelfieScanResult) {
        // Selfie scan completed
    }

    func onFaceMatchCompleted(_ result: FaceMatchResult) {
        // Face match completed
    }

    func onOnboardingSectionCompleted(_ flowTag: String) {
        // Section finished — safe place to start the next section
    }

    func userCancelledSession() {
        // User cancelled the flow
    }

    func onSuccess() {
        // Onboarding finished successfully
    }

    func onError(_ error: IncdFlowError) {
        // Onboarding aborted due to an error
    }
}
```

For the shape and fields of each result object, see the corresponding page in [Modules](https://developer.incode.com/docs/ios-individual-modules).

### Run a Section

Build an `IncdOnboardingFlowConfiguration` for the section, add the modules it should run, and start it with `startOnboardingSection`. The section is tagged at call time with the `sectionTag` parameter instead of on the builder.

```swift
let section = IncdOnboardingFlowConfiguration()
section.addIntro(checks: [])
section.addPhone()
section.addIdScan(scanStep: .both)

IncdOnboardingManager.shared.startOnboardingSection(
    flowConfig: section,
    sectionTag: "id",
    delegate: self
)
```

Only one section can run at a time. Starting a second section while one is still running fails with `IncdFlowError.sectionAlreadyRunning`, which carries the tag of the section that is already active.

### Start Subsequent Sections from the Section-Complete Callback

`IncdOnboardingDelegate` reports the end of a section through `onOnboardingSectionCompleted(_ flowTag:)`. This is the documented signal that a section has finished, so start the next section from this callback rather than from an individual module callback such as `onIdFrontCompleted(_:)`.

```swift
func onOnboardingSectionCompleted(_ flowTag: String) {
    startNextSection()   // OK here
}

func startNextSection() {
    let next = IncdOnboardingFlowConfiguration()
    next.addSelfieScan()
    next.addFaceMatch(matchType: .idSelfie, idCategory: .primary)
    IncdOnboardingManager.shared.startOnboardingSection(
        flowConfig: next,
        sectionTag: "face",
        delegate: self
    )
}
```

### Finish the Flow and Delete Local Data

When you drive the flow section by section, call `finishFlow` after the last section to close the session on the server. Then call `deleteLocalUserData()` to remove all local user data. Both are instance methods on `IncdOnboardingManager.shared`.

```swift
IncdOnboardingManager.shared.finishFlow { success, error in
    IncdOnboardingManager.shared.deleteLocalUserData()
}
```

Deleting local user data is also recommended from the terminal delegate callbacks `onSuccess()`, `onError(_:)`, and `userCancelledSession()`.

### Resume an Existing Session

To resume a session—for example, after the app is relaunched mid-flow—create an `IncdOnboardingSessionConfiguration` with the existing `interviewId` or `token` and call `setupOnboardingSession` before any other API call.

```swift
let sessionConfig = IncdOnboardingSessionConfiguration(
    validationModules: [.id, .faceRecognition, .liveness],
    interviewId: "<EXISTING_INTERVIEW_ID>"
)

IncdOnboardingManager.shared.setupOnboardingSession(sessionConfig: sessionConfig) { result in
    // Safe to run sections again
}
```

<br />
