As a user you might have seen them before - apps so out of date that iOS tells you they are no longer supported. Or they crash on start-up. Or they are so ugly that you don’t want to open them.
Why does that happen? As an iOS developer, I can share the other side of the story: Once-off revenue from initial download are long gone and there are no financial incentives to justify spending more effort on app development. To put it simply, the business is not sustainable.
I suspect this is happening to a lot of apps. To avoid this sad state I highly recommend every app to implement Subscriptions In-App Purchase features, so that the app gains recurring revenue instead of one-time revenue form Paid apps or Non-Consumable In-App Purchase. I believe this is why Apple started allowing apps of all categories to implmement Subscriptions in 2016.
Another way of getting recurring revenue is via Consumables In-App Purchase, but that is only suitable for certain categories of apps and it will not be discussed here.
Auto-Renewable vs Non-Renewing subscriptions
From app owner’s perspective, Auto-Renewable is certainly preferred over Non-Renewing Subscriptions. With Auto-Renewable Subscriptions, users will be automatically charged for the next cycle after one period has ended, and developer gets 85% of the revenue instead of 70% after 12 months.
However, not all features are allowed to be created as Auto-Renewable Subscriptions. In my own experience, the Apple Apps Review Team rejected my Auto-Renewable Subscriptions and asked me to change it to Non-Renewable Subscriptions. They quoted App Store Review Guidelines 3.1.2 about Subscriptions, but did not specify exact reasons.
After some follow-up discussions with them, I now understand that features that simply unlock certain views/features are not considered "dynamic content or services", and cannot be created as Auto-Renewable Subscriptions. They can however still be Non-Renewing Subscriptions.
Even then, using Non-Renewing Subscriptions is preferable compared to one-off revenue like paid apps or Non-Consumables IAP, as there will be recurring revenue if users are willing to pay for the feature again.
How to implement Subscriptions
Instead of rewriting detail steps that others have already documented, I am going to outline high level steps and articles that I followed.
Follow this raywenderlich.com tutorial to perform these tasks:
- Create In-App Purchase Subscription items in App Store Connect.
- Display those items in your app.
- Create user interface that allows user to purchase the items.
- Write mechanism to handle transactions.
Here is the key question: Does the user currently have a valid subscription? Enable the premium feature(s) if yes. The answer can be found in the app receipt. This is what Apple meant by Persisting Using the App Receipt.
Note that even though Apple suggested using "iCloud or your own server" to track non-renewing subscription, there is no real reason you can’t simply rely on the app receipt as well. As Apple mentioned in the article: "Information about all other kinds of purchases is added to the receipt when they’re paid for and remains in the receipt indefinitely".
In the raywenderlich.com tutorial, note that step 7 onwards suggests using a remote server to validate receipts. If you want to save the cost of building and maintaining a server, like I do, you may want to simply validate receipts locally in the device instead.
How to validate receipts in device locally? Follow this tutorial by Andrew Bancroft. It is by far the best guide on this topic and I can’t give enough credit to this article and the author. Thank you Andrew Bancroft.
Here are a few more things I learnt while trying to put everything together:
- The sequence of events when user purchases your subscription item:
- You create SKPayment and add to SKPaymentQueue.
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction])gets called with SKPaymentTransaction.
- If the transactionState is
purchased, that means user has been charged successfully and you should mark it as finished using
queue.finishTransaction(). You can now validate the app receipt. Receipt should be valid with details of latest purchase, once verified you should update the UI to reflect the status accordingly.
- If the transactionState is
failed, you can use
transaction.errorto find out the cause of failure. User might have cancelled the purchase. You should still mark the transaction as finished, because otherwise it will continue to be returned in the callback.
- Every time app launches, validate the app receipt, and it should contain details of items purchased by user, such as product IDs, purchase dates and expiration dates.
- When user wants to restore Subscriptions on a different device, simply create a
SKReceiptRefreshRequest. If successful, app receipt will be updated and you can treat it like the normal purchase scenario.
- When developing and testing In-App Purchase, make sure your app uses the same Bundle ID as the production version you registered in App Store Connect. I made this mistake initially because I usually have dev/beta/release versions of my apps and they all have their own unique Bundle IDs.
Good luck and may your app development be sustainable!