Configuring Advanced Features

In this segment we will explore more advanced topics that will help you get the most out of the SDK.


The AppID is the key to start the PerimeterX SDK, by calling the start function with it. If you are using only one AppID in your app, all other functions in the SDK will work properly without specifying the AppID again (you may pass nil).


The PerimeterXDelegate provides callbacks, such as request blocked or challenge solved. However, The delegate can be set only once when calling the start function. You can set that more objects in your project to receive those callbacks too. Use the registerCallbackForRequestBlockedEvent and the registerCallbackForChallengeSolvedEvent functions. Here is an example:

let requestBlockedEventRegistrationId = PerimeterX.registerCallbackForRequestBlockedEvent {
    print("PerimeterX Request Blocked")

let challengeSolvedEventRegistrationId = PerimeterX.registerCallbackForChallengeSolvedEvent(callback: {
    print("PerimeterX Challenge Solved")

let challengeCancelledEventRegistrationId = PerimeterX.registerCallbackForChallengeCancelledEvent(callback: {
    print("PerimeterX Challenge Cancelled")
NSString *requestBlockedEventRegistrationId = [PerimeterX registerCallbackForRequestBlockedEventForAppId:nil callback:^{
    NSLog(@"Request Blocked Event");

NSString *challengeSolvedEventRegistrationId = [PerimeterX registerCallbackForChallengeSolvedEventForAppId:nil callback:^{
    NSLog(@"Challenge Solved Event");

NSString *challengeCancelledEventRegistrationId = [PerimeterX registerCallbackForChallengeCancelledEventForAppId:nil callback:^{
    NSLog(@"Challenge Cancelled Event");

The registerCallbackForRequestBlockedEvent, the registerCallbackForChallengeSolvedEvent and the registerCallbackForChallengeCancelledEvent functions return a Registration ID. Use it to unregister from getting those callbacks with unregisterCallbackForRequestBlockedEvent, unregisterCallbackForChallengeSolvedEvent and 'unregisterCallbackForChallengeCancelledEvent' functions. Here is an example:

PerimeterX.unregisterCallbackForRequestBlockedEvent(forAppId: nil, registrationId: requestBlockedEventRegistrationId)

PerimeterX.unregisterCallbackForChallengeSolvedEvent(forAppId: nil, registrationId: challengeSolvedEventRegistrationId)

PerimeterX.unregisterCallbackForChallengeCancelledEvent(forAppId: nil, registrationId: challengeCancelledEventRegistrationId)
[PerimeterX unregisterCallbackForRequestBlockedEventForAppId:nil registrationId:requestBlockedEventRegistrationId];

[PerimeterX unregisterCallbackForChallengeSolvedEventForAppId:nil registrationId:challengeSolvedEventRegistrationId];

[PerimeterX unregisterCallbackForChallengeCancelledEventForAppId:nil registrationId:challengeCancelledEventRegistrationId];

Another important callback is when the Perimeter SDK is ready. Calling the start function will start an async operation that will start the PerimeterX SDK. This function has a completion handler, but you can register to this event from other places in your project, using the addInitializationFinishedCallback function. Here is an example:

PerimeterX.addInitializationFinishedCallback(forAppId: nil) {
    print("PerimeterX is ready")
[PerimeterX addInitializationFinishedCallbackForAppId:nil callback:^{
    NSLog(@"PerimeterX is ready");

You can call the addInitializationFinishedCallback when the PerimeterX SDK is ready. In this case, the callback will be called immediately.

Block Error

When the requestsInterceptedAutomaticallyEnabled is set to true, the PerimeterX SDK will automatically block requests and present a challenge to the end user. When there is a block, the original request will fail with the following Error: domain: PerimeterXErrorDomain, code: PerimeterXErrorCode/requestWasBlocked. You can check the received error with the isRequestBlockedError function. Here is an example:

let isRequestBlockedError = PerimeterX.isRequestBlockedError(error: error)
if isRequestBlockedError {
    print("request was blocked by PX")
BOOL isRequestBlockedError = [PerimeterX isRequestBlockedErrorWithError:error];
if (isRequestBlockedError) {
    NSLog(@"request was blocked by PX");

If your are using Alamofire, you should pass the AFError.underlyingError object to the isRequestBlockedError function. Here is an example:

if let error = response.error?.underlyingError {
    let isRequestBlockedError = PerimeterX.isRequestBlockedError(error: error)
    if isRequestBlockedError {
        print("request was blocked by PX")

Block response with the challenge result

The SDK automatically intercepts HTTP requests. Once a request is blocked, a "request blocked" error is returned to your handler code, while the user is solving the challenge. However, you can configure the SDK to delay the response until the user solved the challenge (or cancelled it). In this way, you can retry the original request a soon as you get the error (assuming the user solved the challenge) without waiting for this event to be occurred elsewhere in your code.

You can check the error with isChallengeSolvedError and isChallengeCancelledError functions:

let isChallengeSolvedError = PerimeterX.isChallengeSolvedError(error: error)
if isChallengeSolvedError {
    print("request was blocked by PX and user solved the challenge")

let isChallengeCancelledError = PerimeterX.isChallengeCancelledError(error: error)
if isChallengeCancelledError {
    print("request was blocked by PX and challenge was cancelled")
BOOL isChallengeSolvedError = [PerimeterX isChallengeSolvedError:error];
if (isChallengeSolvedError) {
    NSLog(@"request was blocked by PX and user solved the challenge");

BOOL isChallengeCancelledError = [PerimeterX isChallengeCancelledError:error];
if (isChallengeCancelledError) {
    NSLog(@"request was blocked by PX and challenge was cancelled");

To enable this feature (disabled by default) you should configure it in the policy:

let policy = PXPolicy()
policy.delayResponseUntilChallengeSolvedOrCancelled = true
PerimeterX.setPolicy(policy: policy, forAppId: nil, completion: nil)
PXPolicy *policy = [[PXPolicy alloc] init];
policy.delayResponseUntilChallengeSolvedOrCancelled = YES;
[PerimeterX setPolicyWithPolicy:policy forAppId:nil completion:nil];

Notice that enabling this could mean that your request handler code will not be called at all (for example, when the user does not solve nor cancel the challenge).

Hybrid App

The hybrid App uses both native URL requests and web views to communicate with the server. In the context of PerimeterX, it's important to make sure both native requests and web views are synced together to make sure end users will get the expected behavior from your app. To help PerimeterX SDK achieve that, you should set your domain in the policy for the relevant AppID. Here is an example:

let policy = PXPolicy()"")
PerimeterX.setPolicy(policy: policy, forAppId: nil, completion: nil)
PXPolicy *policy = [[PXPolicy alloc] init]; = [NSSet setWithObject:@""];
[PerimeterX setPolicyWithPolicy:policy forAppId:nil completion:nil];

If your app using Apple Pay on the Web you should disable JavaScript evaluation by the SDK. In order to protect the security of Apple Pay transactions in WKWebView, Apple Pay cannot be used alongside of script injection APIs. You can do this by setting the policy. Here is an example:

let policy = PXPolicy()
policy.allowJavaScriptEvaluation = false
PerimeterX.setPolicy(policy: policy, forAppId: nil, completion: nil)
PXPolicy *policy = [[PXPolicy alloc] init];
policy.allowJavaScriptEvaluation = NO;
[PerimeterX setPolicyWithPolicy:policy forAppId:nil completion:nil];

You can turn off the hybrid app support by calling the turnOffHybridAppSupport function. In order that the hybrid app support will be turned off, you must call this function before calling the start function.

Multiple AppIDs

You can configure multiple AppIDs for the PerimeterX SDK, by calling the start function for each AppID you have.

When using multiple AppIDs, you should:

  1. Specify the relevant AppID anytime you call functions in the PerimeterX SDK.
  2. Set the domain list in the policy for each AppID. Here is an example:
let policyForAppId1 = PXPolicy()"")
PerimeterX.setPolicy(policy: policyForAppId1, forAppId: "<APP_ID_1>", completion: nil)

let policyForAppId2 = PXPolicy()"")
PerimeterX.setPolicy(policy: policyForAppId2, forAppId: "<APP_ID_2>", completion: nil)
PXPolicy *policyForAppId1 = [[PXPolicy alloc] init]; = [NSSet setWithObject:@""];
[PerimeterX setPolicyWithPolicy:policyForAppId1 forAppId:@"<APP_ID_1>" completion:nil];

PXPolicy *policyForAppId2 = [[PXPolicy alloc] init]; = [NSSet setWithObject:@""];
[PerimeterX setPolicyWithPolicy:policyForAppId2 forAppId:@"<APP_ID_2>" completion:nil];

Doctor App

The "Doctor App" is a tool that helps verify the SDK integration in your project by simulating a typical user flow in the application. When the "Doctor app" is enabled, it will pop up as part of the app flow and will guide the developer on simulating a user flow to gather the required information for testing the integration.

The "Doctor App" includes assets that are used for the UI. Those assets are required only for the "Doctor App" usage and are not needed in your app production build. In order minimize the SDK footprint, those assets are loaded dynamically in run time (from v2.1.0).

If you run the "Doctor App" on a real device with iOS 14 or above, those assets will be loaded automatically (internet connection is required). However, if you run the "Doctor App" on a simulator or on a real device with iOS 13 or less, you will need to add the PerimeterX.bundle file to your project. You need to remember to remove it from your project before you build and deploy your app to production.

You can get the PerimeterX.bundle file here (please refer to the relevant Mobile SDK version):

How to add the PerimeterX.bundle file to your project?

  1. Download the file from the link above.
  2. Unzip it and drag the PerimeterX.bundle file to your project (make sure to link it to relevant targets).

In order to enable this feature, call the start function with the enableDoctorCheck as true. Here is an example:

PerimeterX.start(appId: "<APP_ID>", delegate: self, enableDoctorCheck: true) { success, error in
[PerimeterX startWithAppId:@"<APP_ID>" delegate:self enableDoctorCheck:YES completion:^(BOOL success, NSError * _Nullable error) {



Important Notice

This feature is for development only and should not be shipped as enabled with the application to the store.


  1. Welcome screen
    In this screen you select whether to start a new test or load the summary of a previous test, if one exists.
  1. Instruction screen
    In this screen you get a detailed explanation on how to start a new test, what is expected to happen and what you can do in case you are not able to generate a challenge/captcha.
  1. Test selection screen
    In this screen you can select between two different types of tests:
    1. Native app framework - to test your native URL requests.
    2. Web view framework - to test your web view URL requests.
      When you are done executing a test, you will return to this screen to enable you to start the other test.
  1. Summary screen
    In this screen you review the test results. You can go into details regarding each test and get troubleshooting tips in case a test failed, to help you analyze what is causing this issue.


Please Note

When you exit the "Doctor App", your app will also terminated. Just remember to switch the enableDoctorCheck param to false in case you finished validating your SDK integration.

Enable support for Account Defender

In order to enable support for PerimeterX Account Defender, a user ID should be set. You can set it by calling the setUserId function.

PerimeterX.setUserId(userId: "<the current user ID>", forAppId: nil)
[PerimeterX setUserIdWithUserId:@"<the current user ID>" forAppId:nil];

When the current user logs out, you should call this function again with nil. This will turn off the Account Defender supported features.

To properly send Account Defender the complete user flow, call this function for each URL request that has been sent to your server. Notice that when the interceptor is enabled, this is done automatically by the SDK and there is no need to manually call the function.

PerimeterX.registerOutgoingUrlRequest(url: "<URL>", forAppId: nil)
[PerimeterX registerOutgoingUrlRequestWithUrl:@"<URL>" forAppId:nil];

Did this page help you?