hobbysoft.pro

Expertise

StoreKit 2 subscription implementation for iOS apps

I implement in-app purchases with StoreKit 2: subscriptions, lifetime unlocks and free tiers that convert, with entitlement logic that holds up through renewals, refunds and restores. My own apps use these models in production, from Fokus Zone Premium to the monthly, yearly and lifetime PRO options in My Disc Golf.

By HobbySoft, iOS & Mac developer Updated

StoreKit 2 subscriptions, implemented end to end

StoreKit 2 is Apple’s Swift-native API for in-app purchases. It loads products, runs purchases with async/await and returns transactions signed by the App Store, so the app can verify them without parsing receipts. Most paid iOS apps today run on some mix of:

  • Auto-renewable subscriptions: monthly and yearly plans in a subscription group.
  • Non-consumables: a lifetime unlock or a one-off feature pack.
  • A free tier: the app is useful without paying, and the paid tier removes limits.

The purchase button is the easy part. The work is making sure every user sees exactly what they paid for, on every device, after every renewal, refund and reinstall.

Where StoreKit 2 implementations go wrong

Missing transaction updates

Purchases do not only happen behind your paywall. Renewals, Ask to Buy approvals, purchases on another device and offer code redemptions arrive through Transaction.updates. The app must start listening at launch, verify each VerificationResult, update entitlements and call finish(). Apps that skip this have users who paid and still see the paywall.

Entitlements computed in the wrong place

Transaction.currentEntitlements is the source of truth for what the user owns right now. I compute access from it in one entitlement service that the whole app observes, instead of caching an “isPro” flag at purchase time. That one decision prevents a whole class of “I paid but it’s locked” support emails. Revoked transactions, for example after a refund, carry a revocationDate and must remove access.

Subscription edge cases

Billing problems, grace periods, upgrades and downgrades within a group, and pending purchases each need a defined behavior. Product.SubscriptionInfo exposes status and renewal information, so the app can show “your payment failed” instead of silently locking features. I design these states with you up front rather than discovering them in reviews.

Restore and multiple devices

AppStore.sync() exists for the restore button, but a correct entitlement model means users rarely need it. Mixing a lifetime product with subscriptions needs the same care: the lifetime purchase must win regardless of subscription state.

Server side, when you need it

If access must also work on a website or another platform, a backend should verify signed transactions with the App Store Server API and receive App Store Server Notifications V2 for renewals, refunds and expirations. That keeps the server and the app agreeing on who has access.

Testing and review

A local StoreKit configuration file makes renewals and refunds testable in minutes. App Review checks that pricing is clear, restore works and the paywall explains what the user gets, so those are part of the implementation, not an afterthought.

Purchase models in my own apps

I use three different models across my own products, which is the best way to learn where each one breaks:

AppModelWhat it shows
Fokus ZoneFree, with a Premium subscriptionA free core app; Premium unlocks unlimited restrictions, advanced scheduling and detailed analytics
My Disc GolfFree, with PRO monthly, yearly or lifetimeSubscriptions and a one-time lifetime unlock granting the same features
Haptic ForgeOne-time purchaseNo subscription, no ads, no account, for a professional tool

Each model asks different questions: what stays free, what a lifetime buyer gets when new features arrive, and how the app behaves when a subscription lapses. Choosing a model is a product decision as much as a technical one, and I am happy to talk it through with you.

In-app purchase work I take on

  • A full StoreKit 2 setup: products, paywall, entitlement service, restore, and analytics hooks.
  • Hybrid models: free tier, subscriptions and lifetime unlocks living together.
  • Migration from original StoreKit or a third-party purchase SDK to StoreKit 2. See app rescue and modernization.
  • Server-side verification and App Store Server Notifications handling with your backend team.
  • A review of an existing setup that has “paid but locked” complaints.

Get your purchases right

Tell me your pricing model and where users hit the paywall, at contact@hobbysoft.pro. I will point out the edge cases before your users find them.

FAQ

Questions and answers

Should I migrate from original StoreKit to StoreKit 2?

For most apps, yes. StoreKit 2 replaces receipt parsing with signed transactions and async APIs, which removes a lot of fragile code. Migration can be done gradually because purchases made with either API are visible to the other.

Do I need a server for in-app subscriptions?

Not always. StoreKit 2 verifies transactions on device, which is enough for many apps. A server becomes worth it when entitlements must work outside the app, on the web or another platform, or when you want App Store Server Notifications for analytics and support.

Can an app offer both subscriptions and a lifetime purchase?

Yes. A common setup is an auto-renewable subscription group plus a non-consumable lifetime product, with the app granting access if either is active.

How do you test subscriptions before release?

With a StoreKit configuration file in Xcode for fast local tests, including renewals, expirations and refunds, then sandbox and TestFlight testing against App Store Connect products.

Have an app in mind? Let’s ship it.

I take on a small number of projects at a time and stay with them to the finish: one accountable developer, an EU company with clear invoicing, and support long after launch.

Hire me