iOS

集成要求

合规说明

请注意,在贵司的App中集成同盾提供的SDK产品时:

1.1 根据《网络安全法》《电信条例》《电信和互联网用户个人信息保护规定》等相关法律法规要求及监管实践中的标准,在贵司的最终用户首次启动App并在贵司开始采集信息之前,贵司应以交互界面或设计(如隐私政策弹窗等)向最终用户完整告知收集、使用、与第三方共享最终用户个人信息的目的、方式和范围,并征得最终用户的明示同意。

1.2 为向贵司提供业务安全和风控服务,同盾SDK将采集、处理、使用用户的手机终端唯一标志信息(IMEI/IDFA)、Android ID、OAID、IMSI、MEID、MAC 地址、SIM 卡序列号、设备序列号、设备类型、设备型号、系统类型、地理位置、登录 IP 地址等设备信息。为确保贵司使用相关服务的合规性,前述隐私政策应涵盖对同盾SDK提供服务并采集、处理、使用相关信息的授权,以下条款内容供贵司参考,具体表述可由贵司根据贵司隐私协议的整体框架和内容自行确定:

同盾SDK:为了业务安全和风控,我司使用了同盾 SDK,该 SDK 需要获取您的手机终端唯一标志信息(IMEI/IDFA)、Android ID、OAID、IMSI、MEID、MAC 地址、SIM卡序列号、设备序列号、设备类型、设备型号、系统类型、地理位置、登录 IP 地址、应用程序列表、运行中进程信息、传感器(光传感器、重力传感器、磁场传感器、加速度传感器、陀螺仪传感器、心率传感器)相关设备信息,用于设备欺诈风险识别。

同盾隐私协议:https://www.tongdun.cn/other/privacy/id=1

环境要求

条目说明
兼容版本iOS11.0+
支持架构arm64

集成步骤

CocoaPods集成

  • 在 Podfile 文件中对应 target 中新增 pod 'TrustDecisionPro', '4.2.6.5'
  • 在 Podfile 文件中对应 target 中新增 pod 'TrustDecisionLiveness', '2.2.1'
  • 在 Podfile 所在文件夹中执行 pod install --repo-update 命令 (M1系列mac电脑需要执行 arch -x86_64 pod install --repo-update 命令)

SDK目录

Pods/TrustDecisionPro 文件夹下存在如下文件:

  • TDMobRisk.framework(终端SDK主文件,静态库类型)
  • TDCorePlugin.framework (终端SDK依赖核心插件,Embed动态库类型)

Pods/TrustDecisionLiveness 文件夹下存在如下文件:

  • libTDLiveness.a(终端SDK 活体组件,静态库类型)
  • CWResource.bundle (终端SDK 活体组件需要用到的资源包)

引入头文件

在调用的位置引入头文件

#import <TDMobRisk/TDMobRisk.h>
import TDMobRisk

SDK初始化

注意事项

安装后首次启动时,在用户同意隐私协议后,再进行SDK初始化。

避免出现用户未同意隐私协议就进行SDK初始化采集,引发合规风险事故。

接口定义

void (*initWithOptions)(NSDictionary *options);

initWithOptions方法必传配置

配置 key 定义 说明 场景 示例代码
partner 合作方编码 同盾的合作方编码,请联系同盾运营获取 所有场景 Objective C
[options setValue:@"请输入您的合作方编码" forKey:@"partner"];
Swift
options.updateValue("请输入您的合作方编码" , forKey: "partner")
appKey 应用标识 同盾生成的应用标识,和app绑定,用于校验app的有效性,请联系同盾运营获取。
appkey创建需要用户提供应用包名
所有场景 Objective C
[options setValue:@"请输入您的appKey" forKey:@"appKey"];
Swift
options.updateValue("请输入您的appKey" , forKey: "appKey")
partnerKey 合作方Key 同盾的合作方Key,请联系同盾运营获取 所有场景 Objective C
[options setValue:@"请输入您的合作方Key" forKey:@"partnerKey"];
Swift
options.updateValue("请输入您的合作方Key" , forKey: "partnerKey")
country 国家地区 国家地区参数,如cn sg us fra idna 根据国家地区填写对应参数。
cn代表中国,
sg代表新加坡,
us代表北美,
fra代表欧洲,
idna代表印尼
Objective C
[options setValue:@"请输入您所在的国家地区" forKey:@"country"];
Swift
options.updateValue("请输入您所在的国家地区" , forKey: "country")

示例代码

- (void)initTrustDeviceSDK {
  TDMobRiskManager_t *riskManager = [TDMobRiskManager sharedManager];
  NSMutableDictionary *options = [NSMutableDictionary dictionary];

  /*************************** 必传 ***************************/
  // 合作方编码,如tongdun,参考`必传配置`
  [options setValue:@"请输入您的合作方编码" forKey:@"partner"];
    // 同盾平台注册的应用标识,参考`必传配置`
  [options setValue:@"请输入您的appKey" forKey:@"appKey"];
  // 同盾平台注册的合作方Key,参考`必传配置`
  [options setValue:@"请输入您的partnerKey" forKey:@"partnerKey"];
  // 国家地区参数,参考`必传配置`
  [options setValue:@"请输入您所在的国家地区" forKey:@"country"];

  /*************************** 选传 ***************************/
#ifdef DEBUG
  // !!! DEBUG模式下若不设置此参数,app运行会闪退
  [options setValue:@(YES) forKey:@"allowed"];
#endif
  // !!!callback方式已移除,现新增getBlackBoxAsync的方式去异步获取blackbox
  riskManager->initWithOptions(options);
}
func initTrustDecisionSDK() {
  let riskManager = TDMobRiskManager.sharedManager()
  var options = Dictionary<String, Any>()
  /*************************** 必传 ***************************/
  // 合作方编码,如tongdun,参考`必传配置`
  options.updateValue("请输入您的合作方编码", forKey: "partner")
    // 同盾平台注册的应用标识,参考`必传配置`
  options.updateValue("请输入您的appKey", forKey: "appKey")
  // 同盾平台注册的应用标识,参考`必传配置`
  options.updateValue("请输入您的partnerKey", forKey: "partnerKey")
  // 国家地区参数,参考`必传配置`
  options.updateValue("请输入您所在的国家地区", forKey: "country")

  /*************************** 选传 ***************************/
#if DEBUG
  // !!! DEBUG模式下若不设置此参数,app运行会闪退
  options.updateValue(true, forKey: "allowed");
#endif
  // !!!callback方式已移除,现新增getBlackBoxAsync的方式去异步获取blackbox
  riskManager?.pointee.initWithOptions(options)
}

获取blackBox

注意事项

  • 请在 initWithOptionsgetBlackBoxAsyncgetBlackBox,否则会引起SDK异常
  • 建议开发者不要在App内对返回的blackBox进行缓存,获取blackBox请依赖以下函数

异步方法 getBlackBoxAsync (推荐)

  /* 异步获取blackBox
   */
  void (*getBlackBoxAsync)(TDGetBlackBoxAsyncBlock block);

使用场景说明

优点: 网络正常情况下返回非降级blackBox,会降低后续查询接口上传的数据量,数据量大小为26字节左右;
缺点: 不是立即返回,根据网络情况进行等待,一般耗时300ms左右返回;
适用场景: 需要获取最新且为非降级blackBox的场景;

示例代码

TDMobRiskManager_t *riskManager = [TDMobRiskManager sharedManager];
riskManager->getBlackBoxAsync(^(NSString* blackBox){
    NSLog(@"blackBox:%@",blackBox);
});
let riskManager = TDMobRiskManager.sharedManager()
riskManager?.pointee.getBlackBoxAsync(){(blackBoxString : String!)->Void in
   print("blackBox:" + blackBoxString)                               
}

同步方法 getBlackBox

  /* 同步获取blackBox
   * @return 初始化SDK后,同步获取的blackBox
   */
  NSString (*getBlackBox)(void);

使用场景说明

优点: 会立即返回blackBox,不受网络状态的影响;
缺点: 在集成设备指纹SDK后,在之前没有获取到非降级blackBox的情况下,会返回降级blackBox,会增大后续查询接口上传的数据量,数据量大小为5000字节左右;
适用场景: 需要立即获取blackBox的场景;

示例代码

// 同步获取blackBox,未进行SDK初始化禁止调用,否则会引起SDK异常。
TDMobRiskManager_t *riskManager = [TDMobRiskManager sharedManager];
NSString *blackBox = riskManager->getBlackBox();
// 同步获取blackBox,未进行SDK初始化禁止调用,否则会引起SDK异常。
let riskManager = TDMobRiskManager.sharedManager()
let blackBox = riskManager?.pointee.getBlackBox()

弹出活体弹窗

initWithOptions方法选传参数

配置 key 定义 说明 场景 示例代码
language 语言类型
Default: en 英语 Options:
en 英语
zh-Hans 简体中文
zh-Hant 繁體中文
es 西班牙语
id 印尼语
ar 阿拉伯语
fil 菲律宾语
ko 韩语
pt 葡萄牙语
ru 俄语
th 泰语
tr 土耳其语
vi 越南语
客户根据需要设置语言类型 Objective C
[options setValue:@"en" forKey:@"language"];
Swift
options.updateValue("en", forKey: "language")
playAudio 是否播报语音 默认为 NO,不播报语音 开启后,会播报对应提示语音 Objective C
[options setValue:@NO forKey:@"playAudio"];
Swift
options.updateValue(false, forKey: "playAudio")
livenessHttpTimeOut SDK网络超时时间配置(单位:秒) 默认为15s 客户根据需要设置网络超时时间 Objective C
[options setValue:@8 forKey:@"livenessHttpTimeOut"];
Swift
options.updateValue(8, forKey: "livenessHttpTimeOut")
showReadyPage 启动人脸时,会弹出检测准备页面 是否显示准备页面, 默认为 YES, 即显示 关闭后,不显示准备页面,识别流程更短 Objective C
[options setValue:@YES forKey:@"showReadyPage"];
Swift
options.updateValue(true, forKey: "showReadyPage")
faceMissingInterval 没有检测到人脸时的超时时间 (单位:毫秒) 无人脸超时时间, 单位ms 默认为 1000ms 根据需要设置没有检测到人脸时的超时时间 Objective C
[options setValue:@(1000) forKey:@"faceMissingInterval"];
Swift
options.updateValue(1000, forKey: "faceMissingInterval")
prepareStageTimeout 准备检测动作时候的起始时间 (单位:秒) 准备阶段超时时间, 单位秒 默认为 0S, 即永远不超时 根据需要设置 准备阶段超时时间 Objective C
[options setValue:@(0) forKey:@"prepareStageTimeout"];
Swift
options.updateValue(0, forKey: "prepareStageTimeout")
actionStageTimeout 动作阶段中, 最长验证时间 (单位:秒) 动作阶段超时时间, 单位秒 默认为 8S 根据需要设置 动作阶段超时时间 Objective C
[options setValue:@(8) forKey:@"actionStageTimeout"];
Swift
options.updateValue(8, forKey: "actionStageTimeout")

showLiveness

showLiveness 函数 用于显示活体界面,其接口定义如下

/**
 * Display Liveness window
 * @usage: manager->showLiveness(self,^(TDLivenessResultStruct resultStruct) {
 * });
 * @param targetVC The parent view where the popup is displayed, UIViewController type
 * @param block result callback block
*/
void (*showLiveness)(id targetVC,TDShowLivenessBlock block);

回调参数结构体TDLivenessResultStruct

我们使用TDLivenessResultStruct 结构体存储回调的结果;

typedef enum {
    
    // The Liveness is verified successfully;
    TDLivenessResultTypeSuccess,
    // If the Liveness fails, you can get the error code errorCode and errorMsg, and check the cause according to the `Error code` in the document;
    TDLivenessResultTypeFailed,
    // The Liveness window pop-up is successful, waiting to be verified;
    TDLivenessResultTypeReady,
    
} TDLivenessResultType;

typedef struct _TDLivenessResultStruct{
    // return result type
    const TDLivenessResultType resultType;
    // the seqId of the Liveness returned
    char* const seqId;
    // After the return fails, the returned error code can be checked according to the document
    const NSInteger errorCode;
    // Return error message after failure
    char* const errorMsg;
    // liveness score when success
    double score;
    // best image string
    char* const bestImageString;
    
}TDLivenessResultStruct;

示例代码

- (void)showLiveness {
    [TDMobRiskManager sharedManager]->showLiveness(self,^(TDLivenessResultStruct resultStruct) {
        if(resultStruct.resultType == TDLivenessResultTypeSuccess){
            // 如果存在最佳照片
            if(resultStruct.bestImageString.length > 0){
                NSData* data = [[NSData alloc]initWithBase64EncodedString:resultStruct.bestImageString options:NSDataBase64DecodingIgnoreUnknownCharacters];
                UIImage * image = [UIImage imageWithData:data];
                UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
            }
            
            NSString*successMsg = [NSString stringWithUTF8String:resultStruct.errorMsg];
            NSLog(@"successMsg-::%@,score:%f",successMsg,resultStruct.score);
        }else{
            NSString*errorMsg = [NSString stringWithUTF8String:resultStruct.errorMsg];
            NSString*errorMsgInfo = [NSString stringWithFormat:@"人脸检测失败,错误码:%ld,错误信息:%@",resultStruct.errorCode,errorMsg];
            NSLog(@"errorMsgInfo-::%@",errorMsgInfo);
        }
    });
}

    func showLiveness() {
        let targetVC = self
        let riskManager = TDMobRiskManager.sharedManager()
        riskManager?.pointee.showLiveness(targetVC) { (resultStruct : TDLivenessResultStruct!)->Void  in
            switch resultStruct.resultType {
            case TDLivenessResultTypeSuccess:
                let bestImageString : String? = String(utf8String: resultStruct.bestImageString)
                if(bestImageString != nil && bestImageString!.count > 0){
                    let data : Data? = Data(base64Encoded: bestImageString!)
                    if(data != nil){
                        let image : UIImage? = UIImage(data: data!)
                        if(image != nil){
                            UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil);
                        }
                    }
                }
                let successMsg = String(cString:resultStruct.errorMsg)
                let seqId = String(cString:resultStruct.seqId)
                print("人脸识别成功!!!,seqId:\(seqId),successMsg:\(successMsg),score:\(resultStruct.score)")
            case TDLivenessResultTypeFailed:
                let errorMsg = String(cString:resultStruct.errorMsg)
                print("人脸识别失败,errorCode:\(resultStruct.errorCode),errorMsg:\(errorMsg)")
            default:
                break
            }
        }
    }

获取SDK版本号

示例代码

// 获取SDK版本号
TDMobRiskManager_t *riskManager = [TDMobRiskManager sharedManager];
NSString *sdkVersion = riskManager->getSDKVersion();
// 获取SDK版本号
let riskManager = TDMobRiskManager.sharedManager()
let sdkVersion = riskManager?.pointee.getSDKVersion()

错误码

代码提示是否计费
200success 成功(真人)
20700No face detected 没有检测到人脸
20702Person change detected 检测到换人
20703Detection timeout 检测超时
20705Screen lock or background exit during detection 检测过程中锁屏或退出后台
20710No camera permission 没有相机权限
20711User actively cancels detection on the preparation page 准备页面用户主动取消检测
20712User actively cancels detection on the detection page 检测页面用户主动取消检测
20749Inconsistent action, tilt head down 动作不一致做出低头动作
60001Network issue, failed to retrieve session 网络问题,无法获取session
60002Network issue, failed to call anti-hack 网络问题,无法调用anti-hack
12202Identified as a blink attack 判断为抠眼攻击
12203Identified as a mouth movement attack 判断为抠嘴攻击
12204Identified as a partial face attack 判断为半张脸攻击
12205Identified as a video replay attack 判断为视频回放攻击
12206Identified as a black and white image 判断为黑白图片
12207Identified as a paper-based attack 判断为纸面攻击
12208Identified as a frame (including paper or phone frame) 判断为边框(包括纸面、手机等边框)
12209Identified as a moire pattern attack 判断为摩尔纹攻击
12210Identified as a face superiority attack 判断为脸优攻击
12211Identified as a paper-based attack (optical flow) 判断为纸面攻击(光流)
12212Identified as a mask attack 判断为面具攻击
12213Identified as an ID card attack 判断为证卡攻击
12214Identified as a 3D mask attack 判断为3D面具攻击
12215Identified as a synthetic image attack 判断为合成图像攻击
12216Identified as a black-market software attack 判断为黑产软件攻击
12217Identified as a T-type mask attack 判断为T型面具攻击
12218Identified as a blurry image 判断为模糊图片
12219Suspected deepfake image attack 疑似深伪图像攻击
12220Suspected high-resolution screen attack 疑似高清屏幕攻击
12222Injection attack 注入攻击
12250Verification error 其他活体攻击
11350Internal error 内部错误

FAQ

Q1:引入终端SDK后,工程无法再进行 Xcode 调试,如何解决?

A1:请参考 SDK初始化 在终端SDK初始化时,加入如下参数

[options setValue:@(YES) forKey:@"allowed"];
options.updateValue(true, forKey: "allowed")