---
title: "How to use StoreKit views to build a subscription app paywall with SwiftUI"
description: "A guide on Apple’s new StoreView, ProductView, and SubscriptionStoreView APIs for building native paywalls for your subscription app."
language: "en"
publishedAt: "2023-08-25T14:57:29Z"
updatedAt: "2023-08-25T14:57:29Z"
authors:
  - name: "Charlie Chapman"
    url: "https://www.revenuecat.com/blog/author/charlie-chapman"
category: "Engineering"
categoryUrl: "https://www.revenuecat.com/blog/engineering"
readingTime: 12
canonical: "https://www.revenuecat.com/blog/engineering/storekit-views-guide-paywall-swift-ui"
---

# How to use StoreKit views to build a subscription app paywall with SwiftUI

A guide on Apple’s new StoreView, ProductView, and SubscriptionStoreView APIs for building native paywalls for your subscription app.

## Table of contents

- [How to use StoreView](#how-to-use-storeview)
- [How to use ProductView](#how-to-use-productview)
- [How to use SubscriptionStoreView](#how-to-use-subscriptionstoreview)
- [Benefits and tradeoffs](#benefits-and-tradeoffs)
- [Using with RevenueCat](#using-with-revenuecat)
- [RevenueCat Paywalls](#revenuecat-paywalls)
- [Final thoughts](#final-thoughts)

The sample project used throughout this guide can be found at [https://github.com/RevenueCat-Samples/storekit-views-demo-app](https://github.com/RevenueCat-Samples/storekit-views-demo-app).

At WWDC 2023 Apple introduced [StoreKit views](https://developer.apple.com/documentation/storekit/in-app_purchase/storekit_views), a set of new APIs for rendering paywalls in your app using SwiftUI. These new APIs, available starting with iOS 17, enable developers to get a simple paywall up and running in their app quickly with some customization options to help them blend into your app.

![StoreKit views rendered on different Apple devices. Source Apple](https://cdn.sanity.io/images/c3qnx9b0/production/a2c64a4df949bf96a81cc3162861cf8b1bba8a42-1600x899.png)

*Figure 1: StoreKit views rendered on different Apple devices. Source Apple*

In this guide, I’ll cover the three new StoreKit views, `**StoreView**`, `**ProductView**`, and `**SubscriptionStoreView**`, and show how to hook them up to your In App Purchases and integrate them into your app. I’ll also cover some of the tradeoffs of using these new APIs. To follow along and see all of this code in action, check out our [StoreKit views Sample App](https://github.com/RevenueCat-Samples/storekit-views-demo-app).

**NOTE:** If you’re new to StoreKit, I highly recommend reading our [StoreKit 2 tutorial](https://www.revenuecat.com/blog/engineering/ios-in-app-subscription-tutorial-with-storekit-2-and-swift/). It will walk you through setting up App Store Connect, StoreKit configuration files, and how to handle purchase events for unlocking your in-app features.

For all of the examples in this guide I am assuming these products are set up in App Store Connect (for our sample project these products are set up in a local [StoreKitConfiguration file](https://www.revenuecat.com/blog/engineering/ios-in-app-subscription-tutorial-with-storekit-2-and-swift/#h-setting-up-storekit-configuration-file)).

**Auto-Renewable Subscription**

- **Group: **Pro
  - **Products:** 
    - “pro_weekly”
      - $0.49 / week
    - “pro_monthly”
      - $0.99 / month
    - “pro_yearly”
      - $12.99 / year
      - **Includes 1 week free trial**
**In-App Purchases**

- “pro_lifetime” – $89.99
## How to use StoreView

Let’s start with [StoreView](https://developer.apple.com/documentation/storekit/storeview). This is the most straightforward of the new StoreKit views. Here’s a simple code example.

```swift
let productIds = ["pro_weekly", "pro_monthly", "pro_yearly", "pro_lifetime"]
StoreView(ids: productIds)
```

With just this code, you can render a paywall showing each of your in-app purchases that looks like this.

![StoreView with no modifications](https://cdn.sanity.io/images/c3qnx9b0/production/4d3391ddb9c504ea6094a0f584be3fb3eb4829b8-922x1798.jpg)

*Figure 2: StoreView with no modifications*

This renders each of our auto-renewing subscription products as well as our one in-app purchase product in a simple list with a button for each that initiates a purchase.

If you have already made a purchase, `StoreView` will automatically show that product as purchased and disable the button. If you are subscribed to an auto-renewing plan and tap on a different plan within that subscription group, it will allow you to upgrade or downgrade and iOS’s built-in in-app purchase flow will explain how that billing works.

For a lifetime in-app purchase option outside of the subscription group however, the `StoreView` will not automatically cancel a user’s subscription if they purchase your lifetime option. So if you want to support that you’ll have to explain to your users to do that manually.

Out of the box this is pretty boring, so let’s spruce this up by adding an image for each product. You simply return a SwiftUI view in the `StoreView`’s callback and it will render it for each product. I’ve created a simple SwiftUI view called `ProductImage` that takes in a product id and renders a unique image for each that I’ll use here.

```swift
let productIds = ["pro_weekly", "pro_monthly", "pro_yearly", "pro_lifetime"]

StoreView(ids: productIds) { product in
\u00a0\u00a0\u00a0\u00a0ProductImage(productId: product.id)
}
```

Which renders this:

![StoreView with product images](https://cdn.sanity.io/images/c3qnx9b0/production/07c67f34ce09adcf08706bf69cf9e4bb249e28af-922x1798.jpg)

*Figure 3: StoreView with product images*

You can also add a button for users to restore their purchase using the `storeButton` modifier.

```swift
let productIds = ["pro_weekly", "pro_monthly", "pro_yearly", "pro_lifetime"]

StoreView(ids: productIds) { product in
\u00a0\u00a0\u00a0\u00a0ProductImage(productId: product.id)
}
.storeButton(.visible, for: .restorePurchases)
```

Which renders like this:

![StoreView with Restore Purchases button](https://cdn.sanity.io/images/c3qnx9b0/production/a1d01576f59024b4f080ad13ef435dc257640fb5-922x1798.jpg)

*Figure 4: StoreView with Restore Purchases button*

Each of these products look a little chunky, so one thing we can do is set the `productViewStyle` to compact. There are 3 different productViewStyles you can set, `compact`, `regular`, and `large`.

```swift
let productIds = ["pro_weekly", "pro_monthly", "pro_yearly", "pro_lifetime"]

StoreView(ids: productIds) { product in
\u00a0\u00a0\u00a0\u00a0ProductImage(productId: product.id)
}
.productViewStyle(.compact)
.storeButton(.visible, for: .restorePurchases)
```

![Comparison of productViewStyles on StoreView](https://cdn.sanity.io/images/c3qnx9b0/production/fdd04eab90a0429d236c11d0f859be09811895b2-1999x1312.png)

*Figure 5: Comparison of productViewStyles on StoreView*

In the current Xcode beta as of writing (beta 6), there doesn’t appear to be a way to disable the close button on the top right corner. According to [Apple’s documentation](https://developer.apple.com/documentation/storekit/subscriptionstoreview#4250426) you should be able to hide this by adding a storeButton(.hidden, for: .close) modifier to your `StoreView`, but that API does not appear to be working in the current beta.

As far as other customizations, you can use the `background` and `foregroundColor` modifiers to tweak the look a little bit, but things are fairly limited. For a little bit more control, let’s check out `ProductView`.

## How to use ProductView

[ProductView](https://developer.apple.com/documentation/storekit/productview) is a SwiftUI view that renders a single StoreKit product. It’s basically the building block that `StoreView` is using to show your whole paywall. Using `ProductView` you can make a paywall that’s much more customized to fit your app.

Each `ProductView` takes a single productId and renders a product just like you saw in the `StoreView`. And just like the `StoreView`, you can customize the style into 3 different types: `compact`, `regular`, and `large`. You can also add an image by passing a SwiftUI view into the `ProductView` callback.

```swift
ProductView(id: "pro_monthly") {
\u00a0\u00a0\u00a0\u00a0ProductImage(productId: "pro_monthly")
}
.productViewStyle(.compact)
```

This renders a functional, though pretty boring, view for purchasing an individual product.

![Single ProductView with no modifications](https://cdn.sanity.io/images/c3qnx9b0/production/ecc58b76f8c583395d79b0ebc9878eb40b8c43ed-922x1798.jpg)

*Figure 6: Single ProductView with no modifications*

To spruce things up you can add a background with rounded corners, some padding, and tweak the foreground color like this.

```swift
ProductView(id: \u201cpro_monthly\u201d) {
\u00a0\u00a0\u00a0\u00a0ProductImage(productId: \u201cpro_monthly\u201d)
}
.padding()
.background(.thinMaterial, in: .rect(cornerRadius: 20))
.productViewStyle(.compact)
.foregroundColor(.black)
.padding(.horizontal)
```

Now that you’re controlling each element separately you can mix and match different `productViewStyle`’s within a custom SwiftUI view to build a much more custom paywall.

![Custom styled paywall built using different ProductViews](https://cdn.sanity.io/images/c3qnx9b0/production/8f013709a6f41720cbdd4e32be48a938fb334f0e-922x1798.jpg)

*Figure 7: Custom styled paywall built using different ProductViews*

There are a few drawbacks to this approach. `ProductView` will not handle rendering the **Restore Purchases** button for you like `StoreView` will, so you’ll have to render that on your own. You can refer to our [StoreKit 2 guide](https://revenuecat.com/blog/engineering/ios-in-app-subscription-tutorial-with-storekit-2-and-swift/) or look at this guide’s sample project for an example of how to do that.

`ProductView` also behaves the same way as `StoreView` when it comes to mixing auto-renewing subscriptions with one time in-app purchases. Upgrades and downgrades between auto-renewing subscriptions will work automatically, but users will be able to purchase a lifetime in-app purchase and the system will not automatically cancel their subscription.

## How to use SubscriptionStoreView

This brings us to the last new StoreKit view, `SubscriptionStoreView`. This SwiftUI view is specifically tailored for rendering a paywall for a single subscription group set up in App Store Connect. This means in our example, it will not render our lifetime in-app purchase product at all. If you want to build a paywall that mixes auto-renewing subscriptions and in-app purchases, this is not the view for you.

If you are sticking with just a single auto-renewing subscription group however, `SubscriptionStoreView` is more powerful than `StoreView` or `ProductView`.

Let’s start with the most basic implementation. You can initialize a `SubscriptionStoreView` with a subscription group id, or a list of product ids if you don’t want to include all of the products in a group for some reason.

```swift
SubscriptionStoreView(groupID: "GROUP-ID")
```

This one line of code will render this view.

![SubscriptionStoreView with no modifications](https://cdn.sanity.io/images/c3qnx9b0/production/7b949e4e7a4b6c1a55207717b040c157f427944c-922x1798.jpg)

*Figure 8: SubscriptionStoreView with no modifications*

There’s actually a lot going on here. Starting from the top, it renders your app icon with the name of your subscription group, in this case the excitingly named “Pro”.

Next it shows each of the products in your subscription group as selectable elements. The yearly product picks up that there is a 1 week trial and renders that as well.

At the bottom we have a clear CTA button that updates based on the user’s selection. If the user is eligible for a trial for the selected product, it shows “Try It Free”. Otherwise it shows “Subscribe”. The text above the button also updates based on the selection with the appropriate description for how billing works for the selected plan. This is an area that’s frequently rejected by App Review for stylistic reasons so presumably the choices Apple is making here should make it less likely to get a rejection.

If the user has already subscribed to a product within the group, `SubscriptionStoreView` gives it a clear badge and still allows the user to upgrade or downgrade their plan.

![Automatically changing CTA button text depending on which plan is selected](https://cdn.sanity.io/images/c3qnx9b0/production/342117ceea5cfa5568808238bc4ec9110e4b2814-1999x1199.png)

*Figure 9: Automatically changing CTA button text depending on which plan is selected*

If you want to change the copy of the CTA button you can choose between a few options using the `subscriptionStoreButtonLabel` modifier. The current options are `action`, `displayName`, `price`, `multiline`, and `singleline`. Apple is clear to point out that these options are preferences and are not guaranteed to behave the same way on all platforms.

![Comparison of different subscriptionStoreButtonLabel settings on SubscriptionStoreView](https://cdn.sanity.io/images/c3qnx9b0/production/e294fc895876877d2cc50455f2c7fc995b732f1d-1999x1313.png)

*Figure 10: Comparison of different subscriptionStoreButtonLabel settings on SubscriptionStoreView*

Similar to `StoreView`, you can add a **“Restore Purchases”** button at the bottom with the `storeButton` modifier. You can also add a **“Redeem Code”** button this way on the `SubsctiptionStoreView`. Additionally, you can add links to your **Privacy Policy** and **Terms of use** using the `subscriptionStorePolicyDestination` modifier.

```swift
SubscriptionStoreView(groupID: PurchaseManager.subscriptionGroupId)
\u00a0\u00a0\u00a0\u00a0.subscriptionStoreButtonLabel(.multiline)
\u00a0\u00a0\u00a0\u00a0.storeButton(.visible, for: .restorePurchases)
\u00a0\u00a0\u00a0\u00a0.storeButton(.visible, for: .redeemCode)
\u00a0\u00a0\u00a0\u00a0.subscriptionStorePolicyDestination(url: privacyUrl, for: .privacyPolicy)
\u00a0\u00a0\u00a0\u00a0.subscriptionStorePolicyDestination(url: termsUrl, for: .termsOfService)
```

![SubscriptionStoreView with multiple storeButtons and policyDestinations enabled](https://cdn.sanity.io/images/c3qnx9b0/production/c5e29cc1a69148c088b5742631cd551adc8992dc-922x1798.jpg)

*Figure 11: SubscriptionStoreView with multiple storeButtons and policyDestinations enabled*

One final functional element before getting into styling is `visibleRelationships`. When initializing your `SubscriptionStoreView` you can set the `visibleRelationships` parameter to `crossgrade`, `upgrade`, or `all`. This will change which products are presented to the user if they are already subscribed to another product in your subscription group. For more information on subscription groups you can read [iOS Subscription Groups Explained](https://www.revenuecat.com/blog/engineering/ios-subscription-groups-explained/).

Ok, so you have a subscription paywall that’s functionally complete, but… well it looks pretty bland. Let’s look at the ways we can customize its appearance.

Just like `StoreView` and `ProductView`, You can add an image for each product using the `subscriptionStoreControlIcon`. It renders a bit smaller though so you may want to lean more towards a smaller icon as the modifier name suggests.

```swift
SubscriptionStoreView(groupID: PurchaseManager.subscriptionGroupId)
\u00a0\u00a0\u00a0\u00a0.subscriptionStoreControlIcon { product, subscriptionInfo in
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ProductImage(productId: product.id)
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.frame(height: 18)
\u00a0\u00a0\u00a0\u00a0}
```

![SubscriptionStoreControlIcons rendered on a SubscriptionStoreView](https://cdn.sanity.io/images/c3qnx9b0/production/849491cb23015406f633c2510125576dd4ba5fd9-922x1798.jpg)

*Figure 12: SubscriptionStoreControlIcons rendered on a SubscriptionStoreView*

The marketing content at the top looks pretty rough. Fortunately we can pass in a SwiftUI view to make this look however we want. We do this by passing a view into the `SubscriptionStoreView`’s callback.

```swift
SubscriptionStoreView(groupID: PurchaseManager.subscriptionGroupId) {
\u00a0\u00a0\u00a0\u00a0VStack(spacing: 16) {
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Image(systemName: "dollarsign.square.fill")
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.resizable()
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.scaledToFit()
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.foregroundColor(.green)
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.frame(height: 100)

\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Text("Subscribe to Pro!")
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.font(.title)
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.bold()

\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Text("Please buy me \ud83d\ude4f")
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.font(.subheadline.weight(.medium))
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.fontDesign(.rounded)
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.multilineTextAlignment(.center)
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.foregroundColor(.gray)
\u00a0\u00a0\u00a0\u00a0}
\u00a0\u00a0\u00a0\u00a0.padding()
}
```

![Custom marketing content at the top of SubscriptionStoreView](https://cdn.sanity.io/images/c3qnx9b0/production/ade8294f274a31dde665e8d7a0b6e60d669477d4-922x1798.jpg)

*Figure 13: Custom marketing content at the top of SubscriptionStoreView*

To add some color, let’s use the `containerBackground(for:)` modifier on the marketing content SwiftUI view to add a gradient background. If you set the placement to `subscriptionStoreHeader` the background will only render behind your marketing content. If you use `subscriptionStoreFullHeight` it will render your background behind the full screen.

![Comparison of different placement settings for containerBackground on a SubscriptionViewStore](https://cdn.sanity.io/images/c3qnx9b0/production/cf3890c1175f9c5e7e56029480add0b79e401e35-1999x1301.png)

*Figure 14: Comparison of different placement settings for containerBackground on a SubscriptionViewStore*

```swift
SubscriptionStoreView(groupID: PurchaseManager.subscriptionGroupId) {
\u00a0\u00a0\u00a0\u00a0VStack(spacing: 16) {
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Image(systemName: "dollarsign.square.fill")
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.resizable()
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.scaledToFit()
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.foregroundColor(.green)
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.frame(height: 100)

\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Text("Subscribe to Pro!")
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.font(.title)
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.bold()

\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Text("Please buy me \ud83d\ude4f")
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.font(.subheadline.weight(.medium))
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.fontDesign(.rounded)
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.multilineTextAlignment(.center)
\u00a0\u00a0\u00a0\u00a0}
\u00a0\u00a0\u00a0\u00a0.padding()
\u00a0\u00a0\u00a0\u00a0.foregroundColor(.white)
\u00a0\u00a0\u00a0\u00a0.containerBackground(for: .subscriptionStoreFullHeight) {
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0LinearGradient(colors: [.blue, .red], startPoint: .topLeading, endPoint: .bottomTrailing)
\u00a0\u00a0\u00a0\u00a0}

}
```

By default, `SubscriptionStoreView` renders a material background behind all of the products. You can hide this using the `backgroundStyle` view modifier set to `clear`. You can then tweak the background behind each individual product using the `subscriptionStorePickerItemBackground`.

```swift
SubscriptionStoreView(groupID: PurchaseManager.subscriptionGroupId) {
\u00a0\u00a0\u00a0\u00a0VStack(spacing: 16) {
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Image(systemName: "dollarsign.square.fill")
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.resizable()
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.scaledToFit()
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.foregroundColor(.green)
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.frame(height: 100)

\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Text("Subscribe to Pro!")
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.font(.title)
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.bold()

\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Text("Please buy me \ud83d\ude4f")
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.font(.subheadline.weight(.medium))
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.fontDesign(.rounded)
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.multilineTextAlignment(.center)
\u00a0\u00a0\u00a0\u00a0}
\u00a0\u00a0\u00a0\u00a0.padding()
\u00a0\u00a0\u00a0\u00a0.foregroundColor(.white)
\u00a0\u00a0\u00a0\u00a0.containerBackground(for: .subscriptionStoreFullHeight) {
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0LinearGradient(colors: [.blue, .red], startPoint: .topLeading, endPoint: .bottomTrailing)
\u00a0\u00a0\u00a0\u00a0}
}
.backgroundStyle(.clear)
.subscriptionStorePickerItemBackground(.thinMaterial)
```

![SubscriptionStoreView with clear background and material set for subscriptionStorePickerItemBackground](https://cdn.sanity.io/images/c3qnx9b0/production/7352e2c2ffd722a8dbbf5740b62d2d8e6f270871-922x1798.jpg)

*Figure 15: SubscriptionStoreView with clear background and material set for subscriptionStorePickerItemBackground*

While not quite as flexible as building your own SwiftUI paywall using the `ProductView` building blocks, `SubscriptionStoreView` allows you to create a more traditional subscription app paywall with a product picker and a strong call to action button at the bottom. It’s automatic handling of trial eligibility and upgrades also make it a good option.

## Benefits and tradeoffs

These new StoreKit views are a nice way to easily get a usable paywall for your app. Combined with StoreKit 2, you can set up and manage a subscription app using just these native tools.

There are some missing pieces however. The styling options are very limited. You have more flexibility with `ProductView`, but those views are not really suited well for a subscription app and you have to manage trial eligibility, restore functionality, and upgrade/crossgrade options manually.

`SubscriptionStoreView` is more optimized for subscription apps, but doesn’t allow for one time purchase options like a lifetime purchase. You also cannot build multi-page paywalls where one plan is highlighted to purchase, but alternative plans can be found on a separate page.

## Using with RevenueCat

StoreKit views are great for quickly rendering a paywall UI, but you will still need to handle transaction updates within your app, manage which features your users are entitled to, and validate your transactions with your server to prevent fraud. Here at RevenueCat we build tools to make all of that easy and do so much more.

In fact, we’ve already updated our SDK to make using StoreKit views with RevenueCat incredibly easy. If you’re building for iOS 17, have the SDK installed, and import RevenueCat in your file, both `StoreView` and `SubscriptionStoreView` will have a new initializer allowing you to pass in a RevenueCat offering object.

```swift
StoreView(for: offering)
```

Or

```swift
SubscriptionStoreView(for: offering)
```

Everything else should work exactly the same as in this guide, but it will now be powered by RevenueCat offerings. This means you’ll get all of RevenueCat’s charts, ability to run experiments to optimize your paywall offerings, validate your transactions, and of course utilize RevenueCat’s world class SDK to make managing your entitlements and transaction events in your code a breeze.

## RevenueCat Paywalls

If you want a more customizable solution that’s also super easy to use, RevenueCat has recently released a new [paywalls feature](https://revenuecat.com/blog/growth/introducing-revenuecat-paywalls/) that expands the capabilities of your paywalls. RevenueCat paywalls allow you to define a paywall from our dashboard, and our SDK automatically renders that paywall **natively** (not a web view!) on device for you. This allows you to change your paywalls from our backend without needing to wait for an update to get through App Review. Plus it hooks up directly with our powerful [experiments features](https://www.revenuecat.com/feature/experiments/) to make optimizing your paywalls a breeze.

RevenueCat paywalls offer a few features that may make it a better choice over StoreKit views such as:

- iOS 15+ support
- Ability to include non-consumable in-app purchases such as lifetime products inside of your subscription paywall
- Our growing list of templates are much more flexible and follow industry best-practices for high performing paywalls
- Flexible footer view rendering mode allows for highly customizable native marketing content
- Easily run experiments to optimize your paywalls
- Cross-platform support for Android (coming soon)
## Final thoughts

Our biggest goal at RevenueCat is to help developers make more money. We do this by offering an entire [mobile subscription platform](https://www.revenuecat.com/) with native and [cross-platform SDKs](https://github.com/RevenueCat) to make integrating in-app purchases as easy as possible in any application. Our customers don’t need to worry about the different native subscription APIs or the intricacies of keeping subscription status in sync with renewals, cancellations, and billing issues.

I wrote this guide because I believe StoreKit views may be the perfect solution for some of you to get started with subscription apps. Even if you don’t ultimately decide to use our tools, we want to help all developers succeed where we can.
