Skip to main content

Checking Entitlement Status

Use CustomerInfo to check what the user has access to. The SDK keeps this updated in real-time.

Basic Checkโ€‹

// Simple boolean check
if AppActor.shared.customerInfo.entitlements["premium"]?.isActive == true {
showPremiumContent()
} else {
showPaywall()
}

Getting Fresh Dataโ€‹

// Uses 5-minute cache (fast, usually sufficient)
let info = await AppActor.shared.getCustomerInfo()

// Force refresh from StoreKit / server
let info = await AppActor.shared.getCustomerInfo(forceRefresh: true)

Reactive Updates (SwiftUI)โ€‹

AppActor.shared is an ObservableObject with @Published properties. SwiftUI views automatically re-render when customerInfo changes.

struct ContentView: View {
@ObservedObject var appActor = AppActor.shared

var body: some View {
Group {
if appActor.customerInfo.entitlements["premium"]?.isActive == true {
PremiumView()
} else {
FreeView()
}
}
}
}

struct PaywallView: View {
@ObservedObject var appActor = AppActor.shared

var isPremium: Bool {
appActor.customerInfo.entitlements["premium"]?.isActive == true
}

var body: some View {
if isPremium {
Text("You have premium access!")
} else if let offerings = appActor.offerings?.current {
// Show purchase options
ForEach(offerings.packages) { package in
BuyButton(package: package)
}
}
}
}

Detailed Entitlement Infoโ€‹

let info = AppActor.shared.customerInfo

if let premium = info.entitlements["premium"] {
// Access level
print("Active: \(premium.isActive)")

// Subscription details
print("Product: \(premium.productID ?? "none")")
print("Period: \(premium.periodType)") // .monthly, .annual, etc.
print("Expires: \(premium.expirationDate?.description ?? "never")")
print("Will renew: \(premium.willRenew)")

// Billing issues
print("Grace period: \(premium.isInGracePeriod)")
print("Billing retry: \(premium.isInBillingRetry)")
print("Revoked: \(premium.isRevoked)")

// Ownership
print("Family shared: \(premium.ownershipType == .familyShared)")
}

Multiple Entitlementsโ€‹

let info = AppActor.shared.customerInfo

// Check multiple entitlements
let hasPremium = info.entitlements["premium"]?.isActive == true
let hasPro = info.entitlements["pro"]?.isActive == true

// Gate features based on entitlement tier
if hasPro {
showProFeatures()
} else if hasPremium {
showPremiumFeatures()
} else {
showFreeFeatures()
}

Handling Expirationโ€‹

if let premium = info.entitlements["premium"] {
if premium.isActive {
if !premium.willRenew {
// Active but won't renew โ€” show retention offer
showRetentionOffer(expiresAt: premium.expirationDate)
}
} else if premium.isInBillingRetry {
// Payment failing โ€” prompt user to update payment method
showUpdatePaymentPrompt()
} else {
// Expired โ€” show paywall
showPaywall()
}
}

Best Practicesโ€‹

  1. Check entitlements, not products โ€” Use entitlements["key"]?.isActive instead of checking product IDs
  2. Use reactive updates โ€” In SwiftUI, observe AppActor.shared to automatically react to changes
  3. Call onAppForeground() โ€” Refresh state when the app returns to the foreground
  4. Handle grace period โ€” Decide whether to grant access during payment issues
  5. Cache is OK โ€” The 5-minute cache is usually fresh enough. Only force refresh for critical checks.

Next Stepsโ€‹