Integrate the software development kit (SDK) into your app before configuring app protection rules for bot management in the console. This topic describes how to integrate the app protection SDK into an iOS app.
Limits
The iOS SDK is available in two versions: an identifier for advertisers (IDFA) version and a non-IDFA version. The corresponding SDK files are:
AliTigerTally_IDFA.framework
AliTigerTally_NOIDFA.framework
If you use IDFA in your iOS project, we recommend that you integrate the AliTigerTally_IDFA SDK. Otherwise, use the AliTigerTally_NOIDFA SDK.
The `init` function is a time-consuming operation. To ensure the full effectiveness of the security capabilities, we recommend that you wait at least 2 seconds after calling the `init` function before you call the `vmpSign` function. This interval is a recommendation to enhance the protection effect of the SDK. You can adjust the interval as needed. However, a shorter interval may compromise the effectiveness of the security features.
The iOS application must run on iOS 9.0 or later. Otherwise, the app protection SDK cannot be integrated.
Prerequisites
You have obtained the SDK for your iOS app.
To obtain the SDK, submit a ticket.
NoteThe SDK file for iOS applications is named tigertally-X.Y.Z-xxxx-ios.zip, where X.Y.Z indicates the version number. The file contains two framework files and two xcframework files.
You have obtained the SDK authentication key (appkey).
When you create a Bots rule set, click Obtain and Copy AppKey to obtain the SDK authentication key. This key is required for SDK initialization requests in your integration code.

Step 1: Create a project
In this example, an iOS project is created in the Xcode environment. After the project is created, the project directory appears as shown in the following figure.
Step 2: Integrate the framework package
Add the AliTigerTally_IDFA.framework or AliTigerTally_NOIDFA.framework SDK file to your project.
IDFA version

Non-IDFA version

Add the AliCaptcha.framework file for the Captcha module to your project.

Add the AliCaptcha.bundle resource file to your project.

Step 3: Add dependency libraries
Dependency library | Required for the IDFA version | Required for the non-IDFA version |
libc++.tbd | Yes | Yes |
libresolv.9.tbd | Yes | Yes |
CoreTelephony.framework | Yes | Yes |
AdSupport.framework | Yes | No |
AppTrackingTransparency.framework | Yes | No |

Step 4: Edit options
In the Other Linker Flags option, add -ObjC.
Step 5: Add the integration code
1. Add the header file
IDFA version:
#import <AliTigerTally_IDFA/AliTigerTally.h>Non-IDFA version:
#import <AliTigerTally_NOIDFA/AliTigerTally.h>
2. Set data signature
Set a custom identifier for the end user. This lets you configure WAF mitigation policies with greater flexibility.
/** * Set user account * * @param account Account information */ - (void)setAccount:(NSString *)account;Parameters:
account: A string that identifies a user. Data type: NSString. We recommend that you use a desensitized value.
Return value: This function does not return a value.
Sample code:
// You can skip calling setAccount for guest users and directly initialize the SDK. After a user logs on, call setAccount and re-initialize. [[AliTigerTally sharedInstance] setAccount:@"testAccount"];
Initialize the SDK to perform a one-time data collection.
A one-time data collection means that information from the end device is collected once. You can call the init function again to re-initialize data collection if needed.
Initialization and collection can be performed in one of three modes: full data collection, custom privacy data collection, or non-privacy data collection. In non-privacy data collection mode, fields that involve the privacy of end device users, such as IDFA and IDFV, are not collected.
NoteWe recommend that you select a collection mode that complies with your internal requirements and ensures data integrity. Complete data helps identify potential threats more effectively.
// Initialization callback. The callback returns the status code of the interface call. typedef void (^TTInitListener)(int); /** * SDK initialization * * @param appkey Key * @param options Optional parameters * @param onInitFinish Callback for initialization completion * @return Whether the initialization is successful */ - (int)init:(NSString *)appkey collectType:(TTCollectType)type options:(NSMutableDictionary *_Nullable)options listener:(TTInitListener _Nullable)onInitFinish;Parameters:
appkey: The SDK authentication key. Data type: NSString.
collectType: The collection mode. Data type: TTCollectType. Valid values:
Field name
Description
Example
TT_DEFAULT
Indicates that full data is collected.
TT_DEFAULT
TT_NO_BASIC_DATA
Indicates that basic device data is not collected.
Includes: device name, system version number, and screen resolution.
TT_NO_X | TT_NO_Y
(Indicates that neither X nor Y is collected. X and Y indicate the field type names of specific collection items.)
TT_NO_UNIQUE_DATA
Indicates that unique identifier data is not collected.
Includes: IDFV and IDFA.
TT_NO_EXTRA_DATA
Indicates that extended device data is not collected.
Includes: connected Wi-Fi information (SSID and BSSID) and the list of nearby Wi-Fi hotspots.
TT_NOT_GRANTED
Indicates that none of the preceding private data is collected.
TT_NOT_GRANTED
options: The options for data collection. Data type: NSMutableDictionary. The default value is nil. The following parameters are available:
Field name
Description
Example
IPv6
Specifies whether to use an IPv6 domain name to report device information.
0 (default): Use an IPv4 domain name.
1: Use an IPv6 domain name.
1
Intl
Specifies whether to use an international domain name to report device information.
0 (default): Report data within mainland China.
1: Report data to an international site.
1
CustomUrl
The domain name of the server to which data is reported.
https://cloudauth-device.us-west-1.aliyuncs.com
CustomHost
The host of the server to which data is reported.
cloudauth-device.us-west-1.aliyuncs.com
NoteFor international sites, you only need to set the `Intl` parameter. The `CustomUrl` and `CustomHost` parameters are only required when you report data to a specified site. For example, the URL for the US (West) site is https://cloudauth-device.us-west-1.aliyuncs.com.
listener: The SDK initialization callback interface. You can check the status of the initialization in the callback. The default value is nil. Data type: TTInitListener.
TTCode
Code
Notes
TT_SUCCESS
0
The SDK is initialized.
TT_NOT_INIT
-1
The SDK is not initialized.
TT_NOT_PERMISSION
-2
The basic iOS permissions required by the SDK are not fully granted.
TT_UNKNOWN_ERROR
-3
An unknown system error occurred.
TT_NETWORK_ERROR
-4
A network error occurred.
TT_NETWORK_ERROR_EMPTY
-5
A network error occurred. The returned content is an empty string.
TT_NETWORK_ERROR_INVALID
-6
The format of the network response is invalid.
TT_PARSE_SRV_CFG_ERROR
-7
Failed to parse the server configuration.
TT_NETWORK_RET_CODE_ERROR
-8
The gateway failed to respond.
TT_APPKEY_EMPTY
-9
The AppKey is empty.
TT_PARAMS_ERROR
-10
Other parameter errors occurred.
TT_FGKEY_ERROR
-11
A key calculation error occurred.
TT_APPKEY_ERROR
-12
The SDK version does not match the AppKey version.
Return value: Returns an integer error code. A value of 0 indicates success. A negative value indicates failure.
Sample code:
// appkey indicates the authentication key assigned by the Alibaba Cloud platform. NSString *appKey = @"xxxxxxxxxxxxxxxxxxxxx"; // Optional parameters. You can configure IPv6 and international site reporting. NSMutableDictionary *options = [[NSMutableDictionary alloc] init]; [options setValue:@"0" forKey:@"IPv6"]; // Configure as IPv4. [options setValue:@"0" forKey:@"Intl"]; // Configure for reporting within mainland China. // [options setValue:@"1" forKey:@"Intl"]; // Configure for international site reporting. // Report to the US (West) site. // [options setValue:@"https://cloudauth-device.us-west-1.aliyuncs.com" forKey:@"CustomUrl"]; // [options setValue:@"cloudauth-device.us-west-1.aliyuncs.com" forKey:@"CustomHost"]; // A single initialization call represents a single device information collection. You can call the init function again to re-initialize the collection as needed. // Full data collection. if (0 == [[AliTigerTally sharedInstance] init:appkey collectType:TT_DEFAULT options:options listener:nil]) { NSLog(@"Initialization successful"); } else { NSLog(@"Initialization failed"); } // Specify privacy data for collection. Different types of private data can be concatenated using "|". TTCollectType collectPrivacy = TT_NO_BASIC_DATA | TT_NO_EXTRA_DATA; int ret = [[AliTigerTally sharedInstance] init:appkey collectType:collectPrivacy options:options listener:nil]; // Do not collect private fields. int ret = [[AliTigerTally sharedInstance] init:appkey collectType:TT_NOT_GRANTED options:options listener:nil];
Hash the data.
This is a custom signing function that calculates the input data to generate a `whash` string as custom signature data. For POST, PUT, and PATCH requests, you must pass the request body. For GET and DELETE requests, you must pass the complete URL. The `whash` string must be added to the ali_sign_whash field in the HTTP request header.
// Request type: typedef NS_ENUM(NSInteger, TTRequestType) { TT_GET=0, TT_POST, TT_PUT, TT_PATCH, TT_DELETE }; /** * Custom signature data hash * @param type Data type * @param input Signature data * @return whash */ - (NSString *)vmpHash:(TTRequestType)type input:(NSData *)input;Parameters:
type: The data type. Data type: TTTypeRequest. Valid values:
GET: Data for a GET request.
POST: Data for a POST request.
PUT: Data for a PUT request.
PATCH: Data for a PATCH request.
DELETE: Data for a DELETE request.
input: The data to be signed. Data type: NSData. Pass the request body or URL based on the type parameter.
Return value: Returns a `whash` string of the NSString type.
Sample code:
// GET request NSString *url = @"https://tigertally.aliyun.com/apptest"; NSString *whash = [[AliTigerTally sharedInstance] vmpHash:TT_GET input:[url dataUsingEncoding:NSUTF8StringEncoding]]; NSLog(@"whash: %@", whash); // POST request NSString *body = @"hello world"; NSString *whash = [[AliTigerTally sharedInstance] vmpHash:TT_POST input:[body dataUsingEncoding:NSUTF8StringEncoding]]; NSLog(@"whash: %@", whash);
NoteYou do not need to call this function if you configure default signing in the console. If you select custom signing, you must call this function for hash verification before data signing.
Sign the data.
This function uses vmp technology to sign the input data and returns a `wtoken` string for request authentication.
/** * Data signature * @param input Signature data * @return wtoken */ - (NSString *)vmpSign:(NSData *)input;Parameters:
input: The data to be signed. Data type: NSData. This is typically the entire request body or the whash string for custom signing.
Return value: Returns a `wtoken` string of the NSString type.
Sample code:
// Default signing configured in the console (Custom signing is not selected) NSString *body = @"hello world"; NSString *wtoken = [[AliTigerTally sharedInstance] vmpSign:[body dataUsingEncoding:NSUTF8StringEncoding]]; NSLog(@"wtoken: %@", wtoken); // Custom signing configured in the console // Custom signing for a POST request NSString *whash = [[AliTigerTally sharedInstance] vmpHash:TT_POST input:[body dataUsingEncoding:NSUTF8StringEncoding]]; NSString *wtoken = [[AliTigerTally sharedInstance] vmpSign:[whash dataUsingEncoding:NSUTF8StringEncoding]]; NSLog(@"whash: %@, wtoken: %@", whash, wtoken); // Custom signing for a GET request NSString *url = @"https://tigertally.aliyun.com/apptest"; NSString *whash = [[AliTigerTally sharedInstance] vmpHash:TT_GET input:[url dataUsingEncoding:NSUTF8StringEncoding]]; NSString *wtoken = [[AliTigerTally sharedInstance] vmpSign:[whash dataUsingEncoding:NSUTF8StringEncoding]]; NSLog(@"whash: %@, wtoken: %@", whash, wtoken);NoteWhen you call vmpHash for custom signing, the input parameter of the vmpSign function is the generated `whash` string. When you configure scenario-specific app protection policies, the value of Custom Signing Field must be set to `ali_sign_whash`.
When you call vmpHash to generate a `whash` for a GET request, you must ensure that the input URL is identical to the final URL of the network request. Pay special attention to URL encoding, as some frameworks automatically encode Chinese characters or other parameters.
The input parameter of the vmpHash function does not support empty byte arrays or empty strings. If the input is a URL, it must contain a path or a parameter.
When you call vmpSign, if the request body is empty (for example, the body of a POST or GET request is empty), pass a nil object or the NSData value of an empty string, such as [@"" dataUsingEncoding:NSUTF8StringEncoding].
If one of the following `whash` or `wtoken` strings is returned, an exception occurred during initialization:
you must call init first: The `init` function was not called.
you must input correct data: The input data is invalid.
you must input correct type: The input type is invalid.
3. Two-factor authentication
Check the result.
Determine whether to perform two-factor authentication based on the cookie and body fields in the response. If multiple Set-Cookie headers exist, you must merge them into the cookie format before you call this function.
/** * Whether to perform two-factor authentication * * @param cookie response cookie * @param body response body * @return 0: Pass 1: Two-factor authentication */ - (int)cptCheck:(NSString *)cookie body:(NSData *)body;Parameters:
cookie: All cookies from the request response. Data type: NSString.
body: The entire body from the request response. Data type: NSData.
Return value: Returns an integer. A value of 0 indicates that the request passed verification. A value of 1 indicates that two-factor authentication is required.
Sample code:
NSString *cookie = @"key1=value1;key2=value2;"; NSData *body = xxx; int recheck = [[AliTigerTally sharedInstance] cptCheck:cookie body:body]; NSLog(@"recheck: %d", recheck);
Create a slider.
Determine whether to create a slider object based on the result returned by cptCheck. The TTCaptcha object provides `show` and `dismiss` methods to show and hide the slider window. TTOption encapsulates the configurable parameters for the slider. TTListener contains the two callback states for the slider. If you want to use a custom slider window, you must specify the address of the custom page. You can use local HTML files or remote pages.
/** * Display slider verification * * @param view Parent component * @param option Parameters * @param detegate Callback protocol */ - (TTCaptcha *)cptCreate:(UIView *)view option:(TTOption *)option delegate:(id<TTDelegate>)detegate; @protocol TTDelegate <NSObject> @required // Slider verification successful - (void)success:(TTCaptcha *)captcha data:(NSString *)data; // Slider verification failed - (void)failed:(TTCaptcha *)captcha code:(NSString *)code; @end @interface TTOption : NSObject // Click to cancel @property (nonatomic, assign) BOOL cancelable; // Custom page @property (nonatomic, strong) NSString *customUri; // Language @property (nonatomic, strong) NSString *language; @end @interface TTCaptcha : NSObject - (instancetype)init:(UIView *)view option:(TTOption *)option delegate:(id<TTDelegate>)delegate; // Obtain the slider traceId for data statistics - (NSString *)getTraceId; // Show the slider - (void)show; // Dismiss the slider - (void)dismiss; @endParameters:
view: The current page view. Data type: View.
option: The slider configuration parameters. Data type: TTOption.
listener: The slider status callback. Data type: TTDelegate.
Return value: Returns a slider object of the TTCaptcha type.
Sample code:
#pragma mark - TTDelegate - (void)failed:(TTCaptcha *)captcha code:(nonnull NSString *)code { NSLog(@"captcha failed: %@", code); } - (void)success:(TTCaptcha *)captcha data:(nonnull NSString *)data { NSLog(@"captcha success: %@", data); } TTOption *option = [[TTOption alloc] init]; // option.customUri = @"ali-tt-captcha-demo-ios"; option.language = @"cn"; option.cancelable = true; TTCaptcha *captcha = [[AliTigerTally sharedInstance] cptCreate:[self view] option:option delegate:self]; [captcha show];NoteA 'Verification failed' error indicates that an exception is detected during or after the user slides the slider.
The following table describes the error codes.
1001: Verification failed.
1002: System exception.
1003: Parameter error.
1005: Verification canceled.
8001: Slider invocation error.
8002: Abnormal slider verification data.
8003: Slider verification internal exception.
8004: Network error.
Best practices
#import "DemoController.h"
#if __has_include(<AliTigerTally_NOIDFA/AliTigerTally_NOIDFA.h>)
#import <AliTigerTally_NOIDFA/AliTigerTally_NOIDFA.h>
#else
#import <AliTigerTally_IDFA/AliTigerTally_IDFA.h>
#endif
@interface DemoController () <TTDelegate>
@end
static NSString *kAppHost = @"******";
static NSString *kAppUrl = @"******";
static NSString *kAppkey = @"******";
@implementation DemoController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self doTest];
}
- (void)doTest {
NSThread *thread = [[NSThread alloc] initWithBlock:^{
// Initialize
NSMutableDictionary *options = [[NSMutableDictionary alloc] init];
// [options setValue:@"1" forKey:@"Intl"]; // Configure for international site reporting
int code = [[AliTigerTally sharedInstance] init:kAppkey collectType:TT_DEFAULT options:options listener:nil];
NSLog(@"tigertally init: %d", code);
// Do not call synchronously immediately
[NSThread sleepForTimeInterval:2.0];
// Sign
NSString *body = @"hello world";
NSString *whash = nil, *wtoken = nil;
// Custom signing
whash = [[AliTigerTally sharedInstance] vmpHash:TT_Post input:[body dataUsingEncoding:NSUTF8StringEncoding]];
wtoken = [[AliTigerTally sharedInstance] vmpSign:[whash dataUsingEncoding:NSUTF8StringEncoding]];
NSLog(@"tigertally vmp: %@, %@", whash, wtoken);
// Default normal signing
// wtoken = [[AliTigerTally sharedInstance] vmpSign:[body dataUsingEncoding:NSUTF8StringEncoding]];
// NSLog(@"tigertally vmp: %@", wtoken);
[self doPost:kAppUrl host:kAppHost whash:whash wtoken:wtoken body:[body dataUsingEncoding:NSUTF8StringEncoding] callback:^(NSInteger code, NSString * _Nonnull cookie, NSData * _Nonnull body) {
int check = [[AliTigerTally sharedInstance] cptCheck:cookie body:body];
NSLog(@"captcha result:%d", check);
if (check == 0) return;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self doShow];
}];
}];
}];
[thread start];
}
- (void)doShow {
NSLog(@"captcha show");
TTOption *option = [[TTOption alloc] init];
// option.customUri = @"ali-tt-captcha-demo-ios";
option.language = @"cn";
option.cancelable = true;
TTCaptcha *captcha = [[AliTigerTally sharedInstance] cptCreate:[self view] option:option delegate:self];
[captcha show];
}
- (void)doPost:(NSString *)url host:(NSString *)host whash:(NSString *)whash wtoken:(NSString *)wtoken body:(NSData *)body callback:(void(^)(NSInteger code, NSString* cookie, NSData *body))callback {
NSLog(@"start reqeust post");
NSURL* requestUrl = [NSURL URLWithString: url];
NSData* requestBody = body;
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:requestUrl cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
[request setValue: @"text/x-markdown" forHTTPHeaderField: @"Content-Type"];
[request setValue: host forHTTPHeaderField: @"HOST"];
[request setValue: wtoken forHTTPHeaderField:@"wToken"];
if (whash) {
[request setValue:whash forHTTPHeaderField:@"ali_sign_whash"];
}
request.HTTPMethod = @"post";
request.HTTPBody = requestBody;
NSURLSessionDataTask* dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
NSLog(@"tiger tally sign failed: %@", [error description]);
callback(-1, nil, [[error description] dataUsingEncoding:NSUTF8StringEncoding]);
return;
}
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response;
NSInteger code = httpResponse.statusCode;
NSString* body = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSMutableString* cookies = [[NSMutableString alloc] init];
for (NSHTTPCookie* cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
if ([url containsString:[cookie domain]]) {
NSLog(@"domain:%@, path: %@, name:%@, value: %@", [cookie domain], [cookie path], [cookie name], [cookie value]);
[cookies appendFormat: @"%@=%@;", [cookie name], [cookie value]];
}
}
NSLog(@"reponse code: %ld", code);
NSLog(@"reponse cookie: %@", cookies);
NSLog(@"reponse body: %@", body ? (body.length > 100 ? [body substringToIndex:100]:body) : @"");
callback(code, cookies, data);
}];
[dataTask resume];
}
#pragma mark - TTDelegate
- (void)failed:(TTCaptcha *)captcha code:(nonnull NSString *)code {
NSLog(@"captcha failed: %@", code);
UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:code preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:true completion:nil];
}
- (void)success:(TTCaptcha *)captcha data:(nonnull NSString *)data {
NSLog(@"captcha success: %@", data);
UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:data preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:true completion:nil];
}
@end