In iOS 11.2 Apple introduced yet another improvement to the in-app subscriptions system on iOS: introductory prices. Introductory prices are an excellent addition to the iOS developer’s tool belt, however, Apple was sure to add in some of their signature pointless burden. I wrote this guide to illustrate some of the possibilities and to help navigate the unnecessarily difficult.
There are three types of introductory price available: Pay as you go, Pay up front, and Free trial. Each one provides a different buyer’s experience.
As its name hints, Pay as you go charges users a reduced rate for some number of renewal periods before increasing to the regular price.
Pay as you go charges the the user a reduced price for a predetermined number of periods before automatically increasing the price.
To configure a product for Pay as you go you need to specify the price and the number of periods via iTunes Connect. The introductory price must be less than the regular price of the subscription. The available number of periods differ depending on the duration of the subscription.
The available Pay as you go durations vary depending on the regular duration of the product.
Intro period durations must be the same as their regular product durations; you can’t have a one-week Pay as you go period that leads to a monthly subscription. The maximum duration for an entire introductory period is one year, except in the case of a one-week subscription where the maximum is 12 weeks.
Rather than providing a lower price for multiple renewal periods, Pay up front charges any price for one period of a specified duration before the regular price takes over.
Pay up front lets you configure a specific duration and price to occur once before the regular price takes over.
Pay up front has two parameters the developer can modify: the price and the duration. Unlike a Pay as you go price, which must be less than the regular price, Pay up front periods can use any price. This means you could use intro prices to do something like charge a one time high cost period, followed by short renewals.
Using Pay up front to sell a subscription with an initiation fee. I’m not sure how viable this is but it is an interesting option available with the new introductory price types.
I think the most viable use of Pay up front is something like the Comcast model: 1 year up front at a reduced rate, then month-to-month after that.
1 year lock in at a reduced rate, $4.16 a month in this case, followed by the normal rate. I suspect this is works well because you book a lot of revenue up front and the lower month-to-month price may be less likely to cause churn.
Ah, our good old friend Free trial. It has been around for a while now but, in a good move to combat API entropy, Apple has made the current Free trial one of the new introductory price types. The behavior mimics Pay up front, but with a $0.00 price and some additionally available durations: three days, one week, and two weeks. The old API for Free trial still exists, but I am sure Apple will deprecate it in the not too distant future.
The new introductory pricing requires support on the iOS side to function. You must display to a user the introductory price and details about the offer in your user interface. To do this, Apple added an optional
introductoryPrice property to
SKProduct in iOS 11.2. If a product has an introductory price, this property will be non-nil.
SKProduct.introductoryPrice returns an object of a new class,
SKProductDiscount, that contains all the information specified when configuring the intro price:
priceLocale, used for presenting the price to the user
paymentModeenum that is either
numberOfPeriodsthat is used for
payAsYouGoto tell the user the number of periods before the regular price kicks in, always 1 for
subscriptionPeriodthat specifies the duration of a subscription period, i.e. 1 month, 2 months etc.
This data should be used in your payment flow to display the introductory pricing details to the user. Just like prices, you can’t rely on hardcoded knowledge of the products in your app (beyond the product identifiers). This data must be fetched from StoreKit before it can be displayed.
Here be dragons. Once a user completes an introductory pricing period in a subscription group, they are no longer eligible for introductory pricing for any other product in that same group.
When a user initiates a purchase, the App Store system UI will pop-up and present the correct price, either the intro or regular price. However, Apple did not provide us with this same sacred knowledge. We have to figure this out on our own in order to present correct prices to our users.
Apple’s instructions for this seem simple enough:
Apple’s instructions for checking eligibility.
But there is actually a fair bit to unwrap here.
You now have to validate a receipt before a user can see a product’s price.
Validating first is complicated. It requires you to possibly refresh the receipt, which can trigger an App Store login prompt, and to do the actual receipt validation either locally or by sending it to a service. Verifying the receipt before a product is even visible adds yet another thing for developers to screw up. (Which we are known to do from time to time.)
Once you have parsed and validated the receipt, you can see what products the user has purchased. From there you know for what products an introductory period has been completed and can then determine for which subscription groups introductory pricing is unavailable. To compute the eligibility with certainty also requires knowing to which subscription group every product belongs. Neither the
/verifyReceipt response nor
SKProduct will tell you the group for a subscription product. You will now have to have to store both the group and the product in your app or on your backend to verify eligibility correctly.
All of this added complexity increases the time it takes to show the user a product’s price when they land on the purchase page of your app. You now need to:
This will add 1, maybe 2, additional remote calls and possibly add seconds to the time it takes to display products to your users. Time spent loading is sales lost.
The system purchasing UI always shows the correct price when initiating a purchase, so we know that they know the correct price. Why didn’t they just give us that information?
SKProduct.isEligibleForIntroductoryPricing is all we needed.
I suspect it comes down to some combination of internal politics and technical debt but, it’s crazy to me that such an API shortcoming would ship. The developer experience around StoreKit and IAPs is already so broken, and this just adds one more thing to a developer’s plate. Neglect of the developer experience runs downhill. A small increase in complexity for the developer leads to a large increase in poor user experiences.
I wouldn’t be a good salesman if I didn’t also have a solution to the problem. RevenueCat just added support for introductory pricing and along with that we also added support for checking intro pricing eligibility to the RevenueCat iOS SDK. We have been able to condense it down to one call that makes the process smoother for developers.
Introductory prices are a welcome addition to the in-app subscriptions ecosystem. The API is well designed and sensible, and the available product configurations are well thought out and very flexible. We just need a couple of things to make them great:
isEligibleForIntroPricesmethod or property to
Either of these would go a long way to making sure implementations of introductory prices do not create a substandard iOS user experience.
RevenueCat is the best way to implement subscriptions in your mobile app. We handle all the complicated parts so you can get back to building. Request an invite today at https://www.revenuecat.com/