How to Change the Business Model of an App Store App

loading views

If you want to change your app's business model from pay-before-download to using subscriptions or in-app purchases, you might have found Apple's article on this topic. This article is dangerous because how it's documented only works for a macOS app and can cause users who already purchased your app to get wrongly prompted to purchase it again.

I recently went through the pain of using Apple's approach for my universal app, Pagi. So I felt obligated to document what I learned about a bug that only occurs dependent on which Apple platform your user first purchased or downloaded your app.

Universal Apps

The approach in their article relies on AppTransaction.originalAppVersion, which will return the app version your user first purchased or downloaded.

The big catch is that this property behaves differently dependent on which platform your user first purchased or downloaded your app. If they purchased the app on macOS, it will use CFBundleShortVersionString (typically SemVer), which is the behavior described in Apple's article.

If your user first purchased the app on any other Apple platform, like iOS, it will use CFBundleVersion, which is your app's build number.

I don't know what led them to this decision, but it completely breaks the approach in their article if your app supports macOS and any other platform.

An approach to solve this could be to use AppTransaction.originalPlatform, which tells us on which platform the user first purchased your app from, but this property is only available on macOS 15.4+ and iOS 18.4+ so won't be helpful if you want to support devices with older OS versions.

What worked for me, was to use AppTransaction.originalPurchaseDate to check if the user purchased the app before I changed the business model.

AppTransaction has another property called appVersion, but it's not properly documented so it's unclear what it really does. The only way to test it is to ship an app using it to the Mac App Store and iOS App Store. I didn't try it, so if you know how it works, please let me know. I would love to know if this could be a replacement for originalAppVersion.

Surviving App Store Review

If you use Apple's suggested approach, your app probably won't survive App Store Review. This recently happened to my app Pagi, which got rejected because of this.

Screenshot of a Apple App Store Review reply for Pagi's rejection

Apple reviews apps in a sandbox environment where the AppTransaction.originalAppVersion is always 1.0, which means with Apple's approach, your new paywall will never be shown, so the review team can't test your in-app purchase flow and have to reject your submission.

Another problem is that AppTransaction.originalPurchaseDate will always be 2013-08-01 12 AM PDT in the sandbox environment, so if you use that alone, the in-app purchase flow won't get shown as well.

The solution is to use AppTransaction.environmentto check if your app is running in the sandbox. If it is the paywall always have to be shown, otherwise App Store Review will likely reject your app.

The Proper Way

I created this helper function to easily check if the user purchased the app before you changed your business model

func didUserPurchaseApp(originalPurchaseDate: Date, environment: AppStore.Environment) -> Bool {
    let didPurchaseBeforeBusinessModelChange = originalPurchaseDate < dateOfBusinessModelChange
    let isSandbox = [AppStore.Environment.sandbox, .xcode].contains(environment)
    return !isSandbox && didPurchaseBeforeBusinessModelChange
}

In the context that Apple uses in their article, this would look something like this:

let shared = try await AppTransaction.shared
if case .verified(let appTransaction) = shared {
    let isEntitled = didUserPurchaseApp(
        originalPurchaseDate: appTransaction.originalPurchaseDate,
        environment: appTransaction.environment
    )                
    if isEntitled {
        // This customer purchased the app before the business model changed.
        // Deliver content that they're entitled to based on their app purchase.
    } else {
        // This customer purchased the app after the business model changed.
    }
}