Skip to content

Payments

LaunchSwift’s payments feature is implemented with StoreKit 2 primitives and an @Observable state object.

  • ios/SwiftLaunch/Features/Payments/PaymentState.swift — purchase/loading/error state + entitlement cache
  • ios/SwiftLaunch/Features/Payments/Services/StoreKitService.swift — StoreKit 2 adapter
  • ios/SwiftLaunch/Features/Payments/Models/PaymentModels.swiftPaymentError, StoreProduct, StoreTransaction, SubscriptionTier
  • ios/SwiftLaunch/Features/Payments/PaymentsConfig.swift — product ID mapping from AppConfig.current.productIDs
  • ios/SwiftLaunch/Features/Payments/PaywallView.swift — paywall UI
  • ios/SwiftLaunch/Features/Payments/PremiumGate.swift — premium access gate helper
  • ios/SwiftLaunch/Features/Payments/SubscriptionState.swift — compatibility wrapper around PaymentState

SubscriptionTier in PaymentModels.swift:

enum SubscriptionTier: String, CaseIterable, Sendable {
case monthly
case yearly
case lifetime
}

Tier product IDs resolve through PaymentsConfig.Products, which reads from AppConfig.current.productIDs.

Default product IDs in AppConfig.swift:

  • com.swiftlaunch.pro.monthly
  • com.swiftlaunch.pro.annual
  • com.swiftlaunch.pro.lifetime
@Observable
final class PaymentState {
var products: [any StoreProduct] = []
var purchasedProductIDs: Set<String> = []
var isLoading = false
var error: (any Error)?
func loadProducts() async
func purchase(_ product: any StoreProduct) async
func restorePurchases() async
func refreshEntitlements() async
}

PaymentState also:

  • restores cached entitlements from UserDefaults
  • listens for Transaction.updates via StoreKitService.listenForTransactionUpdates()
  • refreshes entitlements when .paymentEntitlementsDidChange is posted
  1. loadProducts() requests products using SubscriptionTier.allProductIDs
  2. purchase(_:) executes StoreKit purchase and updates purchasedProductIDs
  3. restorePurchases() calls AppStore.sync() and reloads entitlements
  4. refreshEntitlements() reads Transaction.currentEntitlements
  5. PaywallView dismisses automatically when purchased IDs become non-empty

PaywallView is in:

  • ios/SwiftLaunch/Features/Payments/PaywallView.swift

It uses PaymentState directly and renders packages from loaded StoreKit products.

  • The feature currently uses StoreKitServiceProtocol (defined in StoreKitService.swift) for StoreKit abstraction.
  • The primary entitlement refresh method in PaymentState is refreshEntitlements().