Skip to content

Bridge togglePaywallSpinner to Expo - #225

Merged
ianrumac merged 1 commit into
superwall:mainfrom
actuallyhoss:feat/toggle-paywall-spinner
Sep 13, 2026
Merged

ianrumac merged 1 commit into
superwall:mainfrom
actuallyhoss:feat/toggle-paywall-spinner

Conversation

@actuallyhoss

Copy link
Copy Markdown
Contributor

The gap

Both native SDKs expose togglePaywallSpinner(isHidden:) and document it identically, as the thing you reach for when a custom paywall action does async work:

Useful for when you want to display a spinner when doing asynchronous work inside SuperwallDelegate.handleCustomPaywallAction.

Superwall.swift:1007 on iOS, Superwall.kt:799 on Android. There are no references to it anywhere in this package.

The custom action itself is fully bridged. An app can already receive one via handleCustomPaywallAction on the delegate or onCustomCallback on usePlacement, and can already do async work in response, but there's no way to make the paywall look busy while that runs. It just sits there looking frozen.

Layer Status before
Native SDKs (iOS + Android) togglePaywallSpinner public and documented
Custom action bridge (delegate, usePlacement, compat) ✅ Fully wired
togglePaywallSpinner in Expo ❌ Absent everywhere

The change

A sync Function on both platforms matching the native signature. Both native implementations dispatch internally and no-op when no paywall is presented, so there's nothing to await and nothing that can throw. The store method is async only because it waits on awaitConfigured() first, in line with every other call in that store.

  • ios/SuperwallExpoModule.swift, android/.../SuperwallExpoModule.kt: the native modules
  • src/SuperwallExpoModule.ts: the module type
  • src/useSuperwall.ts, src/compat/index.ts: both SDK surfaces
  • README.md: Hooks API Reference, next to dismiss
  • example/app/new.tsx: the existing onCustomCallback returned immediately; it now shows the spinner across a two second delay, with the hide in a finally so a throw can't leave it spinning
const togglePaywallSpinner = useSuperwall((state) => state.togglePaywallSpinner)

usePlacement({
  onCustomCallback: async ({ name }) => {
    await togglePaywallSpinner(false)
    try {
      return { status: "success", data: await syncAccount(name) }
    } finally {
      await togglePaywallSpinner(true)
    }
  },
})

Testing

  • Full jest suite: 12 passed, 2 suites.
  • tsc --noEmit on the package, and on the example app (clean apart from a pre-existing style typing error in article-paywall.tsx, untouched here).

The new case in sdk.behavior.test.tsx sits alongside the existing "waits for configure" tests and asserts the call is queued until configure resolves, then passes the boolean through both ways. I checked it's load bearing by swapping the store's native call for a no-op, which turns it red.

I don't have a macOS or Android toolchain to hand, so the Swift and Kotlin haven't been compiled locally and I haven't watched the spinner run on a device. Both native additions are four lines against a documented public API and sit alongside identical Function blocks, so I'd expect them to be fine, but CI should confirm the compile and it's worth someone eyeballing it on a device.

@pkg-pr-new

pkg-pr-new Bot commented Sep 13, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/expo-superwall@225

commit: 3b5fcaa

@ianrumac
ianrumac force-pushed the feat/toggle-paywall-spinner branch from 3b5fcaa to bc3ac15 Compare September 13, 2026 11:48
@ianrumac

Copy link
Copy Markdown
Contributor

Thnks for the PR @actuallyhoss , merging it in for upcoming release!

@ianrumac
ianrumac merged commit cc673cc into superwall:main Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants