Use this file to discover all available pages before exploring further.
This guide shows you how to check if a user has an active subscription and gate premium content accordingly. You’ll also learn how to handle user login/logout to sync subscriptions across devices.
Use hasAccess(to:) to check if the user has access to a specific product:
struct ContentView: View { @Environment(Checkout.self) var checkout var body: some View { VStack { // Basic features always available BasicFeaturesView() // Premium features for subscribers if checkout.hasAccess(to: "prod_xxxxxxxxxxxxxx") { PremiumFeaturesView() } // If you have multiple tiers, check each product separately if checkout.hasAccess(to: "prod_yyyyyyyyyyyyyy") { EnterpriseFeaturesView() } } }}
Or use isSubscribed to check if the user has any active subscription:
if checkout.isSubscribed { // User has at least one active membership}
User IDs allow subscriptions to sync across devices. When a user logs in, the SDK automatically claims any unclaimed memberships from the current device and loads their existing memberships.
func handleLogin(userId: String) async { do { try await checkout.logIn(appUserId: userId) // Memberships are now synced for this user } catch { print("Login failed: \(error)") }}
Users can purchase subscriptions even without logging in. The SDK tracks these purchases by device ID and automatically claims them when the user logs in.
struct PurchaseFlow: View { @Environment(Checkout.self) var checkout @State private var showingLogin = false var body: some View { VStack { if checkout.appUserId == nil { // User not logged in - they can still purchase Text("Purchase as guest or log in to sync across devices") Button("Continue as Guest") { // Purchase will be tied to device Task { try? await checkout.purchase("plan_xxx") } } Button("Log In") { showingLogin = true } } else { // User logged in - purchase tied to their account Button("Subscribe") { Task { try? await checkout.purchase("plan_xxx") } } } } .sheet(isPresented: $showingLogin) { // Your login view here } }}
The SDK automatically updates views when subscription status changes. Since Checkout is @Observable, any view using @Environment(Checkout.self) will re-render when: