Flare is a modern, lightweight Swift framework that simplifies working with in-app purchases and subscriptions. Built on top of StoreKit and StoreKit 2, it provides a clean, unified API with async/await support and includes ready-to-use UI components for seamless integration across all Apple platforms.
- 🛍️ Complete Purchase Support - Consumable, non-consumable, and subscription purchases
- 🎁 Promotional Offers - Support for promotional and introductory offers
- ⚡ Modern Swift - Built with async/await for clean, readable code
- 🔄 StoreKit 1 & 2 - Unified API supporting both StoreKit versions
- 🎨 UI Components - Pre-built SwiftUI and UIKit views for product displays
- 📱 Cross-Platform - iOS, tvOS, watchOS, macOS, and visionOS compatible
- 🧪 Thoroughly Tested - Complete unit, integration, and snapshot test coverage
| Package | Supported Platforms | Xcode | Minimum Swift Version |
|---|---|---|---|
| Flare | iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 7.0+ / visionOS 1.0+ | 15.4+ | 5.10 |
| FlareUI | iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ | 15.4+ | 5.10 |
Add the following dependency to your Package.swift:
dependencies: [
.package(url: "https://github.com/space-code/flare.git", from: "3.3.0")
]Or via Xcode:
- File > Add Package Dependencies
- Enter package URL:
https://github.com/space-code/flare.git - Select version requirements
The package contains two libraries:
- Flare - Core in-app purchase functionality
- FlareUI - Ready-to-use UI components for SwiftUI and UIKit
import Flare
// Configure Flare
Flare.configure(with: .init(applicationUsername: "UUID"))
// Fetch products
let products = try await Flare.shared.fetch(productIDs: ["premium_monthly"])
// Purchase a product
let transaction = try await Flare.shared.purchase(product: products.first!)
// Finish the transaction
Flare.shared.finish(transaction: transaction, completion: nil)Retrieve product information from the App Store:
import Flare
// Fetch single product
let productIDs = ["com.app.premium"]
let products = try await Flare.shared.fetch(productIDs: productIDs)
// Fetch multiple products
let subscriptionIDs = [
"com.app.monthly",
"com.app.yearly",
"com.app.lifetime"
]
let subscriptions = try await Flare.shared.fetch(productIDs: subscriptionIDs)
// Access product details
for product in products {
print("Product: \(product.localizedTitle)")
print("Price: \(product.localizedPriceString)")
print("Description: \(product.localizedDescription)")
}Handle purchases with simple async/await syntax:
import Flare
func purchasePremium() async throws {
let productID = "com.app.premium"
let products = try await Flare.shared.fetch(productIDs: [productID])
guard let product = products.first else {
throw IAPError.storeProductNotAvailable
}
do {
let transaction = try await Flare.shared.purchase(product: product)
await unlockPremiumFeatures()
Flare.shared.finish(transaction: transaction, completion: nil)
} catch {
guard let error = error as? IAPError else { return }
switch error {
case .paymentCancelled:
print("❌ User cancelled the purchase")
default:
print("❌ Purchase failed: \(error)")
}
}
}Work with subscription products and their states:
import Flare
func purchaseSubscription(product: StoreProduct) async throws {
do {
let transaction = try await Flare.shared.purchase(product: product)
Flare.shared.finish(transaction: transaction, completion: nil)
} catch {
guard let error = error as? IAPError else { return }
// Handle the error
}
}Manage transaction lifecycle:
import Flare
// Finish a transaction after delivering content
func completeTransaction(_ transaction: StoreTransaction) {
Flare.shared.finish(transaction: transaction) {
print("✅ Transaction completed successfully")
}
}
// Restore previous purchases
func restorePurchases() async throws {
do {
try await Flare.shared.restore()
print("✅ Purchases restored successfully")
} catch {
print("❌ Failed to restore purchases: \(error)")
}
}
// Listen for transaction updates
func observeTransactions() {
Flare.shared.addTransactionObserver { result in
switch result {
case let .success(transaction):
handleTransaction(transaction)
case let .failure(error):
print("Transaction error: \(error)")
}
}
}Support promotional and introductory offers:
import Flare
func purchaseWithOffer(product: StoreProduct, offer: PromotionalOffer) async throws {
do {
let transaction = try await Flare.shared.purchase(
product: product,
promotionalOffer: offer
)
print("✅ Purchased with promotional offer!")
Flare.shared.finish(transaction: transaction, completion: nil)
} catch {
guard let error = error as? IAPError else { return }
// Handle the error
}
}FlareUI provides ready-to-use UI components for displaying products and handling purchases in both SwiftUI and UIKit.
Display products with built-in SwiftUI views:
import SwiftUI
import FlareUI
struct StoreView: View {
var body: some View {
NavigationView {
SubscriptionsView(ids: ["com.product.subscription"])
.navigationTitle("Premium Features")
}
}
}Integrate with UIKit applications:
import UIKit
import FlareUI
let subscriptionsVC = SubscriptionsViewController(ids: [com.company.subscription_id])
let nav = UINavigationController(rootViewController: subscriptionsVC)
present(nav, animated: true)Comprehensive documentation is available:
- Flare Documentation - Core framework integration and APIs
- FlareUI Documentation - UI components, customization, and integration guides
We love contributions! Please read our Contributing Guide to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes.
Bootstrap the development environment:
mise installThis project adheres to the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code.
Nikita Vasilev
- Email: [email protected]
- GitHub: @ns-vasilev
Flare is released under the MIT license. See LICENSE for details.
Made with ❤️ by space-code