We decided it was time to build and ship a brand new app using SwiftUI to experience first hand the latest from Apple.
The app, now available on the App Store, is called Serenity Now.
It’s a simple audio player with a SwiftUI interface and AVFoundation under the hood. The app is filled with a collection of sounds for focus, relaxation, and better sleep.
App Monetization Goals
We’re in the monetization business, so we set out to implement our own Nami SDK to offer IAPs and subscriptions.
Here are the three products we want to offer:
- Lifetime Access - $49.99 for life (only available as a launch exclusive)
- Monthly Subscription - 3 day free trial, then $3.99 / month
- Yearly Subscription - 1 month free trial, then $39.99 / year
Users who buy one of these products gets access to our Serenity Now Premium features. Those features include:
- Premium-only sound collections - Nature, Uplifting, Water & Sleep
- Premium controls - Repeat & AirPlay
- Ad-free experience
We want users to encounter our paywall during the app experience in three different places:
- First Launch - The first time the user launch the app
- Gated Content - Upon tapping a content tile that requires premium access
- Marketing Tile - Tapping the premium access marketing tile in Settings
Now that our goals are clear, let’s see what it takes to accomplish.
The SwiftUI Lifecycle & SDK Initialization
SwiftUI apps don’t use the the AppDelegate we have all come to know from years of building iOS apps. Instead, the entry point for SwiftUI apps is a struct conforming to the App protocol.
That protocol has one requirement, which is the implementation of a property called body. Digging into the specifics of body is outside the scope of this article, but suffice it to say that is where you’ll kick off your app’s user interface.
For our purposes, we need a place to configure the Nami SDK. It turns out, we can provide a custom init() method on our App struct for this purpose.
Here we setup our NamiConfiguration object with our app’s unique appPlatformID (found in the Nami Control Center > Integrations > [your Apple App Store integration]
Setting up an ObservableObject to Track Purchase State
We will need to monitor changes to the the entitlements a user has access to. A change may occur if they buy one of our IAP products, or if they have a subscription that expires.
In Serenity Now, if a user buys any one of the three IAP products, we grant them access to an entitlement called premium_access.
In SwiftUI, we can monitor for user entitlement changes by setting up an ObservableObject. Specifically, we need to register with the Nami SDK’s registerEntitlementsChangedHandler callback to update our ObservableObject’s premium var.
Since premium has a @Published property wrapper, any SwiftUI view’s body will be re-invoked any time the value changes.
This is exactly what we want. If a user buys one of our products, we want any of the SwiftUI views that rely on the value of premium, to be updated. Concretely, this means any view that gates access based upon the premium_access entitlement will update if the value changes.
Paywall Use Case: Initial App Launch
Next, we want to show our paywall the first time the user launches the app.
In our main SwiftUI view, SerenityNowHome, we run some code in .onAppear that checks UserDefaults for the value of a key didLaunchBefore. If the value is false, it’s the first launch of our app so we tell the Nami SDK to show a paywall if NamiDataSource’s premium var is False.
Paywall Use Case: Gating Premium Content
Next, we want to gate access to certain premium content in our app.
If the user does not have access to the premium content (e.g. NamiDataSource’s premium var is False), we want to present the paywall. Otherwise, we want to show the SwiftUI with the relevant content.
Paywall Use Case: Premium Marketing Tile
Finally, we want to give users a way to access our paywall from a marketing tile in the Settings section of our app.
In our Settings view, if the user does not have premium access (via our NamiDataSource), we show our NamiUpsellBannerView. If the user taps on it, we show our paywall.
If the user does have premium access, we instead show a link to Manage subscription which takes the user to the system Settings.
Final Thoughts
There is much more we want to do with our SwiftUI app and some advanced use cases we want to discuss in future blog posts. For now, we hope this article shows how is it is to get up and running with some very common use cases for selling IAPs and subscriptions.
You may be wondering about the paywall view itself. The paywall was created via Nami’s no-code paywall designer. The Nami SDK provides a native Swift that is configurable from the Nami Control Center so you can make changes instantly.
The SDK provided paywall integrates seamlessly with our SwiftUI app from both a user interface and usability perspective. If you’re interested in giving Nami a spin for your own SwiftUI app, you can create a free account here.