Skip to content

Release 4.0.0

Latest

Choose a tag to compare

@Ramiz69 Ramiz69 released this 19 Oct 13:30
· 7 commits to main since this release
b563917

PurchaseKit 4.0 covers what StoreKit 2 reports about subscriptions, offers and transactions, and makes the
kit's own types extensible, so a value Apple adds later is no longer a breaking release.

Upgrading from 3.x? Read Migrating to PurchaseKit 4. It shows every break below with before-and-after code.

What broke, and why

  • ProductType and PurchasesError are structs, not enums. As enums, every value StoreKit added was a
    breaking release: OS 27's subscription bundles and suites would have been one. Now they are
    RawRepresentable structs, the pattern StoreKit itself uses. A switch over them needs a default branch.
    • ProductType.unknown is gone. A product kind with no constant in this build arrives as itself, with
      StoreKit's raw value. .unknown was only ever a guess.
    • PurchasesError follows the URLError layout: a code, plus productID, offerID and the
      underlyingError it was translated from. Write catch PurchasesError.purchaseCancelled { … } or
      error.code == .purchasePending.
  • StoreKit errors are translated. In 3.x, StoreKitError and Product.PurchaseError escaped raw. They now
    arrive as codes such as productUnavailable, ineligibleForOffer, invalidOffer, networkUnavailable or
    notAvailableInStorefront. A cancellation reported as an error is purchaseCancelled, the same as a cancelled
    sheet.
  • Protocol requirements changed, so mocks need updating. PurchasesProtocol now requires
    purchase(productID:options:), subscriptionStatuses(inGroup:), isEligibleForIntroOffer(inGroup:) and
    appPurchaseInfo(). purchase(productID:) and purchase(productID:appAccountToken:) became extension
    conveniences over the options-based call, so a stand-in implements one purchase method instead of two.
  • Model initializers: StoreProduct requires currencyCode, and StoreTransaction requires productType.
  • activeSubscriptions() includes subscription bundles and suites. Comparing against autoRenewable alone
    made a bundle subscriber look unsubscribed.

New

  • Subscription status: subscriptionStatus(inGroup:) / subscriptionStatuses(inGroup:) and the live
    subscriptionStatusUpdates stream. StoreSubscriptionStatus carries the state (subscribed, grace period,
    billing retry, expired, revoked), renewal date, auto-renew product, grace-period end, expiration reason and
    isActive.
  • PurchaseOptions: quantity, promotional offers signed as JWS, win-back offers, the intro-eligibility JWS and
    Ask to Buy simulation, alongside appAccountToken. An unknown win-back offer fails with offerNotFound before
    the sheet appears, instead of charging full price.
  • Richer models: StoreProduct gains currency, subscription period and introductory, promotional and
    win-back offers. StoreTransaction gains product type, ownership (family sharing), environment, reason, offer,
    revocation reason and type, billing plan, storefront and price.
  • appPurchaseInfo() returns the original app version, for apps moving from paid to free with purchases.
    currentStorefrontCountryCode() and unavailableProductIDs are new as well.
  • App Store sheets on iOS, Mac Catalyst and visionOS: manage subscriptions, refund request, offer-code
    redemption.

Fixed

  • An uncached requestProducts no longer re-announces owned products as new purchases.
  • Products missing from the cache are fetched in one request instead of one request per entitlement.
  • hasEntitlement(for:) asks StoreKit about the one product instead of walking every entitlement.

Distribution

  • The library no longer forces static linking, so Xcode picks static or dynamic linking per client.
  • The package is set up for Swift Package Index, which will host the documentation.

Two names carry a Store prefix, StoreSubscriptionStatus and StoreSubscriptionPeriod, because StoreKit
declares top-level typealiases with the plain names. Any file importing both modules would otherwise fail
with "ambiguous for type lookup".

Requires Xcode 26 or later. Deploys to iOS, iPadOS, Mac Catalyst, macOS, tvOS, watchOS and visionOS 26.