Adding a Tip Jar in SwiftUI Using RevenueCat

I wanted to play around with RevenueCat, and I figured creating a tip jar would be the perfect start. So I sat down yesterday at midnight and went through this article by Thomas Ricouard:

After reading it as well as RevenueCat’s documentation, I was able to add a tip jar to my Gradient Game!

Inspiration

I started off with the inspiration from Apollo’s tip screen — the best Reddit client out there.

Tip jar screen of Apollo app

Based on that, I decided to create three tiers of tips:

  • Love for coffee — Base tip
  • Love for lasagne — Big tip
  • Love for pizza — Huge tip

App Store Connect

I added them under the In-App Purchases section of the app on App Store Connect:

Adding different IAPs on the App Store Connect Page

I had to provide the reference name, product ID, pricing, display name, description, and screenshot (mandatory) for the review process.

Providing information for a particular IAP

Do note that you have to first create one IAP, get it approved by adding it in a build, and then add more IAPs. I made the mistake of adding everything in the first go, and my app was rejected because the first one was not approved.

After getting the first IAP approved (live at the time of writing), I added more.

That wraps up the App Store Connect side. It’s time to move on to RevenueCat!

RevenueCat

After creating an account and logging in, I added Gradient Game as an app. Under Products, I added it with the same identifier as on App Store Connect. Now, I added entitlements for each of them and attached them to the corresponding product.

After this, I was confused as to whether I should add “Offerings” or not. In the docs, the example was of subscriptions. I downloaded the sample SwiftUI project to understand how they did it, and then based on that, I created an offering too.

I created a default offering and added three packages to it, each corresponding to the products that I made earlier.

RevenueCat Offerings

At this point, I was done with the handling of IAPs on both App Store Connect and RevenueCat. Let’s write some code!

SwiftUI and Some Code

I started off with a singleton class called IAPManager (something similar to what appears in the article mentioned above):

import Purchases
import SwiftUI

class IAPManager: ObservableObject {
    static let shared = IAPManager()

    @Published var packages: [Purchases.Package] = []
    @Published var inPaymentProgress = false

    init() {
        Purchases.debugLogsEnabled = true
        Purchases.configure(withAPIKey: "API KEY")
        Purchases.shared.offerings { (offerings, _) in
            if let packages = offerings?.current?.availablePackages {
                self.packages = packages
            }
        }
    }

    func purchase(product: Purchases.Package) {
        guard !inPaymentProgress else { return }
        inPaymentProgress = true
        Purchases.shared.purchasePackage(product) { (_, purchaserInfo, _, _) in
            self.inPaymentProgress = false
        }
    }
}

I configured the Purchases framework and got all the offerings available. I added a method to purchase a particular package based on the user’s selection. For the view, I looped over the packages and showed the description based on that:

import SwiftUI
import Purchases

struct IAPView: View {
    @EnvironmentObject private var subscriptionManager: SubscriptionManager

    var body: some View {
        VStack {
            Text("Tip Jar")
                .font(.largeTitle)
                .padding(.horizontal)

            VStack(alignment: .leading) {
                Text("If you love the game, you can leave a tip to cover development cost! Any tip at all helps a lot!")
                    .fontWeight(.semibold)
            }
            .padding()

            ScrollView {
                ForEach(subscriptionManager.packages, id: \.identifier) { product in
                    Button(action: {
                        subscriptionManager.purchase(product: product)
                    }) {
                        IAPRow(product: product)
                    }
                }
                .padding(.vertical)
            }
        }
    }
}

struct IAPRow: View {
    var product: Purchases.Package

    var body: some View {
        HStack {
            VStack(alignment: .leading) {
                Text(product.product.localizedTitle).bold()
                Text(product.product.localizedDescription)
            }

            Spacer()

            Text(product.localizedPriceString).bold()
        }
        .foregroundColor(.primary)
        .padding(8)
        .frame(maxWidth: .infinity, alignment: .leading)
        .padding(.horizontal)
    }
}

And that’s it! I successfully added a tip jar to my app!

Gradient Game Tip Jar

Conclusion

Working with RevenueCat was a breeze compared to with StoreKit. For adding a normal consumable/non-consumable IAP, there isn’t much of a difference. But for subscriptions, I would choose RevenueCat. I have had some nightmares working with renewable subscriptions with StoreKit in the past, and I would love for someone to handle the receipts while I work on providing the best value for that recurring subscription instead.

I hope you enjoyed this article! If you have any questions, you can leave them in the comments section.