Apple iOS Setup

A complete guide to the basics of adding Nami to your Apple iOS app.

📘

This doc supports the Apple SDK through version 2.9.4.
Looking for the newest Nami SDK? Check out the latest docs

Please make sure you have completed the setup of your app on the Nami Control Center for all these steps to be successful.

Adding Nami to your app has a few steps for a basic app.

  1. Add the SDK to your project
  2. Configure the SDK
  3. Show a paywall in your app
  4. React to a purchase to grant access to paid content or features

We'll run through each of these below.

📘

SwiftUI Apps

Nami works with SwiftUI apps! But there are a few small differences in a SwiftUI setup. Please look for additional callouts below and separate code samples for SwiftUI implementation of the Nami Platform.

Add the SDK to your project

📘

Apple Requirements

The Nami Apple SDK supports

  • iOS 11.3+
  • iPadOS 13+
  • Xcode 11+

Our iOS SDK is available on GitHub, with sample applications and our Nami iOS SDK.

The best way to add Nami to your project is to use the Swift Package Manager (SPM) to install the latest SDK version into your application.

We also support CocoaPods and you can manually install the XCFramework from our GitHub repository.

SPM using Xcode on the Mac:

  1. In the Xcode menu select File > Swift Packages > Add Package Dependency.
  2. Enter the URL https://github.com/namiml/NamiSDK-SwiftPackageManager into the box just below Choose Package Manager, and press the Next button.
1484
  1. Leave the default settings of Up to Next Major and current major version number, press Next button.

📘

The current stable release available via Swift Package Manager is version 2.9.5.

1024
  1. After making sure the correct application target is selected, click the Finish button.
814

Using Cocoapods

If you need help getting started with CocoaPods, please review their documentation.

📘

Must use CocoaPods 1.9.1+

The Nami SDK is distributed as an XCFramework and only works with CocoaPods 1.9.1 and higher.

  1. Ensure that CocoaPods is installed and set up correctly following the instructions above.
  2. In the Podfile in the same directory as your .xcproject file, add the Nami pod, along with a specified version (you should try to always use the latest version)
target 'MyApp' do
  use_frameworks!
  
  # Pods for MyApp
  pod 'Nami', '~> 2.9.4'
  
  target 'MyAppTests' do
    inherit! :search_paths
    # Pods for testing
  end
end
  1. Then run the following command in your Terminal.
pod install

❗️

CocoaPods could not find compatible versions for pod "Nami"

If you receive this or a similar error, try running the command
pod update and then re-running pod install.

Manual Setup

  1. Download the Nami SDK from GitHub at https://github.com/namiml/nami-apple
  2. Copy Nami.xcframework from the downloaded GitHub repository into the same directory as your application .xcproject file.
  3. Open your project in Xcode, select the application target in the project navigator.
  4. Select the General tab if it is not already selected.
954
  1. Drag the Nami.framework in the same directory as your .xcproject file from Finder into the project Frameworks, libraries and Embedded Content section of the General tab. Make sure Embed and Sign is selected.
1228
  1. After this, the Nami framework is installed.
  2. To update, download new versions of the Nami.framework from the GitHub repository, and copy them on top of the Nami.framework in your application - always opt to replace the entire directory when copying.
  3. You may wish to optionally check the Nami.framework into your application repository if you are using source control.

Configure the SDK

We recommend that you configure the Nami SDK as early in your app's launch as possible. This will ensure the SDK is ready to receive and process purchases.

The best spot to do this is in the ApplicationDidFinishLaunchingWithOptions method in the AppDelegate.swift or AppDelegate.m file. We also recommend that you encapsulate the Nami configuration in its own method. Here's a full code example.

For SwiftUI, we recommend adding an init() method if not already present, to your App subclass that starts your application, usually in a file called YourAppNameApp.swift file. We also recommend that you encapsulate the Nami configuration in its own method. Here's a full code example, showing adding an init method:

import Nami

class AppDelegate: UIResponder, UIApplicationDelegate {
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Configure Nami and set up desired handlers
    namiSetup()
  }
}
#import <Nami/Nami.h>

@implementation AppDelegate
  
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [self namiSetup];
}

@end
import Nami

@main
struct ExampleApp: App {
      
  init() {
    namiSetup()
  }
  
  var namiDataSource = NamiDataSource()
    
  var body: some Scene {
    WindowGroup {
      ContentView().environmentObject(namiDataSource)
    }
  }
}

Now to setup Nami, let's look at the namiSetup method. You'll need to go find your App Platform ID in the Control Center for this step.

Note this code is the same for SwiftUI apps.

func namiSetup() {
  let namiConfig : NamiConfiguration = NamiConfiguration( appPlatformID: "YOUR_APP_PLATFORM_ID" )
  namiConfig.logLevel = .warn
  Nami.configure(namiConfig: namiConfig)
}
- (Void) setupNami {
   // Find your appPlatformID in ControlCenter
   NamiConfiguration *namiConfig = [NamiConfiguration configurationForAppPlatformID: @"YOUR_APP_PLATFORM_ID"];
   [namiConfig setLogLevel:NamiLogLevelWarn];
                   
   [Nami configureWithNamiConfig:namiConfig];
}

Nami recommends setting the log level to .warn for apps on the store. .info may be helpful during development to better understand what is going on. .debug level has a lot of information and is likely only helpful to the Nami support team.

📘

SwiftUI NamiDataSource

For SwiftUI apps, you'll see that we referenced a NamiDataSource. You'll need to create this class and publish events to it to use throughout your application.

We'll discuss this more in the section Grant access to paid app features.

Show a paywall

Now that you have the SDK configured, let's show a paywall in your app. This step requires that you have a Configured paywall in the Nami Control Center and that you have a live Campaign with a User-Initiated Paywall.

You may also optionally check if the SDK is able to raise the paywall at the time you are trying to display it.

NamiPaywallManager.preparePaywallForDisplay(backgroundImageRequired: true ) { success, error in
  if success {
   NamiPaywallManager.raisePaywall(fromVC: nil)
  } else if let error = error {
      print("Could not raise paywall, error was \(error.localizedDescription).")
  }
}
[NamiPaywallManager preparePaywallForDisplayWithBackgroundImageRequired:true imageFetchTimeout:10.0 prepareHandler:^(BOOL success, NSError * _Nullable error) {
  if (success) {
    [NamiPaywallManager raisePaywallFromVC:nil];
  } else {
    NSLog(@"Could not raise paywall, error was %@.", [error localizedDescription]);
  }
}];

Grant access to paid app features

Once a user has made a purchase, you'll need to make sure to give them access to the content and features in your app that require a purchase. This is managed on the Nami platform through our entitlement engine.

The first option is to check whether a specific entitlement is active. This is done with the following code.

if NamiEntitlementManager.isEntitlementActive("premium_access") {
 	// allow access to premium app features 
}
if ([NamiEntitlementManager isEntitlementActive:@"premium_access"]) {
   // allow access to premium app features
 }

Nami also triggers a callback any time there is a change to the state of an entitlement. In the callback the full list of currently active entitlements is provided. You can use this to store the state of whether a user has access to premium features locally in your app.

It is important that any callbacks are created as early in the app launch as possible. We recommend adding the callback handlers in the NamiSetup method in your ApplicationDelegate.swift or ApplciationDelegate.m.

NamiEntitlementManager.registerEntitlementsChangedHandler()  { activeEntitlements in
  for ent in activeEntitlements {
    let ent_id = ent.referenceID
    // use the active entitlements reference IDs to grant
    // access to premium app features
  }
}
[NamiEntitlementManager registerEntitlementsChangedHandler:^(NSArray<NamiEntitlement *> * _Nonnull entitlements) {
  for (NamiEntitlement *ent in entitlements) {
    NSString *entID = [ent referenceID];
    // use the active entitlements reference IDs to grant
    // access to premium app features
   }
}];

That's all the basics to get up and running with Nami on Apple. For more use cases, explore the rest of our docs.

SwiftUI

In order to react to changes in the entitlement state of a user in your SwiftUI app, you need to create an ObservableObject you can use to store this state in your app.

The class below shows how to create the NamiDataSource object we used in the Configure the SDK section above. This code will work for any app that only has a single entitlement (the user has access to the paid content or not).

If your app has multiple entitlements or you need to store something like a credit balance, simply create additional @Published variables to store the data you need.

class NamiDataSource: ObservableObject { 
  @Published var purchased = false

  private var listener: NSObjectProtocol?
  
  init() {    
    purchased = (NamiEntitlementManager.activeEntitlements().count > 0)
 
    // react to changes to entitlement state
    NamiEntitlementManager.registerEntitlementsChangedHandler { (activeEntitlements) in
       self.purchased = (activeEntitlements.count > 0)
    }
  }
}

Now to react to the entitlement state of your users, simply reference the NamiDataSource object in your View and access any of the @Published variables. In this case, we simply need to check the boolean purchased.

struct ExampleView: View {
  @EnvironmentObject var namiDataSource: NamiDataSource
  
  var body: some View {
    if namiDataSource.purchased {
      // react to active entitlement 
    }
  }
}