Skip to main content

Overview

SDK 2.0 is a breaking release that makes the integration surface simpler and harder to misuse:
  • show() returns one result record and never throws. Errors arrive as values on the result instead of as exceptions.
  • Purchases run through a registered EncorePurchaseController. The SDK never runs purchase code you didn’t write; the built-in Play Billing fallback is removed.
  • Global handler closures are retired. Results are values at the call site; passive observation moves to the Encore.outcomes stream.
  • The callback form is always delivered on the main thread; the suspending form resumes in your caller’s context. show() itself is safe to call from any thread or scope.
2.0 removes the 1.x legacy surface outright rather than deprecating it, so every call site that needs attention fails to compile. Work through the steps below until the project builds, then run the verification checklist. The removed API table maps every 1.x symbol to its replacement.

Step 1: Update the dependency

Sync and build. The compile errors that appear are your migration worklist; each one corresponds to a step below.

Step 2: Register a purchase controller

This is the largest change. In 1.x, purchases ran through the onPurchaseRequest closure, and if no handler was registered the SDK bought the product itself through Play Billing and reported it via onPurchaseComplete. 2.0 removes the closure, the completion callback, and the built-in Play Billing fallback: that fallback bypassed whatever receipt validation, restore handling, and entitlement bookkeeping your app already has, and it made a missing registration look like a working purchase instead of a misconfiguration. Implement the controller and pass it to configure():
Return Purchased, Cancelled, or Pending; throw for real failures. The full contract is in EncorePurchaseController.

Step 3: Update show() call sites

show() returns the full result record and never throws. The 1.x scope-taking overloads become an explicit callback form:
Result delivery is not foreground-gated: the callback fires even if your Activity backgrounded mid-flow. Don’t touch views without checking your own lifecycle. The suspending form works from any thread or scope:

Step 4: Update result handling

1.x modeled the result as a flat triad (Completed, Dismissed(reason), NoOffers), which forced a claim and a purchase to compete for a single winning case. 2.0 records the facts independently: 2.0 splits the result into NotPresented(reason) and Presented(outcome); see PresentationResult for the full shape. The record carries raw facts only: there is no SDK-computed “unlocked” verdict, so your integration decides what a claim means. Branch on the facts your flow cares about:
result.claim surfaces the claimed offer whenever the advertiser funnel reached Claimed or Verified; read result.advertiser directly to tell the two apart. When the mechanism detail matters, read the record:

Step 5: Replace global callbacks with the outcomes stream

onPassthrough and onPurchaseComplete are removed. Handle fallback inline at the call site (Step 4), and move passive observation (analytics, logging, cross-cutting state) to the Encore.outcomes stream:
Every show() resolution lands on Encore.outcomes, including NotPresented, plus strict-unlock verifications that settle on a later launch.

Removed APIs at a glance


Behavior changes to be aware of

These compile fine but behave differently at runtime:
  • Claim-then-purchase records both funnels. 1.x collapsed the flow to a single winner. 2.0 carries advertiser = Claimed(...) and publisher = Purchased side by side, and the result is delivered once, after the purchase resolves (later than 1.x fired its callbacks).
  • Strict mode records the claim, and verification upgrades it. With UnlockMode.Strict, the record says advertiser = Claimed(...) immediately, and in-session verification upgrades it to Verified(...). A claim that ends unverified re-verifies across launches and surfaces as PlacementOutcome.StrictUnlockVerified on Encore.outcomes; the persistence rules are in unlock modes.
  • The offer sheet no longer recreates on rotation or dark-mode changes, so mid-flow configuration changes can’t produce spurious results, and a dead flow (killed sheet) resolves as Presented(dismissal = Interrupted) instead of wedging show() in “already presenting”.
  • Gesture dismissal now reports swipe_dismiss in analytics (previously lumped into close_button). On the SDUI sheet, which is the default render path, this covers both dragging the sheet down and tapping outside it; on the native fallback sheet only dragging reports it. All other event names and close-reason strings are unchanged on the wire.
Backup rules caveat for strict mode. The SDK ships an include-only Auto Backup allowlist: it includes com.encorekit.encore.xml (the cross-install NCL identity file) and excludes its pending-claims file by omission. If your app replaces the SDK’s backup rules wholesale (tools:replace on the manifest attributes), keep com.encorekit.encore.xml included and keep com.encorekit.encore.pending.xml out of your include list. Runtime guards back the pending-claims exclusion up either way.
New in 2.x with no migration required: placements can declare a claim-only reward surface (Encore.placement("streak_complete").useCase(UseCase.REWARD_USERS)) with per-presentation headline() / subheadline() copy overrides. The surface fires after the user has already done something worth celebrating, and reward presentations never run a purchase and never show the paywall layout.

Verify the migration

Confirm each of these before shipping:
  1. The project compiles with no references to removed 1.x APIs.
  2. If a subscription product is configured for your app, an EncorePurchaseController is registered at configure time.
  3. Every fallback path (your original paywall or flow) runs off the raw facts (result.claim == null && result.publisher != PublisherOutcome.Purchased), not a removed callback.
  4. If you subscribe to Encore.outcomes, the subscription starts at app startup (there is no replay).
  5. Test the full flow on a device: present a placement, claim an offer, complete a purchase, and dismiss without acting. Each path should resolve exactly once with the result you expect.

If you hit anything this guide doesn’t cover, reach out to admin@encorekit.com.