Before you startPaywalls build on the standard FlowPilot setup, plus RevenueCat:
- A FlowPilot workspace, an app inside it, and an SDK API key for that app.
- A RevenueCat account with your products, entitlements, and an offering configured for your app.
- Paywalls enabled for your workspace. If you do not see a Paywalls item in the dashboard sidebar, reach out and we will turn it on.
Add the RevenueCat adapter
The RevenueCat adapter ships as a separate Swift package,FlowPilotRevenueCat, on purpose. Apps that use FlowPilot but not RevenueCat never resolve or fetch purchases-ios. Add this package (and RevenueCat) only in an app that shows paywalls.
TODO: confirm the published
FlowPilotRevenueCat package URL and version rule, then replace the placeholders below.Wire it up
The order matters: configure RevenueCat first, then FlowPilot, then register the adapter before you present a paywall.Configure RevenueCat (host-owned)
Configure RevenueCat yourself, with your own API key and your own user identity. FlowPilot never calls
Purchases.configure, never signs users in or out, and never claims the RevenueCat delegate.Present a paywall
A paywall is a placement, so you present it withpresentPlacement, the same call you use for onboarding. What changes is the result: a paywall flow reports a paywallOutcome.
The paywall outcome
FlowResult.paywallOutcome is nil for non-paywall flows. For a paywall it is one of:
| Outcome | Meaning |
|---|---|
.purchased | The user bought a subscription and the entitlement is active. |
.restored | A restore found an active entitlement. |
.dismissed | The user closed the paywall without buying. Read paywallGatingMode to decide what that means. |
.skippedEntitled | The user already held the entitlement, so the paywall was not shown. |
.skippedNoFlow | Nothing was configured to present (paused placement, no flow). |
.skippedAudience | The user did not match the audience. |
.skippedHoldout | The user was bucketed into a holdout that shows no paywall. |
.failed | Something went wrong. Fail-open: let your feature continue. |
FlowOutcome (.completed, .dismissed, .error) is unchanged, so your existing result handling still compiles. See Results and outcomes.
FlowPilot reports the outcome and the gating mode. It does not enforce feature access. Your app decides what a
.dismissed result on a hard paywall means. A user in a billing grace period or retry state is reported as entitled by RevenueCat, so skipIfEntitled naturally leaves them alone.Fail-open by default
If no paywall is configured, the audience does not match, the user is in a holdout, or the flow fails to load, FlowPilot does not present anything and your app’s own path continues. This is the safe default: a paywall problem never blocks your app. When a feature must stay locked behind a paywall, your app enforces that using the outcome above.Check ahead of time with a peek
To render a lock badge or decide whether to gate a feature before presenting, usepeekPlacement. It resolves whether a paywall would show for this placement and this user, without emitting an impression or locking the user into an experiment variant.
PaywallPeekResult reports availability (available, skippedEntitled, notPaywall, noFlow, or unknown), plus isEntitled, gatingMode, and requiredEntitlement. On an inconclusive peek (offline or an error) the availability is unknown, so treat it as “do not gate” rather than showing a broken lock.
Test before you ship
Real purchases need a device and a signed build. In development, the fastest end-to-end path is the RevenueCat Test Store, which exercises the full purchase flow without App Store Connect. Use an Apple sandbox account for accelerated-renewal and restore testing, and always run one pass on a real device before submitting.Diagnostics
The SDK logs each purchase state transition. Set the log level to see them, and turn off diagnostic reporting if you prefer:Common mistakes
- Registering the provider before configuring RevenueCat. Configure RevenueCat first. The adapter reads
Purchases.shared, which must already be configured. - Letting FlowPilot own identity. Keep
Purchases.configure, log in, and log out in your app. FlowPilot only reads through the adapter. - Treating a skip as an error. Every
skipped*outcome is a normal, fail-open result. Continue your app. - Testing purchases in the simulator. Use the RevenueCat Test Store or a sandbox account on a real device.