A Swift package for subscription management using RevenueCat.
English | ζ₯ζ¬θͺ
π Full Documentation
The Subscription package integrates with RevenueCat to provide a high-level API for implementing in-app purchases and subscriptions.
- β Check and observe subscription status
- β Fetch available plans
- β Purchase and restore plans
- β User authentication integration
- β SwiftUI-ready (async/await, AsyncStream)
- β Actor-based thread-safe design
- iOS 17.0+ / macOS 14.0+
- Swift 6.0+
- RevenueCat SDK 5.14.0+
Complete the following steps in the RevenueCat Dashboard:
-
Create a project
- Create a new project and obtain your API key
-
Configure products
- Create subscription products in App Store Connect / Google Play Console
- Import products into the RevenueCat Dashboard
- Configure entitlements (e.g., "premium")
-
Create an offering
- Group plans (monthly, annual, etc.) into an offering
- Set the default offering
Add the following to your Package.swift:
dependencies: [
.package(url: "https://github.com/no-problem-dev/swift-subscription.git", from: "1.0.4")
]Or add it via File > Add Package Dependencies... in Xcode using https://github.com/no-problem-dev/swift-subscription.
import Subscription
let config = SubscriptionConfiguration(
apiKey: "your_revenuecat_api_key",
entitlementId: "premium"
)
let subscriptionUseCase = SubscriptionUseCaseImpl(configuration: config)// Get cached status immediately
let status = await subscriptionUseCase.getSubscriptionStatus()
// Fetch the latest status from the server
let latestStatus = try await subscriptionUseCase.checkSubscriptionStatus()
if latestStatus.isActive {
print("Active plan: \(latestStatus.activePackageId ?? "unknown")")
}// Fetch available plans
let offerings = try await subscriptionUseCase.loadOfferings()
if let packages = offerings?.packages {
for package in packages {
print("\(package.title): \(package.price)")
}
// Purchase a plan
if let package = packages.first {
let status = try await subscriptionUseCase.purchase(packageId: package.id)
}
}// Observe subscription status changes
Task {
for await status in subscriptionUseCase.observeSubscriptionStatus() {
if status.isActive {
// Enable premium features
}
}
}import SwiftUI
import Subscription
struct ContentView: View {
@Environment(\.subscriptionUseCase) private var subscriptionUseCase
@State private var status: SubscriptionStatus = .inactive
var body: some View {
VStack {
if status.isActive {
Text("Premium Member")
} else {
Button("Subscribe to Premium") {
Task { await showPaywall() }
}
}
}
.task {
guard let subscriptionUseCase else { return }
for await newStatus in subscriptionUseCase.observeSubscriptionStatus() {
status = newStatus
}
}
}
private func showPaywall() async {
// Paywall presentation
}
}Inject the use case at the App entry point:
@main
struct MyApp: App {
private let subscriptionUseCase = SubscriptionUseCaseImpl(
configuration: SubscriptionConfiguration(apiKey: "your_revenuecat_api_key")
)
var body: some Scene {
WindowGroup {
ContentView()
.subscriptionUseCase(subscriptionUseCase)
}
}
}// On user login
func userDidLogin(userId: String) async throws {
try await subscriptionUseCase.syncUser(userId: userId)
}
// On user logout
func userDidLogout() async throws {
try await subscriptionUseCase.clearUser()
}do {
let status = try await subscriptionUseCase.purchase(packageId: packageId)
} catch let error as SubscriptionError {
switch error {
case .purchaseCancelled:
// User cancelled β no error display needed
break
case .networkError:
// Prompt retry on network error
showRetryAlert()
default:
showAlert(message: error.localizedDescription)
}
}- RevenueCat SDK (5.14.0+)
MIT License
For issues or feature requests, please create a GitHub Issue.