Fix a Superwall Paywall Not Showing on iOS: Placements, Campaigns, and SDK Config

A Superwall paywall not presenting on iOS usually comes down to a placement mismatch, campaign rules, subscription status, or SDK configuration. Here is how to diagnose and fix it in order.

Understanding the Symptom

The symptom is that you call register for a placement, expect a paywall, and nothing appears; your feature code either runs immediately or nothing happens at all. Before changing anything, get precise about which of those you are seeing, because they point in different directions. If the feature closure runs immediately with no paywall, Superwall likely decided not to present, which usually means the campaign rules did not match or the user is considered entitled. If nothing happens at all, the SDK may not be configured, the placement name may not match any campaign, or configure may not have finished before you called register. It also matters where you are testing: simulator versus device, and debug versus release configuration, can behave differently, especially around purchasing and any StoreKit configuration file. Reproduce the problem reliably first, note the exact placement name you are firing, and check whether the same placement presents from a clean test build. A clear reproduction turns a vague not-showing complaint into a specific decision path you can inspect, which is what the rest of this guide walks through in order of likelihood. Do not start changing code or campaign rules at random; the fastest fixes come from first classifying which of the two symptoms you have, because that single distinction eliminates half of the possible causes immediately.

Check the Placement Name Matches Exactly

The most common cause is a mismatch between the placement string in your register call and the placement configured in the campaign. The names must match exactly, including capitalization and any underscores or spaces, because Superwall routes purely on that string. A call to register(placement: "FeatureUnlock") will not match a campaign placement named feature_unlock. Audit both sides: copy the exact name from the dashboard campaign and compare it character by character with the string in code, watching for trailing whitespace, typos, or a name that was renamed in the dashboard but not updated in the app. If you maintain placement names as scattered string literals, this class of bug is easy to introduce, so consider centralizing them as constants in one file that both your code and your documentation reference. Also confirm the placement actually belongs to a campaign; a placement you fire that no campaign listens for will simply do nothing. This single check resolves a large share of not-showing reports, so do it before investigating anything more complex. Once you are certain the names are identical and the placement is attached to a live campaign, move on to the campaign's audience rules. It is remarkable how often an hour of deep debugging ends with the discovery that a placement was quietly renamed during a dashboard cleanup and the app was never updated to match.

Verify Campaign Audience Rules

If the placement matches but no paywall appears, the campaign's audience rules are the next suspect. A campaign only presents when a rule matches the current user, and rules can filter on user attributes, device or locale properties, subscription status, and limits on how often a paywall is shown. Review each rule for the placement and ask whether your test user actually satisfies it. Common traps include a rule that targets a segment your test user is not in, a rule that only fires once per user so it will not re-present after the first time, or a frequency cap that has already been hit. If you set user attributes from the app to drive targeting, confirm you are actually setting them before firing the placement, because a rule that depends on an attribute that was never set will not match. Temporarily simplify the rule to present the paywall to everyone to confirm the paywall and placement work in isolation, then reintroduce your targeting to find which condition excludes the user. Remember that changing rules in the dashboard takes effect without an app rebuild, which makes this a fast thing to test iteratively. The once-per-user trap deserves special attention during testing, because once your device has seen the paywall it may not see it again, which can fool you into thinking a fix did not work when in fact the frequency cap is doing exactly what you configured.

Rule Out Subscription Status

Superwall does not present a paywall to a user it believes is already entitled, so an incorrect subscription status is a frequent reason the feature closure runs immediately with no paywall. Determine what Superwall thinks the user's status is. If you rely on Superwall's built-in purchasing, a lingering sandbox subscription or restored purchase can mark the user active. If you supply a purchase controller and report status yourself, a bug there can leave the user incorrectly marked as subscribed, so verify that your controller reports the correct status and that it updates when subscriptions expire. When testing, use a fresh sandbox account with no active subscription to confirm the paywall presents for a non-entitled user, and separately test an entitled account to confirm it is correctly skipped. If you integrate with RevenueCat for entitlements, make sure the status you push to Superwall reflects RevenueCat's current customer info rather than a stale cached value. Getting subscription status right is essential not only for this bug but for correct gating overall, because a user wrongly marked entitled bypasses your paywall entirely and a user wrongly marked free is nagged despite paying. Sandbox subscriptions also renew and expire on an accelerated schedule, so a test account that looked entitled a few minutes ago may have silently lapsed, which is worth remembering before you conclude the code is wrong.

Confirm SDK Configuration and Timing

If nothing happens at all when you fire a placement, verify the SDK is configured correctly and early enough. Confirm Superwall.configure(apiKey:) runs during app launch, in the AppDelegate's didFinishLaunchingWithOptions or your SwiftUI App init, and that it uses the correct public API key for the environment you are testing. A wrong key, an accidentally used secret key, or pointing debug and release builds at different Superwall projects can all cause campaigns not to load. Timing matters too: register calls made before configuration completes and before campaigns are fetched will not present correctly, so ensure configure has run before any placement fires, which is naturally the case if configuration happens at launch. Raise the SDK log level during debugging so you can see initialization, campaign fetch, and placement evaluation messages in the console; these logs usually reveal whether the SDK loaded your campaigns and how it evaluated the placement. Also check network conditions, since the SDK needs to reach Superwall to download configuration; a device with no connectivity on first launch may have nothing cached to present. Configuration and timing problems tend to produce a total absence of behavior rather than a wrong paywall, which is a useful signature: if firing a placement does absolutely nothing and no log line even acknowledges it, suspect configuration before you suspect campaign rules.

Use Logs and the Dashboard to Trace the Decision

When the quick checks do not resolve it, trace the actual decision Superwall made. Increase the log level to its most verbose setting and reproduce the placement; the logs typically show that the placement was received, which campaign and rule it matched or failed to match, and why a paywall was or was not presented. This is the fastest way to distinguish a name mismatch, from a rule that excluded the user, from an entitled-status skip. Pair this with the dashboard, where you can inspect the campaign, confirm the placement is attached, review the audience rules, and check that the paywall variant is active and linked to real products. If you use experiments, confirm the user qualifies for the experiment and that at least one variant is set to present. Watching the log line for your placement while adjusting the campaign in the dashboard lets you correlate cause and effect quickly, since rule changes apply without a rebuild. This combination of verbose client logs and dashboard inspection will localize almost any not-showing problem to a specific, fixable cause rather than leaving you guessing. Make a habit of reading the logs top to bottom for the single placement you fired rather than skimming, because the exact reason a paywall was suppressed is usually stated plainly in one line that is easy to scroll past.

Prevent Regressions Going Forward

Because paywall presentation depends on remote configuration, it can break in production without a code change, so build a little resilience against that. Centralize placement names in code as constants so a rename cannot silently desync one call site, and document which placements map to which campaigns so the mapping is not tribal knowledge. Add lightweight logging or analytics around register calls that records whether a paywall was presented, skipped, or the feature ran, so you can detect a sudden drop in paywall impressions that would signal a broken campaign. When someone edits a campaign in the dashboard, treat it like a production change: verify on a real device afterward that paywalls still present as expected, including the entitled and non-entitled cases. Keep a simple test build or QA checklist that exercises each important placement so regressions surface before users hit them. Finally, remember the division of responsibility: Superwall controls presentation and rules, but your app still owns configuration, identity, and reported subscription status, and App Store Connect still owns the products. Most not-showing bugs live at the seams between those pieces, so monitoring those seams is the best long-term prevention. An impression-count alert that fires when paywall presentations drop unexpectedly is one of the highest-value pieces of instrumentation you can add, because it catches a broken campaign in hours rather than at the end of a disappointing revenue month.

Frequently Asked Questions

My feature runs but no paywall appears. Why?

Superwall decided not to present, which usually means either the campaign audience rules did not match the user, or Superwall believes the user is already entitled. Check the rules for your placement and verify the user's subscription status, testing with a fresh sandbox account that has no active subscription.

Nothing happens at all when I fire a placement. What is wrong?

Typically the SDK is not configured, configure ran too late, or the placement name does not match any campaign. Confirm Superwall.configure runs at launch with the correct public API key, that the placement string exactly matches the campaign, and raise the log level to see how the placement is evaluated.

How exact does the placement name have to be?

Exact, including capitalization and underscores. register(placement: "FeatureUnlock") will not match a campaign placement named feature_unlock. Copy the name directly from the dashboard, check for trailing whitespace and typos, and consider centralizing placement names as constants to prevent desync.

Could subscription status be blocking the paywall?

Yes. Superwall does not show a paywall to a user it considers entitled. A stale sandbox subscription or a bug in your purchase controller can mark a user active. Verify the reported status, and if you use RevenueCat, ensure you push the current customer info rather than a cached value.

How do I see why a paywall did or did not show?

Raise the SDK log level to its most verbose setting and reproduce the placement. The logs show that the placement was received, which rule matched or failed, and why a paywall was or was not presented. Pair this with dashboard inspection of the campaign, rules, and variants.

Can a dashboard change break paywalls without an app update?

Yes, because presentation is controlled remotely. Editing a campaign can stop paywalls from showing with no code change. Treat campaign edits as production changes, verify on a real device afterward, and add logging around register calls to detect a sudden drop in paywall impressions.