I was thinking about how they achieve this above and beyond some
canOpenURL: smarts, and I presume they are using a shared bundle ID or similar to allow the apps integrate with each other at a deeper level.
Somewhat tangentially this got me thinking about a possibility of solving the “there’s no upgrade pricing in the App Stores” problem.
Upgrade pricing is where existing customers can purchase the substantially different new versions of an app at a discounted price. Apple seems to be pushing people away from this, and towards new app releases which have their own single lower price. There is resistance to this from some developers, and I’m not convinced either way – but I think there is an interesting possibility here of providing a hybrid solution.
The upgrade pricing problem
The core issue is that some developers do not want to charge their existing paying customers full price for a new release, and as such they may feel forced to offer a low price across the board, or use fire-sale pricing on the first few days of sale for loyal customers and any other takers. You can argue this introduces a downward pressure on app pricing as a whole and this is at least at the moment feels like a negative for the industry.
The difference between a £4.99 app and a £2.49 app is not much in absolute terms but is massive in terms of the perceived cost to the customer relative to a purchase of a previous release at £4.99. Therefore being able to offer a large discount to your loyal customers is typically a win/win.
The problem from a developer perspective is that you want customers to be able to continue enjoying the existing version of the app they purchased from you, when you may be making significant changes to the app the warrant a new purchase and cannot be supplied as an update to the existing app because the old and the new cannot co-exist. This is the case for example in large-scale UI rewrites such as the transition to iOS 7.
As a result developers are forced to create a separate app in the store, priced separately and sandboxed on the iOS device or Mac from the previous release of the app. iA recently did this with their transition from iA Writer to Writer.pro. The changes were significant enough to mean it could not be a straight update, and warranted re-purchase.
Aside from subscription pricing for apps, which is yet to be shown to be viable, there is no other option to generate ongoing revenue for all the work you have put into developing a major new release.
The solution is possibly right under our noses
My proposal is to use In-App Purchases to solve the problem. In-App Purchases have the benefit that you can make the app free in the store – which solves the “how does an existing customer download the app without paying again” problem, while also allowing you to benefit from allowing “try out users” which is another big problem in the app store – the caveat being your app has to be functional in the free version for Apple to be happy. However it is pretty plain that this is the way to go for almost all apps now.
The steps involved would be:
- User downloads new free app release
- User runs the new free app release
- The new app release detects that a previous release is installed by checking for a supported URL protocol, and presents the upgrade offer to the user “As you are an existing customer we’d like to offer you the Pro upgrade for 50% discount if you have purchased the previous release”
- If the user wants to take up the offer, the app issues a URL request to the old app version, asking it to verify that the previous version was paid (implementation depends on your choice and previous pricing model)
- The old app is executed, and then in xcallback fashion opens the new app again, passing in data confirming that the original version was purchased
- The new version of the app receives the confirmation token data in the URL and then allows the user to make the discounted in-app purchase which is not available to users who have not paid previously.
Let’s do a concrete example. Say that a customer Jane has “YourMegaApp 1” installed which they paid for or downloaded free and paid for the Pro version with In-App Purchase. They want to get “YourMegaApp 2” which is a separate app in the store.
- Jane downloads “YourMegaApp 2” and runs it on her device which already has “YourMegaApp 1” installed
- “YourMegaApp 2” executes
canOpenURL:with a URL like
- This returns YES and “YourMegaApp 2” shows Jane a message saying “It looks like you have YourMegaApp 1 – if you are a paid user you can upgrade to YourMegaApp 2 Pro for a discounted price. Would you like to do this?”
- Jane is wowed by this generous offer and taps YES, as she knows she paid for the previous version
- “YourMegaApp 2” executes openURL: with a URL like
your-mega-app-1://verify-purchase?verify-callback=your-mega-app-2://purchase-verify-result?request-token=32434329043294834(this URL shown is not URL escaped, for the sake of simplicity)
- “YourMegaApp 1” is opened, sees the verb in the URL and assesses whether or not the user paid for the old release. If the version 1 release was paid only, this is a no-brainer. If it was free with In-App Purchase, the IAP receipt should be verified (depending on how much you care / likely people are to defraud your app)
- “YourMegaApp 1” creates a data packet indicating whether or not they do qualify for the upgrade and including the “request-token” from the URL, and signs this cryptographically – either on-device or preferably via a web service you have. Signing on-device would mean your private keys would be needed in the app, which could easily be extracted and used to make fraudulent stub apps that fake this URL protocol process to get free upgrades.
- “YourMegaApp 1” then calls
openURL:with the “verify-callback” URL and the signed data appended, URL encoded e.g.
- “YourMegaApp 2” is opened, pulls out the data from the URL and verifies the signature matches the request-token – which can be performed on the client as only the public key is required to verify, but could also happen via a server.
- The signature is valid, and “YourMegaApp 2” shows Jane the In-App Purchase for the discounted upgrade.
That all looks like rather a lot, but it’s pretty simple in practice. It is in a way a form of cross-app In-App Purchase receipt checking, under your control.
Your previous app release obviously needs to have this functionality in it to support the upgrade, but this is also your chance to use a similar mechanism to copy any settings or data across to the new app using a similar approach.
…but Apple won’t allow this
This is debatable. The In-App Purchase Guidelines do have some pertinent rules:
You must make your In-App Purchase items available to all of the devices registered to a user.
In-App Purchase items cannot be shared across applications.
— source: Apple In-App Purchase Guidelines
The first of those I think is fine. Strictly speaking all devices of the user can see the discount In-App Purchase if they still have the old version installed, or choose to install it, and restore In-App Purchases on the old version if applicable.
The second I think is fuzzy. If the old version of the app did not use In-App Purchases then this obviously does not apply. If the old version did use In-App Purchases, the purchase itself is not being shared, it is merely being leveraged for a new purchase. Apple may of course disagree.
Anyway there it is. I’ve not seen anybody suggest an approach like this before, but perhaps I am just treading a path that somebody has already discovered and abandoned. I may well be totally crazy but I think this is something that could work.
For me at least it would appear to provide the best of all worlds; a free install/try-out experience, paid “Pro” features, and discounted “Pro” releases of completely new apps – perhaps even unrelated apps that the same developer releases. You could even do “Buy 2 of our apps get 1 free” type deals like this.
If you have any comments please let me know on Twitter @transition_io
UPDATE: Ken Case of Omnigroup pointed out on Twitter that they have done something similar to provide upgrade pricing for Mac versions of OmniGraffle 6. He also pointed out that shared keychain entries could be used on iOS instead of using a URL scheme, i.e. the old release could store some verifiable ID in the keychain that the new release could check for and validate. However I think given there are also likely settings and data migration issues, I think a URL scheme may work best on iOS – but perhaps a hybrid could be used where the keychain is used to avoid the requirement for an extra server check during the upgrade process.