https://github.com/jaywcjlove/StoreKitHelper
A lightweight StoreKit2 wrapper designed specifically for SwiftUI, making it easier to implement in-app purchases.
https://github.com/jaywcjlove/StoreKitHelper
apple iap in-app-purchase ios ios-swift jaywcjlove macos payment purchase receipt storekit storekit2 swift swiftui
Last synced: 4 months ago
JSON representation
A lightweight StoreKit2 wrapper designed specifically for SwiftUI, making it easier to implement in-app purchases.
- Host: GitHub
- URL: https://github.com/jaywcjlove/StoreKitHelper
- Owner: jaywcjlove
- Created: 2025-03-06T14:33:55.000Z (7 months ago)
- Default Branch: main
- Last Pushed: 2025-06-04T11:42:14.000Z (4 months ago)
- Last Synced: 2025-06-05T08:03:46.457Z (4 months ago)
- Topics: apple, iap, in-app-purchase, ios, ios-swift, jaywcjlove, macos, payment, purchase, receipt, storekit, storekit2, swift, swiftui
- Language: Swift
- Homepage:
- Size: 109 KB
- Stars: 111
- Watchers: 2
- Forks: 7
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
Awesome Lists containing this project
README
StoreKit Helper
===A lightweight StoreKit2 wrapper designed specifically for SwiftUI, making it easier to implement in-app purchases.

✦ My macOS/iOS application ✦
## Documentation
Please refer to the detailed `StoreKitHelper` [documentation](https://github.com/jaywcjlove/devtutor) in [DevTutor](https://github.com/jaywcjlove/devtutor), which includes multiple quick start examples, custom payment interface examples, and API references, providing comprehensive examples and guidance.
## Usage
At the entry point of the SwiftUI application, create and inject a `StoreContext` instance, which is responsible for loading the product list and tracking purchase status.
```swift
import StoreKitHelperenum 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)
}
}
}
```Use `StoreKitHelperView` to directly display an in-app purchase popup view and configure various parameters through a chained API.
```swift
struct PurchaseContent: View {
@EnvironmentObject var store: StoreContext
var body: some View {
StoreKitHelperView()
.frame(maxWidth: 300)
.frame(minWidth: 260)
// Triggered when the popup is dismissed (e.g., user clicks the close button)
.onPopupDismiss {
store.isShowingPurchasePopup = false
}
// Sets the content area displayed in the purchase interface
// (can include feature descriptions, version comparisons, etc.)
.pricingContent {
AnyView(PricingContent())
}
.termsOfService {
// Action triggered when the [Terms of Service] button is clicked
}
.privacyPolicy {
// Action triggered when the [Privacy Policy] button is clicked
}
}
}
```Click to open the paid product list interface.
```swift
struct PurchaseButton: View {
@EnvironmentObject var store: StoreContext
var body: some View {
if store.hasNotPurchased == true {
PurchasePopupButton()
.sheet(isPresented: $store.isShowingPurchasePopup) {
/// Popup with the paid product list
PurchaseContent()
}
}
}
}
```You can use the `hasNotPurchased` property in `StoreContext` to check if the user has made a purchase, and then dynamically display different interface content. For example:
```swift
@EnvironmentObject var store: StoreContextvar body: some View {
if store.hasNotPurchased == true {
// 🧾 User has not purchased - Show restricted content or prompt for purchase
} else {
// ✅ User has purchased - Show full features
}
}
```### filteredProducts
This is a simple migration solution: the product list is filtered by product ID, retaining the old product IDs so existing users don’t need to repurchase and can restore their purchases, while new users purchase through the new product IDs, achieving a smooth transition.
```swift
enum AppProduct: String, InAppProduct {
/// old
case sponsor = "focuscursor.Sponsor"
case generous = "focuscursor.Generous"
/// new
case monthly = "focuscursor.monthly"
case lifetime = "focuscursor.lifetime"
var id: String { rawValue }
}StoreKitHelperView()
.filteredProducts() { productID, product in
if productID == AppProduct.sponsor.rawValue {
return false
}
if productID == AppProduct.generous.rawValue {
return false
}
return true
}StoreKitHelperSelectionView()
.filteredProducts() { productID, product in
return true
}
```## License
Licensed under the MIT License.