Skip to content

A lightweight StoreKit2 wrapper designed specifically for SwiftUI, making it easier to implement in-app purchases.

License

Notifications You must be signed in to change notification settings

jaywcjlove/StoreKitHelper

Repository files navigation

Using my apps is also a way to support me:
Screen Test Deskmark Keyzer Vidwall Hub VidCrop Vidwall Mousio Hint Mousio Musicer Audioer FileSentinel FocusCursor Videoer KeyClicker DayBar Iconed Mousio Quick RSS Quick RSS Web Serve Copybook Generator DevTutor for SwiftUI RegexMate Time Passage Iconize Folder Textsound Saver Create Custom Symbols DevHub Resume Revise Palette Genius Symbol Scribe

StoreKit Helper

中文

A lightweight StoreKit2 wrapper designed specifically for SwiftUI, making in-app purchases implementation simpler and more intuitive.

StoreKit Helper

Documentation

Please refer to the detailed StoreKitHelper documentation in DevTutor, which includes multiple quick start examples, custom payment interface examples, and API references, providing comprehensive examples and guidance.

Features

  • 🚀 SwiftUI Native: Designed specifically for SwiftUI with @ObservableObject and @EnvironmentObject support
  • 💡 Simple API: Clean and intuitive interface for managing in-app purchases
  • 🔄 Automatic Updates: Real-time transaction monitoring and status updates
  • Type Safe: Protocol-based product definitions with compile-time safety
  • 🧪 Testable: Fully testable architecture with comprehensive test case coverage ExampleTests.swift/StoreKitHelperTests.swift

Usage

Create and inject a StoreContext instance at your SwiftUI app's entry point, which is responsible for loading the product list and tracking purchase status.

import StoreKitHelper

enum AppProduct: String, InAppProduct {
    case lifetime = "focuscursor.lifetime"
    case monthly = "focuscursor.monthly"
    var id: String { rawValue }
}

@main struct DevTutorApp: App {
    @StateObject var store = StoreContext(products: AppProduct.allCases)
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(store)
        }
    }
}

You can use the hasNotPurchased or hasPurchased properties in StoreContext to check if the user has made a purchase, then dynamically display different interface content. For example:

@EnvironmentObject var store: StoreContext

var body: some View {
    if store.hasNotPurchased == true {
        // 🧾 User hasn't purchased - show limited content or purchase prompt
    } else {
        // ✅ User has purchased - show full functionality
    }
    if store.hasPurchased == true {
        // ✅ User has purchased - show full functionality
    } else {
        // 🧾 User hasn't purchased - show limited content or purchase prompt
    }
}

StoreKitHelperView

Use StoreKitHelperView to directly display in-app purchase popup views and configure various parameters through a chainable API.

struct PurchaseContent: View {
    @EnvironmentObject var store: StoreContext
    var body: some View {
        let locale: Locale = Locale(identifier: Locale.preferredLanguages.first ?? "en")
        StoreKitHelperView()
            .environment(\.locale, .init(identifier: locale.identifier))
            .environment(\.pricingContent, { AnyView(PricingContent()) })
            .environment(\.popupDismissHandle, {
                // Triggered when the popup is dismissed 
                // (e.g., user clicks the close button)
                store.isShowingPurchasePopup = false
            })
            .environment(\.termsOfServiceHandle, {
                // Action triggered when the [Terms of Service] button is clicked
            })
            .environment(\.privacyPolicyHandle, {
                // Action triggered when the [Privacy Policy] button is clicked
            })
            .frame(maxWidth: 300)
            .frame(minWidth: 260)
    }
}

Click to open the paid product list interface.

struct ContentView: View {
    @EnvironmentObject var store: StoreContext
    var body: some View {
        if store.hasNotPurchased == true, store.isLoading == false {
            PurchasePopupButton()
                .sheet(isPresented: $store.isShowingPurchasePopup) {
                    PurchaseContent()
                }
        }
    }
}

StoreKitHelperSelectionView

Similar to StoreKitHelperView, but for selecting purchase items to make payments.

struct PurchaseContent: View {
    @EnvironmentObject var store: StoreContext
    var body: some View {
        let locale: Locale = Locale(identifier: Locale.preferredLanguages.first ?? "en")
        StoreKitHelperSelectionView()
            .environment(\.locale, .init(identifier: locale.identifier))
            .environment(\.pricingContent, { AnyView(PricingContent()) })
            .environment(\.popupDismissHandle, {
                // Triggered when the popup is dismissed 
                // (e.g., user clicks the close button)
                store.isShowingPurchasePopup = false
            })
            .environment(\.termsOfServiceHandle, {
                // Action triggered when the [Terms of Service] button is clicked
            })
            .environment(\.privacyPolicyHandle, {
                // Action triggered when the [Privacy Policy] button is clicked
            })
            .frame(maxWidth: 300)
            .frame(minWidth: 260)
    }
}

API Reference

InAppProduct Protocol

protocol InAppProduct: CaseIterable {
    var id: String { get }
}

StoreContext Properties

  • products: [Product] - Available products from the App Store
  • purchasedProductIDs: Set<String> - Set of purchased product identifiers
  • hasNotPurchased: Bool - Whether the user hasn't purchased any products
  • hasPurchased: Bool - Whether the user has purchased any products
  • isLoading: Bool - Whether products are currently loading
  • errorMessage: String? - Current error message, if any

StoreContext Methods

  • purchase(_ product: Product) - Purchase a specific product
  • restorePurchases() - Restore previous purchases
  • isPurchased(_ productID: ProductID) -> Bool - Check if a product is purchased by ID
  • isPurchased(_ product: InAppProduct) -> Bool - Check if a product is purchased
  • product(for productID: ProductID) -> Product? - Get product by ID
  • product(for product: InAppProduct) -> Product? - Get product by InAppProduct

License

Licensed under the MIT License.