Repository navigation
0.1.0
One-time purchases and trials over StoreKit 2, for macOS 26 and iOS 26, with a simulated store for testing them.
Early. Non-consumables and trials only. No subscriptions, no consumables. The API may still move before 1.0.
.package(url: "https://github.com/Kosikowski/swift-storekit.git", from: "0.1.0")What is in it
| Product | Link it into | |
|---|---|---|
PurchaseCore |
the app | All the logic. Foundation and Observation only: no StoreKit, no SwiftUI. |
PurchaseStoreKit |
the app | AppStoreFront: the App Store behind Core's protocols. |
PurchaseUI |
the app | .purchaseStore(_:), PurchaseButton, RestorePurchasesButton. No paywall. |
PurchaseTestKit, PurchaseDebugUI |
the app | A simulated store, scenarios, a debug panel. DEBUG only, whole: in a release build both modules are empty. |
PurchaseTestSupport |
test targets only | ManualClock, waitUntil, a .storekit validator. |
PurchaseDirectDistribution |
a build sold outside the App Store | EverythingOwnedStoreFront. |
What it gets right, so that your app does not have to
Each of these was found in shipping code or measured against real StoreKit, and has a test:
- Ownership and prices are read in a task nothing can cancel. Real StoreKit answers a cancelled task with nothing owned and an empty product list — not errors — and SwiftUI cancels
.taskwhenever a view goes away. - A purchase unlocks at once, though StoreKit may list it a second after
purchase()returns. - "Not answered yet" is a state (
unknownis notnone), and ownership never waits for prices. - A purchase that does not verify — or a result StoreKit adds later — is a failure that gets said, never a cancellation.
- A trial is a free non-consumable dated by the App Store: the same trial on every device, and it ends by itself.
- An app using the package links in Release. (With Xcode 27, a package's public function returning
some Viewthat ends in.taskdoes not; see decision D25.)
Testing
swift test runs everything that decides anything, offline. make check adds the layer check, a release test run, the iOS build, builds of the Demo, and swift package release-check, which proves the simulated store is absent from a release build — of the package, and of a built app. The real adapter runs against real StoreKit from a hosted test bundle, on the Mac and in an iOS simulator, nightly. What StoreKit was measured to do, per OS and per Xcode, is in docs/05-testing.md, docs/10-decisions.md and spike/README.md.
Start with getting started; if you have hand-written StoreKit 2 code already, migrating an existing app.