> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getflowpilot.io/llms.txt
> Use this file to discover all available pages before exploring further.

# API reference

> The public types, methods, and options of the FlowPilot iOS SDK, grouped by area, with links to the guide pages that show usage.

This is the lookup reference for the FlowPilot iOS SDK. Every symbol here is declared `public` in the SDK source. The guide pages (Getting started through Offline and performance) teach how to use them in context; this page is the index. Internal types are not listed, because you cannot call them.

<Note>
  This reference reflects **FlowPilot iOS SDK v1.0.0** (`FlowPilotSDK.version`). Public symbols can change between versions, so check the version string in your installed package if a signature here does not match.
</Note>

## Setup

Configure the SDK once at launch, then reach everything else through `FlowPilot.shared`. See [Configuration](/ios-sdk/configuration).

```swift theme={null}
// Configure the SDK. Pass a FlowPilotConfiguration. No-ops (logs an error) if
// the API key does not start with "fp_", leaving FlowPilot.shared nil.
public static func configure(_ configuration: FlowPilotConfiguration)

// The shared instance. nil until configure(_:) succeeds.
public private(set) static var shared: FlowPilot?

// Merge new key/value pairs into the SDK context for future resolves.
public func updateContext(_ context: SDKContext)
```

### `FlowPilotConfiguration`

A `public struct`. Only `apiKey` and `appId` are required; everything else has a default.

| Parameter                | Type                          | Default             | Notes                                                                                      |
| ------------------------ | ----------------------------- | ------------------- | ------------------------------------------------------------------------------------------ |
| `apiKey`                 | `String`                      | required            | Workspace SDK key. Must start with `fp_`.                                                  |
| `appId`                  | `String`                      | required            | The app's FlowPilot App ID.                                                                |
| `environment`            | `FlowPilotEnvironment`        | `.production`       | Picks the base URL.                                                                        |
| `context`                | `SDKContext?`                 | `nil`               | Initial SDK context for variable resolution and targeting.                                 |
| `cachingEnabled`         | `Bool`                        | `true`              | Disk cache for resolved flows.                                                             |
| `cacheDirectory`         | `String?`                     | `nil`               | Custom flow cache directory.                                                               |
| `resolveTimeout`         | `TimeInterval`                | `4.0`               | Hard wall-clock deadline for a resolve. Clamped to a minimum of `0.5`.                     |
| `bundledFlows`           | `[String: String]`            | `[:]`               | Placement key to main-bundle JSON resource name.                                           |
| `bundledFlowAssets`      | `[String: BundledFlowAssets]` | `[:]`               | Offline image/font assets per placement.                                                   |
| `mediaPreloadingEnabled` | `Bool`                        | `true`              | Preload images in screen order when a session starts.                                      |
| `imageMemoryCacheSize`   | `Int`                         | `50 * 1024 * 1024`  | Image memory cache cap, in bytes (50MB).                                                   |
| `imageDiskCacheSize`     | `Int`                         | `200 * 1024 * 1024` | Image disk cache cap, in bytes (200MB).                                                    |
| `debugMode`              | `Bool?`                       | `nil`               | Debug override.                                                                            |
| `logLevel`               | `FlowPilotLogLevel`           | `.error`            | SDK log verbosity.                                                                         |
| `prefetchOnLaunch`       | `[String]`                    | `[]`                | Placement keys to warm in the background after `configure(_:)`. No-op when caching is off. |
| `prefetchMediaStrategy`  | `PrefetchMediaStrategy`       | `.firstScreen`      | How aggressively launch prefetch (and `prefetch(_:warmMedia: true)`) warms images.         |

### `FlowPilotEnvironment`

```swift theme={null}
public enum FlowPilotEnvironment: Sendable, Equatable {
    case development   // https://dev-api.flowpilot.io/v1
    case staging       // https://staging-api.flowpilot.io/v1
    case production    // https://api.flowpilot.io/v1
    case custom(url: String)
}
```

### `FlowPilotLogLevel`

```swift theme={null}
public enum FlowPilotLogLevel: Int, Comparable, Sendable {
    case none = 0
    case error = 1
    case warn = 2
    case info = 3
    case debug = 4
    case verbose = 5
}
```

### `PrefetchMediaStrategy`

```swift theme={null}
public enum PrefetchMediaStrategy: Sendable {
    case none          // JSON + fonts only
    case firstScreen   // + first-screen & persistent-zone images (default)
    case allScreens    // + every screen's images
}
```

Set via `FlowPilotConfiguration.prefetchMediaStrategy`. Governs launch prefetch (`prefetchOnLaunch`) and bounds the screen window when a `prefetch(_:warmMedia: true)` call opts into media warming. See [Prefetching](/ios-sdk/prefetching).

### `SDKContext`

```swift theme={null}
// Host-provided key/value context for variable resolution and targeting.
public typealias SDKContext = [String: Any]
```

## Presenting

Methods on `FlowPilot.shared`. The `presentPlacement` overloads are UIKit-only (`#if canImport(UIKit)`). See [Presenting placements](/ios-sdk/presenting-placements), [SwiftUI integration](/ios-sdk/swiftui), and [Prefetching](/ios-sdk/prefetching).

```swift theme={null}
// Resolve and present (UIKit). Returns the result.
@MainActor
public func presentPlacement(
    _ placementKey: String,
    from viewController: UIViewController,
    options: PresentationOptions? = nil
) async throws -> FlowResult

// Resolve and present (UIKit), with a completion closure instead of a return.
@MainActor
public func presentPlacement(
    _ placementKey: String,
    from viewController: UIViewController,
    options: PresentationOptions? = nil,
    completion: ((FlowResult) -> Void)? = nil
) async throws

// Never-throwing present (UIKit). Walks the full fallback chain, then presents
// your `fallback` view controller if FlowPilot has nothing to show.
@MainActor
@discardableResult
public func presentPlacement(
    _ placementKey: String,
    from viewController: UIViewController,
    fallback: @escaping @MainActor () -> UIViewController,
    options: PresentationOptions? = nil
) async -> FlowResult

// SwiftUI: build a session you drive with FlowPresenterView / .flowPresenter.
@MainActor
public func createSession(
    placementKey: String,
    additionalContext: SDKContext? = nil,
    preloadMedia: Bool? = nil
) async throws -> FlowSession

// SwiftUI: non-throwing counterpart. Returns nil when nothing is presentable.
@MainActor
public func resolveSession(
    placementKey: String,
    additionalContext: SDKContext? = nil,
    preloadMedia: Bool? = nil
) async -> FlowSession?

// True if a flow is cached or resolves for this placement. Not @MainActor.
// Routes through the cache-populating path, so the resolve is not wasted: a
// following present hits the cache.
public func isPlacementReady(_ placementKey: String) async -> Bool

// Warm the cache for one or more placements. Best-effort, concurrent, never
// throws; returns a per-placement outcome map. Pass warmMedia: true to also warm
// images (bounded by prefetchMediaStrategy). @discardableResult.
@discardableResult
public func prefetch(
    _ placementKeys: [String],
    warmMedia: Bool? = nil
) async -> [String: PrefetchOutcome]
```

### `PrefetchOutcome`

```swift theme={null}
public struct PrefetchOutcome: Sendable {
    public enum State: Sendable {
        case warmed(fromCache: Bool)   // a presentable flow is cached and ready
        case noFlow                    // resolve ok, backend has nothing to show
        case failed(FlowPilotError)    // resolve failed; nothing warmed
    }
    public let placementKey: String
    public let state: State
    public let mediaWarmed: Bool       // first/all-screen images warmed
    public var isWarmed: Bool          // convenience: true when state is .warmed
}
```

Returned per placement by `prefetch(_:warmMedia:)`. See [Prefetching](/ios-sdk/prefetching).

### `PresentationOptions`

```swift theme={null}
public struct PresentationOptions: Sendable {
    public let additionalContext: SDKContext?
    public let presentationStyle: PresentationStyle?
    public let animated: Bool

    public init(
        additionalContext: SDKContext? = nil,
        presentationStyle: PresentationStyle? = nil,
        animated: Bool = true
    )
}
```

### `PresentationStyle`

```swift theme={null}
public enum PresentationStyle: String, Sendable {
    case fullScreen
    case modal
    case bottomSheet
}
```

<Warning>
  `presentationStyle` is accepted by `PresentationOptions` but is **not applied** in the current SDK build. Flows present full screen (`FlowHostingController` forces `.fullScreen`). Setting `.modal` or `.bottomSheet` has no effect today. See [Presenting placements](/ios-sdk/presenting-placements).
</Warning>

### `PlacementInfo`

```swift theme={null}
public struct PlacementInfo: Sendable {
    public let placementId: String
    public let placementKey: String
    public let willShow: Bool
    public let flowId: String?
    public let experimentId: String?
    public let variantId: String?
}
```

<Note>
  `PlacementInfo` is declared `public`, but no public API returns it in v1.0.0. To check whether a placement will show, use `isPlacementReady(_:)` instead.
</Note>

## Session

`FlowSession` is the live, observable handle to a presented flow. It is a `public final class` conforming to `ObservableObject` and `Identifiable`. See [SwiftUI integration](/ios-sdk/swiftui), [Variables and SDK context](/ios-sdk/variables-and-context), and [Media preloading](/ios-sdk/media-preloading).

### Published state

```swift theme={null}
public private(set) var flow: ResolvedFlow
public let placementId: String?

@Published public private(set) var currentScreen: ScreenNode?
@Published public private(set) var isActive: Bool
@Published public private(set) var mediaPreloadProgress: Double   // 0.0 ... 1.0
@Published public private(set) var isMediaPreloaded: Bool

public var id: String   // Identifiable
```

`currentScreen` exposes a `ScreenNode` whose public fields are `id`, `kind`, `name`, `screenType`, `props`, and `layout`.

### Methods

```swift theme={null}
// Variables
public func getVariable(_ key: String) -> VariableValue?
public func getAllVariables() -> [String: VariableValue]
@discardableResult public func setVariable(_ key: String, value: VariableValue) -> Bool
public func resetVariables(sdkContext: SDKContext? = nil)

// Media preloading
public func waitForMediaPreload() async
public func cancelMediaPreload()

// Conversions
public func trackConversion(
    amount: Double,
    currency: String,
    productId: String? = nil,
    metadata: [String: Any]? = nil
)

// Lifecycle
public func startNavigation()
public func waitForCompletion() async -> FlowResult
public func dismiss()
public func failPresentation()
public func navigate(to screenNodeId: String)

// Live mirror hot-replace (used by the preview tooling)
@MainActor public func replaceFlow(_ newDefinition: FlowDefinition, fonts: [FontFile]? = nil)

// Preview-only initializer (skips resolution, suppresses analytics)
public convenience init(
    flow: ResolvedFlow,
    suppressAnalytics: Bool = true,
    sdkContext: SDKContext? = nil
)

// Deprecated: use startNavigation() then waitForCompletion()
@available(*, deprecated)
public func start() async -> FlowResult
```

<Note>
  The engine objects inside a session (`variableStore`, `navigationController`, `actionExecutor`, and so on) are internal. Use the delegating methods above instead of reaching into them.
</Note>

## Presenter (SwiftUI / UIKit)

How a session becomes on-screen UI. See [SwiftUI integration](/ios-sdk/swiftui).

```swift theme={null}
// SwiftUI view that renders a session.
@MainActor
public struct FlowPresenterView: View {
    public init(session: FlowSession)
}

// View modifier: present when the bound session is non-nil, clear it on result.
extension View {
    public func flowPresenter(
        session: Binding<FlowSession?>,
        onResult: ((FlowResult) -> Void)? = nil
    ) -> some View
}

// UIKit hosting controller that wraps FlowPresenterView (UIKit only).
@MainActor
public class FlowHostingController: UIHostingController<FlowPresenterView> {
    public init(session: FlowSession)
    public func onCompletion(_ handler: @escaping (FlowResult) -> Void)
}
```

`FlowPresenterModifier` (the type behind `.flowPresenter`) is also `public`, but use the `.flowPresenter(session:onResult:)` modifier rather than constructing it directly.

## Results

Returned by the presenting and `waitForCompletion()` APIs. See [Results and outcomes](/ios-sdk/results-and-outcomes).

```swift theme={null}
public struct FlowResult: Sendable {
    public let outcome: FlowOutcome
    public let finalVariables: [String: VariableValue]
    public let screensVisited: [String]
    public let durationMs: Int
    public let experimentAssignments: [String: String]
    public let error: FlowPilotError?
}

public enum FlowOutcome: String, Sendable {
    case completed
    case dismissed
    case error
}
```

## Variables

See [Variables and SDK context](/ios-sdk/variables-and-context).

```swift theme={null}
public enum VariableValue: Sendable, Equatable, Codable {
    case string(String)
    case number(Double)
    case boolean(Bool)
    case stringList([String])
    case numberList([Double])
    case booleanList([Bool])

    // Typed accessors (coercing where noted)
    public var stringValue: String?
    public var numberValue: Double?        // coerces a numeric string
    public var intValue: Int?              // coerces a numeric string
    public var boolValue: Bool?            // coerces "true"/"1"/"yes" and numbers
    public var stringListValue: [String]?
    public var numberListValue: [Double]?
    public var booleanListValue: [Bool]?

    // Inspection
    public var isEmpty: Bool
    public var isString: Bool
    public var isNumber: Bool
    public var isBoolean: Bool
    public var isList: Bool
    public var typeName: String
    public var displayString: String
}
```

`VariableValue` also conforms to `ExpressibleBy{String,Integer,Float,Boolean,Array}Literal`, so you can write `session.setVariable("plan", value: "premium")` or `value: 3`.

```swift theme={null}
public enum VariableType: String, Codable, Sendable {
    case string
    case number
    case boolean
    case list
}

// Used by custom components: an input that is either a bound variable or a constant.
public enum ComponentInputValue: Codable, Sendable {
    case bind(String)
    case value(VariableValue)
}
```

## Custom components and screens

Registration methods on `FlowPilot.shared`, plus the definition types. See [Custom components](/ios-sdk/custom-components) and [Custom screens](/ios-sdk/custom-screens).

```swift theme={null}
// Register / unregister a custom component by key (+ version, default 1).
public func registerCustomComponent(
    key: String,
    version: Int = 1,
    definition: CustomComponentDefinition
)
public func unregisterCustomComponent(key: String, version: Int = 1)

// Deprecated bare-key overloads (use version 1).
@available(*, deprecated)
public func registerCustomComponent(_ typeId: String, definition: CustomComponentDefinition)
@available(*, deprecated)
public func unregisterCustomComponent(_ typeId: String)

// Custom screens.
public func registerCustomScreen(_ screenId: String, definition: CustomScreenDefinition)
public func unregisterCustomScreen(_ screenId: String)
```

```swift theme={null}
public struct CustomComponentDefinition: Sendable {
    public let inputs: [String: VariableType]?
    public let outputs: [String: OutputSchema]?
    public let factory: @Sendable @MainActor (CustomComponentProps, CustomComponentContext) -> AnyView

    public init(
        inputs: [String: VariableType]? = nil,
        outputs: [String: OutputSchema]? = nil,
        factory: @escaping @Sendable @MainActor (CustomComponentProps, CustomComponentContext) -> AnyView
    )
}

public struct OutputSchema: Sendable {
    public let description: String?
    public let payload: [String: VariableType]?
    public init(description: String? = nil, payload: [String: VariableType]? = nil)
}

public struct CustomComponentProps: Sendable {
    public let inputs: [String: VariableValue]
    // Read-only validated accessors (warn in debug on type mismatch):
    public func string(_ key: String) -> String?
    public func string(_ key: String, default defaultValue: String) -> String
    public func number(_ key: String) -> Double?
    public func number(_ key: String, default defaultValue: Double) -> Double
    public func bool(_ key: String) -> Bool?
    public func bool(_ key: String, default defaultValue: Bool) -> Bool
    public func int(_ key: String) -> Int?
    public func int(_ key: String, default defaultValue: Int) -> Int
}

public final class CustomComponentContext: @unchecked Sendable {
    public let containerSize: CGSize
    public let containerConstraints: ContainerConstraints
    public func emit(_ eventName: String, payload: [String: Any]? = nil)
    public func emit(_ eventName: String)

    public struct ContainerConstraints: Sendable {
        public let minWidth: Double?
        public let maxWidth: Double?
        public let minHeight: Double?
        public let maxHeight: Double?
        public let supportsIntrinsicSize: Bool
    }
}
```

```swift theme={null}
public struct CustomScreenDefinition: Sendable {
    public let inputs: [String: VariableType]?
    public let outputs: [String: OutputSchema]?
    public let factory: @Sendable @MainActor (CustomScreenParams, CustomScreenContext) -> AnyView
    public init(/* same shape as CustomComponentDefinition */)
}

public struct CustomScreenParams: Sendable {
    public let inputs: [String: VariableValue]
    public func string(_ key: String) -> String?
    public func string(_ key: String, default defaultValue: String) -> String
    public func number(_ key: String) -> Double?
    public func number(_ key: String, default defaultValue: Double) -> Double
    public func bool(_ key: String) -> Bool?
    public func bool(_ key: String, default defaultValue: Bool) -> Bool
}

public final class CustomScreenContext: @unchecked Sendable {
    public func emit(_ eventName: String, payload: [String: Any]? = nil)
    public func emit(_ eventName: String)
    public func setZonesVisible(_ visible: Bool)
    @available(*, deprecated) public func setChromeVisible(_ visible: Bool)
}
```

<Warning>
  Custom **components** render today. Custom **screens** do not: the SDK accepts `registerCustomScreen` and stores the definition, but the renderer never looks it up or invokes the factory in v1.0.0. Treat the custom-screen types as a roadmap API. See [Custom screens](/ios-sdk/custom-screens).
</Warning>

## Analytics

See [Analytics integration](/ios-sdk/analytics) and the [Event taxonomy](/reference/event-taxonomy).

```swift theme={null}
// Set an additive callback that receives every analytics event the SDK emits.
public func setAnalyticsCallback(_ callback: @escaping AnalyticsCallback)

public typealias AnalyticsCallback = (AnalyticsEvent) -> Void
```

```swift theme={null}
public struct AnalyticsEvent: Codable, Sendable {
    public let eventId: String          // wire: event_id
    public let appId: String            // wire: app_id
    public let eventName: String        // wire: event_type
    public let timestamp: Date
    public let placementId: String?
    public let flowId: String
    public let flowVersionId: String
    public let userId: String
    public let sessionId: String
    public let devicePlatform: String   // "ios"
    public let sdkVersion: String
    public let flowVersion: Int?
    public let experimentId: String?
    public let variantId: String?
    public let variantName: String?
    public let screenId: String?
    public let screenName: String?
    public let screenIndex: Int?
    public let elementId: String?
    public let elementType: String?
    public let interactionType: String?
    public let revenue: Double?
    public let currency: String?
    public let timeSinceFlowStartMs: Int?
    public let timeOnScreenMs: Int?
    public let appVersion: String?
    public let country: String?
    public let properties: [String: AnyCodable]?
}

// Events the SDK fires automatically.
public enum AutomaticEventType: String, Sendable {
    case flowStarted = "flow_start"
    case flowCompleted = "flow_complete"
    case flowDismissed = "flow_exit"
    case screenViewed = "screen_view"
    case screenExited = "screen_exit"
    case experimentAssigned = "experiment_exposure"
    case elementInteraction = "element_interaction"
}
```

Conversions are emitted with `event_type` `conversion` via `trackConversion(...)`, not through `AutomaticEventType`.

## Errors

See [Error handling](/ios-sdk/error-handling).

```swift theme={null}
public func setErrorCallback(_ callback: @escaping ErrorCallback)
public typealias ErrorCallback = (FlowPilotError) -> Void

public struct FlowPilotError: Error, Sendable, CustomStringConvertible {
    public let code: FlowPilotErrorCode
    public let message: String
    public let underlyingError: Error?
    public let context: [String: String]?
    public var description: String
    public var localizedDescription: String
    public var isClientError: Bool     // true for a 4xx status in context
    public var isServerError: Bool     // true for a 5xx status in context
}
```

`FlowPilotErrorCode` is a `String` enum with these cases (raw values in the [Error handling](/ios-sdk/error-handling) table): `invalidApiKey`, `invalidAppId`, `sdkNotInitialized`, `networkError`, `apiError`, `timeout`, `rateLimited`, `placementNotFound`, `flowNotFound`, `targetingNotMet`, `frequencyLimitReached`, `unsupportedSchemaVersion`, `invalidFlowSchema`, `componentRenderError`, `customComponentNotFound`, `customScreenNotFound`, `navigationError`, `variableError`, `actionError`, `fatalActionError`, `actionChainTimeout`, `internalError`.

<Warning>
  `setErrorCallback(_:)` stores the closure but the SDK never invokes it in v1.0.0. Observe errors through the thrown `FlowPilotError` (from the throwing present/create APIs) and through `FlowResult.error`. See [Error handling](/ios-sdk/error-handling).
</Warning>

## Caching and offline

See [Caching](/ios-sdk/caching) and [Offline and bundled flows](/ios-sdk/offline-bundled-flows).

```swift theme={null}
// Clear cached flows + fonts + icons.
public func clearCache() async

// Clear cached images + icons.
public func clearImageCache()

// The shared image cache, for advanced use.
public var imageCache: ImageCache { get }

// Register a bundled (offline) default flow for a placement. Four overloads:
public func registerBundledFlow(placementKey: String, json: Data, assets: BundledFlowAssets? = nil)
public func registerBundledFlow(
    placementKey: String,
    resource: String,
    withExtension ext: String = "json",
    in bundle: Bundle = .main,
    assets: BundledFlowAssets? = nil
)
public func registerBundledFlow(
    placementKey: String,
    assetBundle subdirectory: String,
    in bundle: Bundle = .main,
    flowResource: String = "flow",
    manifestResource: String = "manifest"
)
public func registerBundledFlowAssets(placementKey: String, assets: BundledFlowAssets)
```

```swift theme={null}
public final class ImageCache: @unchecked Sendable {
    public static let shared: ImageCache
    public static let bundledAssetTTL: TimeInterval   // effectively non-expiring
    public var maxMemoryCacheSize: Int                // default 50MB
    public var maxDiskCacheSize: Int                  // default 200MB
    public var defaultTTL: TimeInterval               // default 7 days

    public init(cacheDirectory: String? = nil)
    public func getImage(for url: URL) -> UIImage?
    public func hasImage(for url: URL) -> Bool
    public func setImage(_ image: UIImage, for url: URL, ttl: TimeInterval? = nil)
    public func setImageData(_ data: Data, for url: URL, ttl: TimeInterval? = nil)
    public func removeImage(for url: URL)
    public func clearAll()
    public func cleanExpired()
}
```

```swift theme={null}
public struct BundledFlowAssets: @unchecked Sendable {
    // Assets in a plain filesystem directory (manifest + files).
    public init(directoryURL: URL, manifest: String = "manifest")
    // Assets in an app bundle, optionally inside a folder-reference subdirectory.
    public init(bundle: Bundle = .main, subdirectory: String? = nil, manifest: String = "manifest")
}
```

<Note>
  On non-UIKit platforms (macOS without UIKit), `ImageCache` is a reduced stub: `getImage`, `setImage`, and `setImageData` are not available there.
</Note>

## Callbacks and typealiases

```swift theme={null}
public typealias AnalyticsCallback = (AnalyticsEvent) -> Void
public typealias ErrorCallback = (FlowPilotError) -> Void
public typealias FlowCompletionCallback = (FlowResult) -> Void
public typealias FlowDismissalCallback = (String) -> Void
```

<Note>
  `FlowCompletionCallback` and `FlowDismissalCallback` are declared `public` for source compatibility, but there is no setter for them and the SDK does not invoke them in v1.0.0. Get the result from the present APIs' return value, the `completion:` closure, or `FlowResult` instead.
</Note>

## Deprecated aliases

Short-prefix names kept for source compatibility with v1.0 integrators. Each is marked `@available(*, deprecated, renamed:)`; migrate to the `FlowPilot*` name.

| Deprecated alias  | Use instead              |
| ----------------- | ------------------------ |
| `FPConfiguration` | `FlowPilotConfiguration` |
| `FPEnvironment`   | `FlowPilotEnvironment`   |
| `FPLogLevel`      | `FlowPilotLogLevel`      |
| `FPError`         | `FlowPilotError`         |
| `FPErrorCode`     | `FlowPilotErrorCode`     |

## Debug

```swift theme={null}
// Draw debug borders around rendered components.
public static var debugBordersEnabled: Bool

// Raise the log level to .verbose (development only).
public func enableDebugOverlay()
// Restore the configured log level.
public func disableDebugOverlay()
// Log user id, session id, environment, and registry counts.
public func logState()
```

<Note>
  `createTestSession()` also exists but is compiled only in `#if DEBUG` builds and is for rendering tests, not production use.
</Note>

## The `FlowPilotDelegate` protocol

```swift theme={null}
public protocol FlowPilotDelegate: AnyObject {
    func flowPilotDidComplete(_ flowId: String, result: FlowResult)
    func flowPilotDidDismiss(_ flowId: String)
    func flowPilotDidFail(_ error: FlowPilotError)
}
```

<Warning>
  `FlowPilotDelegate` is declared `public`, but the SDK has no setter for it and never calls its methods in v1.0.0. It is not usable today. Use the closure-based APIs, the present return values, and the never-throwing overloads instead. See [Error handling](/ios-sdk/error-handling).
</Warning>

## Related pages

* [Installation](/ios-sdk/installation)
* [Configuration](/ios-sdk/configuration)
* [Quickstart](/ios-sdk/quickstart)
* [Presenting placements](/ios-sdk/presenting-placements)
* [SwiftUI integration](/ios-sdk/swiftui)
* [Results and outcomes](/ios-sdk/results-and-outcomes)
* [Prefetching](/ios-sdk/prefetching)
* [Variables and SDK context](/ios-sdk/variables-and-context)
* [Conversions and revenue](/ios-sdk/conversions)
* [Custom components](/ios-sdk/custom-components)
* [Custom screens](/ios-sdk/custom-screens)
* [Analytics integration](/ios-sdk/analytics)
* [Error handling](/ios-sdk/error-handling)
* [Caching](/ios-sdk/caching)
* [Offline and bundled flows](/ios-sdk/offline-bundled-flows)
* [Media preloading](/ios-sdk/media-preloading)
* [Troubleshooting](/ios-sdk/troubleshooting)
