Skip to main content

Overview

Encore presents a premium brand offer (a real perk a brand funds to acquire customers, such as a free trial, discount, or credit) at a moment you choose. Start with Reward Users: a reward sheet at a moment the user just earned, such as a completed purchase, a streak, or a milestone, at no cost to you. The other use case, Reduce Churn, presents offers in a cancel flow or after a paywall decline.

Present Offers

The facade is @MainActor, so call show() from a main-actor context: a SwiftUI button action, a .task, or a UIKit handler. show() never throws and always resolves; a SwiftUI Button action is synchronous, so wrap the await in a Task:
Select .useCase(.rewardUsers) on every reward placement. A reward placement never runs a purchase and never falls back to the paywall sheet, so it needs no subscription product and no purchase controller. The walkthrough, including per-call copy, is the Reward Placements guide.
Contracts: placement() · PresentationResult.
result.claim means the user tapped through to an offer, not that they finished it. To pay your own prize for trying an offer, see Reward for Trying (SDK 2.3.0+).

Callback form

When the call site can’t await, show(resume:) delivers every outcome (including .notPresented) on the main actor:
show() never throws. Failures arrive as .notPresented(.error(EncoreError)) on the PresentationResult, so there is no catch block to forget.

Handle Purchases

This section applies to Reduce Churn placements only. A reward placement never runs a purchase, so an app that presents only reward placements can skip it. When a user accepts an offer that requires a purchase, the SDK calls the EncorePurchaseController you registered at configure time. It performs the purchase through your billing code (StoreKit 2, RevenueCat, Adapty, or your own) and reports what happened:
The example above uses StoreKit 2. See EncorePurchaseController for RevenueCat, Adapty, and custom subscription manager implementations.
If your app uses Encore offers without a subscription product, no controller is needed and no purchase is ever attempted. If a product is configured but no controller is registered, the SDK never attempts a purchase: the claim completes with publisher: .notAttempted and the sdk_iap_no_purchase_controller analytics event flags the misconfiguration.

Observe Outcomes Passively

Anything that wants to watch results without owning a call site (analytics forwarding, a debug overlay, cross-screen state) consumes the outcomes stream:

What a Grant Looks Like

The result of show() records the raw facts of the flow; read the granted entitlement itself from isActive / isActivePublisher, the authoritative state. See Two-phase entitlements in EntitlementScope for how provisional and verified grants differ and which scope to gate on.

Next Steps