Privacy-Focused In-App Subscriptions
How to set up a bare-bones implementation of the Purchases SDK
Over the last few years, customer privacy has become an increasingly important consideration for developers. Between the regular occurrence of data breaches and the implementation of GDPR, app developers have to be more and more conscientious about what data they collect and how that data is stored and used.
Apple recently put a spotlight on this issue by implementing the App Tracking Transparency framework and requiring developers to disclose information about data collection and use within their App Store listing. Today’s users are more concerned about privacy than ever before—and can find out what data your app collects before they even download it.
When you add subscriptions to your app with RevenueCat, you’ll have to start disclosing some of this data collection to your customers. But rest assured, this doesn’t mean your users’ data isn’t private or secure. And this disclosure isn’t just required for developers using RevenueCat; you’ll have to disclose your data collection even if you run your own subscription status server.
The fact is, the only way to keep your users’ subscription status secure is to validate your receipts from a server, which requires some minimum data disclosures.
Can’t I just do receipt validation locally? Why do I have to send user purchase data to a server?
Apple strongly recommends against performing receipt validation locally. It makes it easy for bad actors to intercept the connection and perform a man-in-the-middle attack. Local receipt validation also removes the ability to sync purchases between iOS and Android and you’ll have to update your app every time you make changes to the products you sell.
Of course, if you are performing receipt validation directly from the device, you won’t need to disclose any purchase data collection. This is a trade-off, and you’ll have to decide for yourself what makes the most sense for you.
Are you willing to allow your premium features and content to be accessed by jailbroken devices or run the risk of someone hijacking the connection between your app and Apple? For some, the answer to this question may genuinely be yes. If that’s the case for you, check out SwiftyStoreKit and local validation implementations.
Going that route means you’ll be sacrificing other features, though, including super-simple purchase logic, customer support tools, and robust reporting/metrics—plus privacy for your users.
Subscriptions with RevenueCat
In this blog post, I’ll walk you through a bare-minimum implementation of subscriptions in a Swift app, using a stripped-down version of the RevenueCat Magic Weather sample app as a project template. We’ll use RevenueCat’s Purchases SDK for subscription status and purchase logic, and we’ll discuss how to keep your users’ data private while also taking advantage of the benefits mentioned above.
What is Magic Weather?
Magic Weather is RevenueCat’s sample app that was built to be as consistent as possible across platforms, while still taking into account each platform’s differences. In this post, we’ll be starting with a stripped-down version of the Swift sample app that’s ready for subscriptions. Of course, if your app is ready for subscriptions too, you can follow along.
You can download the project template here.
To follow along with this tutorial, you’ll need to have the Purchases SDK installed. If you haven’t already done this, go here for installation instructions.
Additionally, if you haven’t already set up your products, check out our guide on Products, Packages, Offerings, and Entitlements.
Configuring the SDK
Before we get started, let’s enable debug logs. Debug logs provide important context from the Purchases SDK and will help us debug any issues that come up down the line. Enable debug logs by calling this method as early as possible in your app’s lifecycle, like in the AppDelegate’s didFinishLaunchingWithOptions
method:
1Purchases.debugLogsEnabled = true
One of the easiest ways to keep your users’ data private is to take advantage of RevenueCat’s anonymous user identifiers. Automatically generated by the SDK, these IDs keep your users anonymous.
With the RevenueCat SDK, using an anonymous ID is simple. Just call configure
with your API key, leaving out the appUserId
variable—or just pass nil
. Add the following underneath the line that we used to enable debug logs:
1Purchases.configure(withAPIKey: "api_key")
A new, randomized, and anonymous user identifier has now been created by the SDK, and this user’s purchases will be attached to this anonymous ID.
Check Subscription Status
Let’s jump to our WeatherViewController.swift
file and find the performMagic
method. Currently, this method automatically changes our fake weather forecast, but we need to make sure we’re checking the user’s subscription status beforehand.
Replace this line:
1self.setWeatherData(SampleWeatherData.generateSampleData(for: self.currentEnvironment))
With this:
1Purchases.shared.purchaserInfo { (purchaserInfo, error) in
2 if purchaserInfo?.entitlements[Constants.entitlementID]?.isActive == true {
3 self.setWeatherData(SampleWeatherData.generateSampleData(for: self.currentEnvironment))
4 } else {
5 let main = UIStoryboard(name: "Paywall", bundle: nil).instantiateInitialViewController()!
6 self.present(main, animated: true, completion: nil)
7 }
8}
This will fetch our PurchaserInfo
from the Purchases SDK and determine whether our anonymous user has an active entitlement. If they do, we’ll magically change the weather—if not, we’ll present them with a paywall. Since the Purchases SDK caches the user’s PurchaserInfo
for a few minutes, it’s safe to call this fetch method as often as you need!
We haven’t made any purchases yet, so the user won’t have an active entitlement. Let’s fix that!
Fetch Offerings, Make Purchases, Unlock Entitlements
It’s time to fetch your product offering from RevenueCat and display it in your app’s paywall. In the project template, open up the PaywallViewController.swift
file. RevenueCat organizes products into Offerings and Packages. Let’s fetch your Offering and reload the tableView
that displays our Packages.
In viewDidLoad
, add the following after the super method:
1Purchases.shared.offerings { (offerings, error) in
2 self.offering = offerings?.current
3 self.tableView.reloadData()
4}
Our paywall uses this current Offering to create a cell for each available Package, then on cell selection, calls purchasePackage
via the SDK. The rest of the purchase logic is already implemented in this sample, so if you scroll down to the end of the PaywallViewController.swift
file, you’ll see that purchase method in action in the tableView’s didSelectRowAt
method:
1Purchases.shared.purchasePackage(package) { (transaction, purchaserInfo, error, userCancelled) in
2// … additional implementation
3}
The Purchases SDK handles the rest of the purchase logic, including interacting directly with StoreKit and syncing the user’s receipt to RevenueCat’s backend, unlocking any associated entitlements. Of course, this all happens anonymously. After a purchase, the paywall will be dismissed, and you can start changing the forecast.
Now let’s visit App Store Connect to set up the privacy disclosures that Apple requires for our app’s App Store listing.
App Store Connect
Find your app in App Store Connect, and select App Privacy in the sidebar.
Since we’re only using anonymous identifiers, and we aren’t collecting any PII, we only need to let Apple know of one data type that we are collecting.
Find Purchases in the data type list, and check the box.
Click Save. Then you’ll need to set up additional information about what RevenueCat does with the Purchase History data.
All RevenueCat users must select the Analytics and App Functionality options. This data isn’t tied to a user’s identity, since we use anonymous IDs—but these are basic functionalities of the RevenueCat dashboard that must be disclosed.
- Selecting Analytics ensures compliance for RevenueCat’s dashboard features, including Customer History, Charts, and Experiments.
- Selecting App Functionality ensures compliance for RevenueCat’s receipt validation and enables features via Entitlements
Once you’ve selected those options, click Next.
Then, Apple will ask you if the purchase history data we discussed previously is linked to a user’s identity. Since we’re using anonymous IDs, you can safely select No:
Click Next. Apple will now give some definitions and examples of what it means to use data for tracking purposes. Since we’re not tracking our users with this purchase data, select No again, then click Save:
As far as Apple’s privacy requirements, you’re all set! Your data privacy settings should now look like this:
It’s important to note that there are other situations where you may need to disclose additional privacy details—for example, if you use any of RevenueCat’s analytics integrations. Take a look at our support guide on app privacy for more information.
Providing Customer Support
You’ve made sure your users’ data is private, but what happens if they have a problem with their subscription? Ideally, you should have a way to view each user in the RevenueCat dashboard to diagnose any issues or grant promotional subscriptions.
Displaying a user’s anonymous app user ID somewhere in your app’s UI enables you to provide this support.
Once again, the Purchases SDK provides an easy way to access this ID:
1Purchases.appUserId
Simply show this ID in your app’s settings (or if you’re using the Magic Weather sample app, the User tab) and give your users the option to share this with you. Pro tip: Automatically copy the ID to the user’s clipboard when they tap it—this will save a lot of headache!
Don’t worry, this doesn’t fall under Apple’s definition of data collection, as it’s optional for the user to provide this data, and it’s not part of your app’s core functionality.
Summary
That’s it! At this point you’re making purchases and tracking subscription status—and your users are anonymous and private. You’re also able to provide customer support to your users via the RevenueCat dashboard without violating their privacy.
Want more sample projects to learn how to implement the Purchases SDK? Check out the RevenueCat docs for details on how to get set up on every platform—including iOS, Android, React Native, and Flutter!
You might also like
- Blog post
How we built the RevenueCat SDK for Kotlin Multiplatform
Explore the architecture and key decisions behind building the RevenueCat Kotlin Multiplatform SDK, designed to streamline in-app purchases across platforms.
- Blog post
Inside RevenueCat’s engineering strategy: Scaling beyond 32,000+ apps
The strategies and principles that guide our global team to build reliable, developer-loved software
- Blog post
RevenueCat Ship-a-ton
The hackathon that’s all about shipping… a ton.