Raise a Paywall

There are many situations where you may want explicit control over when a paywall is displayed in your app. One common example would be any place in your app you have a button or action that should raise the in-app purchase options.

Raising a paywall is done by first calling a method that ensures all data needed to raise the paywall is available, and then actually displaying the paywall itself.

The preparePaywallForDisplay method will ensure you have all the data to display the paywall or provide you an error indicating what the issue is.

Basics

A basic call looks like this:

NamiPaywallManager.preparePaywallForDisplay() { (success, error) in
  if (success) {
    NamiPaywallManager.raisePaywall(fromVC: self)
  } 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]);
  }
}];
NamiPaywallManager.preparePaywallForDisplay { success, error ->
  if (success) {
    NamiPaywallManager.raisePaywall(activity)
  } else {
    println("preparePaywallForDisplay failed -> $error")
  }
}
NamiPaywallManager.preparePaywallForDisplay(new NamiResultCallback<PreparePaywallResult>() {
  @Override
  public void invoke(PreparePaywallResult result) {
    result.onSuccessOrElse(new NamiSuccessHandler() {
      @Override
      public void invoke() {
        NamiPaywallManager.raisePaywall(activity);
      }
    }, new NamiFailureHandler<PreparePaywallError>() {
      @Override
      public void invoke(@NonNull PreparePaywallError error) {
        Log.d("TAG", "preparePaywallForDisplay Error -> " + error);
      }
    });
  }
});
var preparePaywallResult = await NamiPaywallManager.preparePaywallForDisplay();

if (preparePaywallResult.success) {
    NamiPaywallManager.raisePaywall();
} else {
    // Log error or retry prepare or take any other appropriate action
}
NamiPaywallManager.PreparePaywallForDisplay(true, 2.0, (success, error) => {
  if (success) {
    NamiPaywallManager.RaisePaywall(this);
  } else {
    Console.WriteLine($"Error preparing paywall for display: {error.LocalizedDescription}");
  }
});

📘

No Optional Parameters on Some Platforms

The following platforms do not support optional parameters, and you'll have to provide all optional parameters listed in the next section on any call to the preparePaywallForDisplay method.

  • Objective-C
  • React Native
  • Xamarin

👍

Nami Best Practice

You can simply call raisePaywall directly, however, we recommend always wrapping it in preparePaywallForDisplay so that if an error occurs you are able to get more information about what went wrong.

This will display the User-Initiated Paywall in your current live campaign, as set up in the Control Center.

On Apple platforms, passing nil for fromVC will cause the paywall to be raised from the app's root view controller

📘

In order to ensure the paywall renders as quickly as possible if the background image is not available the paywall will display without the image and use the Background Color set on the Overview tab of the Paywall screen on the Control Center.

Additional Controls

If you want to force the paywall to wait to display until the image is available, simply modify the call as follows.

NamiPaywallManager.preparePaywallForDisplay(backgroundImageRequired: true) { (success, error) in
  if (success) {
    NamiPaywallManager.raisePaywall(fromVC: self)
  } 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]);
  }
}];
NamiPaywallManager.preparePaywallForDisplay(backgroundImageRequired = true) { success, error ->
  if (success) {
    NamiPaywallManager.raisePaywall(activity)
  } else {
    println("preparePaywallForDisplay failed -> $error")  
  }
}
NamiPaywallManager.preparePaywallForDisplay(true, (success, error) -> {
    if (success) {
        NamiPaywallManager.raisePaywall(activity);
    } else {
        // Log error or retry prepare or take any other appropriate action
    }
    return Unit.INSTANCE;
});
var preparePaywallResult = await NamiPaywallManager.preparePaywallForDisplay(backgroundImageRequired: true);

if (preparePaywallResult.success) {
    NamiPaywallManager.raisePaywall();
} else {
    // Log error or retry prepare or take any other appropriate action
}

👍

Nami Best Practice

If your app launches a paywall very quickly after installation, we recommend setting backgroundImageRequired to true.

On first launch, Nami must download configuration data and images for all the paywalls your app uses.

After this first launch, all these data are stored locally and will be readily available.

On the first launch after install, if you want your image to have a paywall, this will ensure the image is available before the paywall is displayed.

This call will wait up to about 10 seconds to retrieve the image before it returns an error. If you want more control over how long it waits, please at the following parameters to the call

NamiPaywallManager.preparePaywallForDisplay(backgroundImageRequired: true, imageFetchTimeout: 3) { (success, error) in
  if (success) {
    NamiPaywallManager.raisePaywall(fromVC: self)
  } 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]);
  }
}];
NamiPaywallManager.preparePaywallForDisplay(backgroundImageRequired = true, imageFetchTimeout = 3) { success, error ->
  if (success) {
    NamiPaywallManager.raisePaywall(this)
  } else {
    println("preparePaywallForDisplay failed -> $error")
  }
}
NamiPaywallManager.preparePaywallForDisplay(true, 3L, (success, error) -> {
    if (success) {
        NamiPaywallManager.raisePaywall(activity);
    } else {
        // Log error or retry prepare or take any other appropriate action
    }
    return Unit.INSTANCE;
});
var preparePaywallResult = await NamiPaywallManager.preparePaywallForDisplay(backgroundImageRequired: true, imageFetchTimeout: 3);

if (preparePaywallResult.success) {
    NamiPaywallManager.raisePaywall();
} else {
    // Log error or retry prepare or take any other appropriate action
}

which takes a number in seconds.

Invoking a Specific Paywall

You may also use this method to prepare a specific paywall by its Developer Paywall ID. Rather than showing all the variations here, we show how to raise a paywall by its ID and with all the optional parameters to enforce that the image is available and how long to wait for the image to download.

You may include whichever of these you need for your use case following the general guidelines outlined in the previous section.

NamiPaywallManager.preparePaywallForDisplay(developerPaywallID: "primary", backgroundImageRequired: true, imageFetchTimeout: 3) { (success, error) in
  if (success) {
    NamiPaywallManager.raisePaywall(developerPaywallID: "primary", fromVC: self)
  } else if let error = error {
    print("Could not raise paywall, error was \(error.localizedDescription).")
  }
}
[NamiPaywallManager preparePaywallForDisplayWithDeveloperPaywallID: @"primary" backgroundImageRequired:true imageFetchTimeout:10.0 prepareHandler:^(BOOL success, NSError * _Nullable error) {
  if (success) {
    [NamiPaywallManager raisePaywallWithDeveloperPaywallID:"primary" fromVC:nil];
  } else {
    NSLog(@"Could not raise paywall, error was %@.", [error localizedDescription]);
  }
}];
NamiPaywallManager.preparePaywallForDisplay(
  developerPaywallId = "primary",
  backgroundImageRequired = true,
  imageFetchTimeout = 3L
) { success, error ->
  if (success) {
    NamiPaywallManager.raisePaywall(
      developerPaywallId = "primary", 
      this
    )
  } else {
    println("preparePaywallForDisplay failed -> $error")  
  }
}
NamiPaywallManager.preparePaywallForDisplay("primary", true, 3L, (success, error) -> {
    if (success) {
        NamiPaywallManager.raisePaywall("primary", activity);
    } else {
        // Log error or retry prepare or take any other appropriate action
    }
    return Unit.INSTANCE;
});
var preparePaywallResult = await NamiPaywallManager.preparePaywallForDisplay(developerPaywallId: "primary", backgroundImageRequired: true, imageFetchTimeout: 3);

if (preparePaywallResult.success) {
    NamiPaywallManager.raisePaywall(developerPaywallId: "primary");
} else {
    // Log error or retry prepare or take any other appropriate action
}
NamiPaywallManager.PreparePaywallForDisplayByDeveloperPaywallID("primary", true, 2.0, (success, error) => {
  if (success) {
    NamiPaywallManager.RaisePaywallByDeveloperPaywallID("primary", this);
  } else {
    Console.WriteLine($"Error preparing paywall for display: {error.LocalizedDescription}");
  }
});

React Native

The React Native methods for working with preparePaywallForDisplay work a little differently than other frameworks.

On React Native, this method is a 4 step process.

  1. Create a listener for an event that will fire when the prepare method completes.
  2. Call the prepare method.
  3. In the method invoked by the listener, call the raisePaywall method.
  4. In the callback you also need to remove the event listener.

A couple of notes on this approach.

  1. We recommend not calling this method before you've presented the first screen of your app. This is due to how the underlying SDK loads some data required for the paywall. Note this is different than how we recommend registering other event listeners.
  2. We recommend not invoking a 2nd paywall while there is one already displayed unless you are careful about how you create and delete the event listeners.

The call pattern for raising the paywall for the User-Initiated Paywall for your live campaign has this form.

const {NamiEmitter} = NativeModules;
const eventEmitter = new NativeEventEmitter(NamiEmitter);
 
const onPreparePaywallFinished = (result) => {
  if (result.success == true) {
    NativeModules.NamiPaywallManagerBridge.raisePaywall();
  } else {
    console.log("error is " + result.errorMessage );
  }
  eventEmitter.removeListener('PreparePaywallFinished', onPreparePaywallFinished);
}

eventEmitter.addListener('PreparePaywallFinished', onPreparePaywallFinished);
NativeModules.NamiPaywallManagerBridge.preparePaywallForDisplay(true, 2);

Note that both arguments are required. The first boolean is true/false for backgroundImageRequired and the second argument is an int for the imageFetchTimeout.

If you wish to raise a specific paywall by its Developer Paywall ID please use this slightly different method.

const {NamiEmitter} = NativeModules;
const eventEmitter = new NativeEventEmitter(NamiEmitter);
 
const onPreparePaywallFinished = (result) => {
  if (result.success == true) {
    NativeModules.NamiPaywallManagerBridge.raisePaywallByDeveloperPaywallId(result.developerPaywallID);
  } else {
    console.log("error is " + result.errorMessage );
  }
  eventEmitter.removeListener('PreparePaywallFinished', onPreparePaywallFinished);
}

eventEmitter.addListener('PreparePaywallFinished', onPreparePaywallFinished);
NativeModules.NamiPaywallManagerBridge.preparePaywallForDisplayByDeveloperPaywallId("primary", true, 2);

The first string passed in the preparePaywallForDisplay method is the developerPaywallId.

Errors

The following errors may be returned by the preparePaywallForDisplay method.

@objc enum PreparePaywallError : Int {
  case PAYWALL_ALREADY_DISPLAYED = 0
  case DATA_NOT_AVAILABLE = 1
  case NO_LIVE_CAMPAIGN = 2
  case IMAGE_LOAD_FAILED = 3
  case DEVELOPER_PAYWALL_ID_NOT_FOUND = 4
  case SDK_NOT_INITIALIZED = 5
}
enum PreparePaywallError {
  case PAYWALL_ALREADY_DISPLAYED = 0
  case DATA_NOT_AVAILABLE = 1
  case NO_LIVE_CAMPAIGN = 2
  case IMAGE_LOAD_FAILED = 3
  case DEVELOPER_PAYWALL_ID_NOT_FOUND = 4
  case SDK_NOT_INITIALIZED = 5
}
enum class PreparePaywallError {
  SDK_NOT_INITIALIZED,
  DEVELOPER_PAYWALL_ID_NOT_FOUND,
  PAYWALL_ALREADY_DISPLAYED,
  IMAGE_LOAD_FAILED,
  DATA_NOT_AVAILABLE,
  NO_LIVE_CAMPAIGN
}
enum PreparePaywallError {
  SDK_NOT_INITIALIZED,
  DEVELOPER_PAYWALL_ID_NOT_FOUND,
  PAYWALL_ALREADY_DISPLAYED,
  IMAGE_LOAD_FAILED,
  DATA_NOT_AVAILABLE,
  NO_LIVE_CAMPAIGN
}

What these errors mean:

  • SDK_NOT_INITIALIZED - you must call the Nami.configure method before the SDK will work. If you haven't made this call before you try to raise a paywall, you will get this error.
  • DEVELOPER_PAYWALL_ID_NOT_FOUND - the particular paywall you are trying to raise by Developer Paywall ID was not found.
  • PAYWALL_ALREADY_DISPLAY - for Paywall Creator paywalls, you will get this error if another paywall is already displayed. This error does not occur for Linked Paywalls.
  • IMAGE_LOAD_FAILED - this error will only occur if you set backgroundImageRequired to true and then the image was not able to be downloaded before the imageFetchTimeout was reached.
  • DATA_NOT_AVAILABLE - this error occurs when some data required by the SDK were not available. This most commonly occurs when the data returned by the Nami.configure call is not available yet.
  • NO_LIVE_CAMPAIGN - The Nami SDK requires a campaign to be live in the Control Center for correct functioning. If there is currently no live campaign, you will get this error.