Learn the core concepts and build your first premium feature in 10 minutes
InAppKit works with three simple concepts:
Product("com.yourapp.pro") // A product ID from App Store Connectenum AppFeature: String, AppFeature {
case removeAds = "remove_ads"
case cloudSync = "cloud_sync"
case exportPDF = "export_pdf"
}.withPaywall { context in
Text("Upgrade to Pro!") // Your custom paywall UI
}InAppKit uses a consistent Product API pattern:
Simple rule: Need features? Always use
features:parameter
// No features
Product("com.app.basic")
// Enum features
Product("com.app.pro", features: [AppFeature.sync, AppFeature.export])
// All enum cases
Product("com.app.premium", features: AppFeature.allCases)
// String features (for simple cases)
Product("com.app.custom", features: ["feature1", "feature2"])// Don't use unlabeled parameters (removed for consistency)
Product("com.app.pro", [.sync, .export]) // ❌
Product("com.app.premium", AppFeature.allCases) // ❌Let's build a note-taking app with premium features.
import InAppKit
enum NoteFeature: String, AppFeature {
case unlimitedNotes = "unlimited_notes"
case cloudSync = "cloud_sync"
case exportPDF = "export_pdf"
case customThemes = "custom_themes"
}ContentView()
.withPurchases(products: [
Product("com.noteapp.pro", features: [
NoteFeature.unlimitedNotes,
NoteFeature.cloudSync,
NoteFeature.exportPDF
]),
Product("com.noteapp.premium", features: NoteFeature.allCases)
])struct NotesListView: View {
var body: some View {
VStack {
// Free content
ForEach(freeNotes) { note in
NoteRow(note: note)
}
// Premium content
ForEach(premiumNotes) { note in
NoteRow(note: note)
.requiresPurchase(NoteFeature.unlimitedNotes)
}
// Export button
Button("Export PDF") {
exportToPDF()
}
.requiresPurchase(NoteFeature.exportPDF)
}
}
}ContentView()
.withPurchases(products: [...])
.withPaywall { context in
VStack {
Text("Unlock Premium Features")
.font(.title)
ForEach(context.availableProducts, id: \\.id) { product in
ProductRow(product: product)
}
}
.padding()
}struct ExportButton: View {
@State private var canExport = false
var body: some View {
Button("Export PDF") {
exportToPDF()
}
.disabled(!canExport)
.onAppear {
canExport = InAppKit.shared.hasAccess(to: NoteFeature.exportPDF)
}
}
}// One product unlocks everything
ContentView()
.withPurchases("com.app.pro")
Text("Premium Feature")
.requiresPurchase() // Uses default product// Different products unlock different features
ContentView()
.withPurchases(products: [
Product("com.app.basic", features: [Feature.removeAds]),
Product("com.app.pro", features: [Feature.removeAds, Feature.cloudSync]),
Product("com.app.premium", features: Feature.allCases)
])// Combine subscriptions with feature gating
ContentView()
.withPurchases(products: [
Product("com.app.monthly", features: [Feature.premiumContent]),
Product("com.app.annual", features: [Feature.premiumContent, Feature.prioritySupport])
])// Show calculated savings to encourage annual subscriptions
ContentView()
.withPurchases(products: [
Product("com.app.monthly", features: features),
Product("com.app.yearly", features: features)
.withRelativeDiscount(comparedTo: "com.app.monthly")
// Automatically displays "Save 31%" (calculated from actual prices)
.withBadge("Best Value", color: .green)
])Different Display Styles:
// Show dollar amount saved
.withRelativeDiscount(comparedTo: "monthly", style: .amount)
// Displays: "Save $44"
// Show free months
.withRelativeDiscount(comparedTo: "monthly", style: .freeTime)
// Displays: "2 months free"
// Custom color
.withRelativeDiscount(comparedTo: "monthly", color: .purple)
// Displays in purple instead of default orangestruct ContentView: View {
@State private var notesCount = 0
var body: some View {
VStack {
if notesCount >= 5 {
Text("Create unlimited notes")
.requiresPurchase(Feature.unlimitedNotes)
} else {
Button("Add Note") { notesCount += 1 }
}
}
}
}Monetization Patterns → Learn which pattern fits your app best
Customization Guide → Add marketing info, custom UI, and advanced features
API Reference → Complete documentation and advanced configuration
Questions? Check our troubleshooting guide or open an issue.