I recently needed to implement a Non-Renewing Subscription In-App Purchase on iOS, well, actually a number of Non-Renewing Subscriptions!
In App Purchase Types
For newbies, there are multiple types of In App Purchase options, Consumable, Non-Consumable, Auto-Renewable Subscriptions, Free Subscriptions, and lastly Non-Renewing Subscriptions.
You may well be inclined to look at the Auto-Renewing Subscription, Auto Renewable Subscriptions will, as you would expect, allow access to content for a specific duration of time, and then auto renew.
However, Apple do limit how they can be used, and this means that, in most cases, they can only be used with NewsStand apps.
So, we are left with Non-Renewing Subscriptions. As the name suggests, these are subscriptions that will allow access to some form of content for a defined period of time, after which the subscription will end, and the access will be removed. The user must then renew the subscription.
Whereas with an Auto-Renewing Subscription you would manage the subscription periods with StoreKit, Non-Renewing Subscriptions require you to do the work yourself.
This means you must deal with calculating subscription periods, taking into account subscriptions that may be renewed prior to the previous subscription expiring, or conversely, managing gaps in subscription.
It is also necessary to be able restore the Non-Renewing Subscription to another device owned by any given user, who perhaps uses an iPhone and an iPad etc..
I discounted a backend server pretty quickly, as I was conscious that I did not wish to push the user, upon launch, to have to register with the backend, as I personally dislike this type of user experience, and feel it may cause users to simply delete the app without progressing.
My next thought was to use iCloud. Store the data regarding subscriptions into iCloud, this can be shared between users devices or to a new device etc.. But what if the user isn’t logged into iCloud? Or what if the user disables the apps access to iCloud?
Either way, I would be left with the scenario where the user could potentially loose the subscription that they have bought, and then I have the headache of rectifying the ensuing support ticket.
At this point, the leads I found online seemed to dry up, as they were centered around the approaches mentioned above. It was time to do some digging in the code, and start being creative in my approach.
What I found was rather amazing.
The data relating to a Non-Renewing Subscription appears in the receipt as you would expect.
However, the receipt does not just contain the last In App Purchase, the receipt actually contains each Non-Renewing Subscription that the user has ever made. Within these sub-receipts, it is then possible to locate the date and time the subscription was made (original_purchase_date), as well as the Product ID (product_id).
The receipt will be updated every time a Non-Renewing Subscription is purchased, so the latest receipt will always be present.
Furthermore, it is possible to access this data from another device with the same Apple ID by simply making a call to refresh the receipt, (SKReceiptRefreshRequest) which will ensure the latest transaction history is now available on my other device.
Armed with this fresh information, it was necessary to design the logic required to work with the data contained within the receipt.
Starting with the earliest Non-Renewing Subscription, by taking the install date (original_purchase_date) as the start point for the calculation, it is then possible to build the calculation.
It is necessary to relate product ID’s (product_id) shown within the receipts, to physical time periods to which they relate (such as 1 month or 12 months etc), and add this time period onto the start date that we already located.
By using this process, it is possible to iterate through all Non-Renewing In App Purchase subscriptions that exist within the receipt, and either append the following subscriptions onto the end of the last subscription where the new subscription is taken out shortly before expiry, or, move forwards to the next start point and build from that in the case of a lapsed subscription which causes a gap in service.
The final date we end up with is the effective expiry date of the purchases made by the user.
True Non-Renewing Subscriptions
As a result, we now have a Non-Renewing Subscription solution that will effectively calculate subscription periods as well as allow for restores, without any extra backend infrastructure, or indeed without iCloud.
What strikes me most of all is the fact that this utilizes the receipt, which should always be the sole source of truth.
In the meantime, make sure you subscribe for updates (see subscribe option at the top right of this page!).