Skip to main content

Presenting Offers with Superwall

Encore works alongside Superwall so you can present retention offers at key moments in your paywall flow.

Option 1: On Paywall Dismissal

Trigger Encore when a user dismisses your Superwall paywall without converting:
import SuperwallKit
import Encore

// Listen to Superwall's paywall dismissal
class SWDelegate: SuperwallDelegate {
    func willDismissPaywall(withInfo paywallInfo: PaywallInfo) {
        // User dismissed without purchasing
        Encore.placement().show()
    }
}
Use the same fluent API documented here: placement()

Option 2: Custom Paywall Action

Create/Update the Superwall delegate to handle custom actions from your paywall:
import SuperwallKit
import Encore

// Listen to Superwall's custom actions
class SWDelegate: SuperwallDelegate {
    func handleCustomPaywallAction(withName name: String) {
        // Action is EncoreTrigger
        if name == "EncoreTrigger" {
            Encore.placement()
                .onGranted { entitlements in
                    Superwall.shared.dismiss()
                }
                .onNotGranted { reason in
                    Superwall.shared.dismiss()
                }
                .show()
        }
    }
}
Then, in the Superwall editor, add a custom action named “EncoreTrigger” to any button or element in your paywall.
Use the same fluent API documented here: placement()

Tracking Entitlements with Superwall

Simple async check

Combine Encore’s entitlement status with Superwall’s current subscription state when you only need a single evaluation:
Task {
  let hasEncoreTrial = await Encore.shared.isActive(.freeTrial())
  let hasProAccess = hasEncoreTrial || (Superwall.shared.subscriptionStatus != .inactive)
}

Reactive check (Combine)

To keep hasProAccess updated automatically, combine Superwall subscription updates with Encore’s publisher:
import Foundation
import Encore
import Combine
import SuperwallKit

class EntitlementManager {
    @Published var hasProAccess: Bool = false
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        // Combine Superwall subscription status with Encore promotional rewards
        Publishers.CombineLatest(
            Superwall.shared.$subscriptionStatus,
            Encore.shared.isActivePublisher(for: .freeTrial()),
        )
        .map { superwallActive, encoreActive in
            // User has pro access if they have an active subscription from ANY source
            return superwallActive == .active || encoreActive
        }
        .assign(to: \.hasProAccess, on: self)
        .store(in: &cancellables)
    }
}
The superwallActive variable represents the Superwall subscription status and should be checked against .active.