Consumable Purchases with the Superwall SDK
Learn about how consumable purchases, like in-app currency and credits, work with the Superwall SDK for more flexible monetization beyond subscriptions.
Overview
Superwall's SDK extends beyond subscription models to support consumable products such as game coins and app credits. By default, the SDK treats consumable purchases identically to subscriptions when purchased:
// This will be true when a consumable, or non-consumable, is purchased
if Superwall.shared.subscriptionStatus == .active {
showSubscribedAccountView()
}If this behavior suits your needs, no additional implementation is required.
Customizing Paywall Presentation
There are two approaches when you need different behavior:
Option 1: Always Display Paywalls
Configure your paywall to always display regardless of purchase history. Navigate to Sidebar -> Settings and set the "Present Paywall" option to "Always".

Option 2: Manual Subscription Status Management
For more complex logic, implement a purchase controller to manage subscription states manually. This approach requires creating aPurchaseController that handles purchasing, restoration, and subscription status updates.
Creating a Purchase Controller
Adopt the PurchaseController protocol and implement required functions:
class PurchaseManager: PurchaseController {
func purchase(product: SKProduct) async -> SuperwallKit.PurchaseResult {
// Purchase a product
}
func restorePurchases() async -> SuperwallKit.RestorationResult {
// Restore one
}
}Sample Implementation
class PurchasesManager: PurchaseController {
func purchase(product: SKProduct) async -> SuperwallKit.PurchaseResult {
let result = await SomeProductPurchaseUtil.purchase(product)
switch result {
case .success:
return .purchased
case .userCancelled:
return .cancelled
case .pendingPurchase:
return .pending
case .alreadyPurchased:
return .restored
case .failed(let error):
return .failed(error)
}
}
func restorePurchases() async -> SuperwallKit.RestorationResult {
do {
try await AppStore.sync()
try await updateUserPurchases()
return .restored
} catch {
return .failed(error)
}
}
}Handling Subscription States
Monitor purchase and subscription changes from your source of truth, then update Superwall accordingly:
class PurchasesManager: PurchaseController {
let purchaseListener = PurchaseListener()
init() {
purchaseListener.subscriptionStatusChanged { status in
switch status {
case .onlyConsumables:
Superwall.shared.subscriptionStatus = .inactive
case .purchaseSubscription, purchasedBoth:
Superwall.shared.subscriptionStatus = .active
}
}
}
func purchase(product: SKProduct) async -> SuperwallKit.PurchaseResult {
let result = await ProductPurchase.purchase(product)
switch result {
case .success(let result):
return .purchased
case .userCancelled:
return .cancelled
case .pending:
return .pending
@unknown default:
fatalError()
}
}
func restorePurchases() async -> SuperwallKit.RestorationResult {
do {
try await AppStore.sync()
try await updateUserPurchases()
return .restored
} catch {
return .failed(error)
}
}
}Maintaining accurate subscription status is critical, as it affects paywall presentation and other SDK functionality.
SDK Initialization
Pass your purchase controller during Superwall initialization:
@main
struct MyApp: App {
let purchaseUtil: PurchasesManager = .init()
init() {
Superwall.configure(apiKey: "YourAPIKey",
purchaseController: purchaseUtil)
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}Summary
Superwall supports flexible monetization strategies beyond subscriptions, including consumable and non-consumable products. By implementing a purchase controller, you maintain complete control over purchasing and subscription state management, enabling diverse revenue models tailored to your app's needs.