iOS SDK

Presenting Flows

Show paywalls and screens to users as overlay WebViews

Presenting Flows

Flows are the published screen bundles you build in the Studio -- paywalls, onboarding sequences, upgrade prompts -- compiled and delivered to the SDK. Present them as overlay WebViews that render above your app's UI and support native actions like purchases, navigation, and custom events.

Terminology note: In the Studio you edit a Project. Publishing creates an immutable Version (ver_...).
The SDK and profile payload still refer to these published versions as "flows", so the flowId you pass to showFlow() is a version ID.

How flows reach the device

  1. You design and publish a project in the Nuxie Studio.
  2. The SDK fetches the flow definition as part of the user's profile payload.
  3. Flow assets (HTML, CSS, JavaScript, fonts) are cached locally as a WebArchive for fast presentation.
  4. When a campaign triggers or you call showFlow(), the SDK loads the cached assets into a WebView overlay.

The SDK prefetches flow assets in the background whenever the profile updates, so flows load instantly even on first presentation.

Manual presentation

Present a flow directly by its ID:

swift
try await NuxieSDK.shared.showFlow(with: "ver_abc123")

This opens the flow in a dedicated overlay window above your app's current view hierarchy. The flow renders full-screen and handles its own dismissal through actions configured in the Studio.

You can also get a FlowViewController for custom embedding:

swift
let viewController = try await NuxieSDK.shared.getFlowViewController(with: "ver_abc123")
present(viewController, animated: true)

Automatic presentation via campaigns

Campaigns present flows automatically based on triggers you configure in the dashboard:

  • Event triggers -- a flow appears when the user fires a specific event via trigger().
  • Segment triggers -- a flow appears when the user enters (or exits) a segment.

When a campaign matches, the SDK presents the flow without any additional code:

swift
// If a campaign targets "checkout_started", this may present a flow
NuxieSDK.shared.trigger("checkout_started")

Use the TriggerHandle to observe what happened:

swift
NuxieSDK.shared.trigger("checkout_started") { update in
    switch update {
    case .decision(.flowShown(let ref)):
        print("Flow presented: \(ref)")
    case .decision(.suppressed(let reason)):
        print("Campaign suppressed: \(reason)")
    case .decision(.noMatch):
        print("No campaign matched this event")
    default:
        break
    }
}

Reentry policies

Campaigns support reentry policies that control how often a flow appears:

PolicyBehavior
Every timeShow the flow on every qualifying trigger.
One timeShow the flow once per user, ever.
Once per windowShow the flow once within a time window (e.g., once per day).

The overlay window

Flows display in a dedicated UIWindow layered above your app:

  • The window sits at the .alert window level, so it appears above navigation bars, tab bars, and modals.
  • Only one flow can be presented at a time. Presenting a new flow dismisses the current one.
  • After the app returns to the foreground, there is a short grace period (under one second) before flows can appear, preventing abrupt presentations.

Runtime bridge

The flow's web content communicates with your native app through a runtime bridge. This bridge supports:

  • Purchase actions -- initiate a StoreKit purchase from a button in the flow.
  • Restore actions -- trigger a purchase restore.
  • Navigation actions -- move between screens within a multi-screen flow.
  • Dismiss actions -- close the flow.
  • Open link actions -- open a URL in the system browser.
  • Custom delegate actions -- send a message to your app via NotificationCenter.

Listening for delegate actions

When a flow fires a call_delegate action, the SDK posts a Notification your app can observe:

swift
NotificationCenter.default.addObserver(
    forName: .nuxieCallDelegate,
    object: nil,
    queue: .main
) { notification in
    let message = notification.userInfo?["message"] as? String
    let payload = notification.userInfo?["payload"] as? [String: Any]
    // Handle the delegate call
}

Other bridge actions post similar notifications: .nuxiePurchase, .nuxieRestore, .nuxieOpenLink, .nuxieDismiss.

Flow caching

The SDK caches flow assets at multiple layers for fast loading:

  • WebArchive cache -- the full flow bundle is stored on disk as a WebArchive file. On presentation, the SDK loads from the local file instead of fetching over the network.
  • Font cache -- custom fonts referenced by flows are downloaded and served from a local cache via a custom URL scheme.
  • In-memory cache -- enriched flow models (including resolved StoreKit product metadata) are held in memory for the current session.

Cache invalidation happens automatically:

  • When the profile delivers updated flow content (detected by content hash), the old cache is removed and the new version is prefetched.
  • When the user logs out (reset()), all flow caches are cleared.

Next steps

  • Purchases -- handle purchases initiated from within flows
  • Campaigns -- configure triggers, goals, and reentry policies in the dashboard
  • Features & Entitlements -- gate features based on what the user has purchased