Documentation Index
Fetch the complete documentation index at: https://docs.encorekit.com/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Every Encore SDK exposes the same primitive:Encore.placement(id).show() opens the offer sheet, the user accepts or dismisses, and you do something next. There are two idiomatic ways to wire up the “do something next” part:
- Async-result —
await show()returns a result; branch on it locally. - Handler-based — register
onPurchaseRequest/onPassthroughonce at app launch; the SDK calls back into them whenever a placement resolves.
show() already has access to your purchase + decline logic.
The two patterns
Async-result (recommended when applicable)
The trigger is also the handler. Code that should run in both branches is written once, control flow is local, no global state.Handler-based
Register handlers once at app init. Anywhere in the app can then callshow() without knowing how purchases are wired.
On every platform, re-registering a handler replaces the previous one — no double-firing. You can call the setters multiple times safely.
Decision tree
When async-result wins
- The trigger lives next to the purchase code (e.g. a
Cancel Subscriptionbutton in your settings screen that already imports your billing client) - Code that should run on both branches — analytics, navigation, cleanup — would otherwise duplicate across handler + caller
- You want the post-show flow to read top-to-bottom without hopping between files
When handlers win
- The trigger is a third-party paywall delegate (Superwall, RevenueCat) where you don’t control the call site
- Purchase code lives in a ViewModel or DI scope not visible from a UI button action
- Multiple
show()sites share post-purchase logic and you’d rather centralize it
Mixing is fine
Handlers can fire for cross-cutting telemetry while async results drive control flow. The two patterns coexist on the same placement — the handler pattern is the default the SDK falls back to whenever the call site doesn’t act on the returned result.Common scenarios
| Scenario | Pattern |
|---|---|
Cancel button in your subscription settings (you have purchase code) | Async-result |
| Encore presented from a Superwall delegate that can’t see your VM | Handler |
Multiple show() sites sharing one billing path | Handler |
Single show() call with branch-specific UI navigation | Async-result |
| Cross-cutting analytics on every offer regardless of caller | Handler (alongside async-result) |
Cross-cutting concerns
Re-registration is replace, not append
All five SDKs replace the previous handler when you call the setter again. You don’t need to defensively unsubscribe. On React Native, the handler setters return an unsubscribe function for component-lifecycle convenience — useful but not required to prevent double-fires.Async wrapping at non-async triggers
show() is async on every platform. When the trigger is a synchronous callback (SwiftUI Button action body, Compose onClick, an event listener) you wrap the call in the platform’s standard async launcher:
| Platform | Wrapper |
|---|---|
| Swift / SwiftUI | Task { ... } |
| Android / Compose | lifecycleScope.launch { ... } or coroutineScope.launch |
| Flutter | unawaited(...) or await from an async callback |
| React Native | await from an async event handler |
| Web | await from an async function |
Web has no global handlers
The Web SDK doesn’t exposeonPurchaseRequest / onPassthrough — browsers handle the purchase via redirects/forms, so there’s no purchase-delegation contract to register. Use the async-result of Encore.placement(id).show() for control flow, and per-placement callbacks (Encore.placement(id).onGranted(...).onNotGranted(...)) for cross-cutting telemetry.
Order of operations for handlers
If you go with handlers, register them before anyshow() call. The simplest place is right after configure() at app launch. Late registration risks a placement firing into the void.
Per-platform guides
iOS
Swift code for both patterns,
Task wrapping, replace semanticsAndroid
Kotlin code,
lifecycleScope.launch, sealed-class PresentationResultReact Native
TypeScript code,
PlacementResult.status, unsubscribe semanticsFlutter
Dart code,
EncorePresentationResult switch, setOn* namingWeb
Async-result + per-placement callbacks (no global handlers)