Privacy-Focused In-App Subscriptions

How to set up a bare-bones implementation of the Purchases SDK

Privacy-Focused In-App Subscriptions
Cody Kerns

Cody Kerns

PublishedLast updated

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!

In-App Subscriptions Made Easy

See why thousands of the world's tops apps use RevenueCat to power in-app purchases, analyze subscription data, and grow revenue on iOS, Android, and the web.

Related posts

How we solved RevenueCat’s biggest challenges on data ingestion into Snowflake
How we solved RevenueCat’s biggest challenges on data ingestion into Snowflake
Engineering

How we solved RevenueCat’s biggest challenges on data ingestion into Snowflake

Challenges, solutions, and insights from optimizing our data ingestion pipeline.

Jesús Sánchez

Jesús Sánchez

April 15, 2024

How RevenueCat handles errors in Google Play’s Billing Library
How RevenueCat handles errors in Google Play’s Billing Library  
Engineering

How RevenueCat handles errors in Google Play’s Billing Library  

Lessons on Billing Library error handling from RevenueCat's engineering team

Cesar de la Vega

Cesar de la Vega

April 5, 2024

Use cases for RevenueCat Billing
Engineering

Use cases for RevenueCat Billing

3 ways you can use the new RevenueCat Billing beta today.

Charlie Chapman

Charlie Chapman

March 21, 2024

Want to see how RevenueCat can help?

RevenueCat enables us to have one single source of truth for subscriptions and revenue data.

Olivier Lemarié, PhotoroomOlivier Lemarié, Photoroom
Read Case Study