> >

移动端iOS常见问题解答

iOS 设备在使用 H5 时出现文字点选验证位置错位

文字出现错位是由于 iOS 点击了软键盘顶起了页面,输入框失去焦点后不能恢复导致页面 clientY 变化的原因,提供以下方案:

  1. 加长整个页面

  2. H5 中添加如下代码:

    html,body { width: 100%; height: 100%; }

国际化多语言模式,验证页面语言未跟随系统语言变化,或未按照设置的语言变化

  1. 首先检查工程中是否有添加 GT3Captcha.bundle 文件,并确认 GT3Captcha.bundle 中是否包含对应的语言

  2. 检查主工程的 Info.plist 文件中是否有设置 CFBundleLocalizations,并查看是否有添加对应语言的英文简称,如下图所示:

linkerflags

如何进行异步任务方式的集成?

  1. 自定义一个类,该类需要实现 GT3AsyncTaskProtocol 协议

  2. 在该类中实现 GT3AsyncTaskProtocol 协议中的如下两个方法,在第一个方法中,进行自定义的 api1 的请求,拿到对应的 gtchallengesuccess 参数,并通过 completion 回调将参数传给 SDK,在第二个方法中,进行 api2 的验证,并通过 completion 回调将 api2 的验证结果传给 SDK

    - (void)executeRegisterTaskWithCompletion:(void (^)(GT3RegisterParameter * _Nullable, GT3Error * _Nullable))completion {

    }

    - (void)executeValidationTaskWithValidateParam:(GT3ValidationParam *)param completion:(void (^)(BOOL, GT3Error * _Nullable))completion {

    }
  3. 实例化一个 GT3CaptchaManager,并通过 registerCaptchaWithCustomAsyncTask:completion: 方法注册异步任务,最后调用 startGTCaptchaWithAnimated: 方法即可开启验证

详细可参考如下代码:

@interface DemoAyncTask : NSObject <GT3AsyncTaskProtocol>

@property (nonatomic, weak) id<DemoAyncTaskDelegate> delegate;

- (void)executeValidationTaskWithValidateParam:(GT3ValidationParam *)param completion:(void (^)(BOOL, GT3Error * _Nullable))completion;
- (void)executeRegisterTaskWithCompletion:(void (^)(GT3RegisterParameter * _Nullable, GT3Error * _Nullable))completion;

- (void)cancel;

@end

@implementation DemoAyncTask

- (void)executeValidationTaskWithValidateParam:(GT3ValidationParam *)param completion:(void (^)(BOOL, GT3Error * _Nullable))completion {
/**
* TO-DO 处理result数据, 进行二次验证
*/
__block NSMutableArray *postArray = [[NSMutableArray alloc] init];
[param.result enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL * stop) {
NSString *paramItem = [NSString stringWithFormat:@"%@=%@", key, obj];
[postArray addObject:paramItem];
}];

NSString *postForm = [postArray componentsJoinedByString:@"&"];

EnvModel *model = [EnvSettingCenter defaultCenter].currentEnv;

NSDictionary *headerFields = @{@"Content-Type":@"application/x-www-form-urlencoded;charset=UTF-8"};
NSMutableURLRequest *secondaryRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:model.validateAPI] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:15.0];
secondaryRequest.HTTPMethod = @"POST";
secondaryRequest.allHTTPHeaderFields = headerFields;
secondaryRequest.HTTPBody = [postForm dataUsingEncoding:NSUTF8StringEncoding];

NSURLSession *session = [NSURLSession sharedSession];
self.validateDataTask = [session dataTaskWithRequest:secondaryRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (!error && httpResponse.statusCode == 200) {
NSError *err;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&err];
if (!err) {
NSString *status = [dict objectForKey:@"status"];
if ([status isEqualToString:@"success"]) {
NSLog(@"通过业务流程");

if (completion) {
completion(YES, nil);
}

if (_delegate && [_delegate respondsToSelector:@selector(asyncTaskDidFinish:error:)]) {
[_delegate asyncTaskDidFinish:YES error:nil];
}
}
else {
if (completion) {
completion(NO, nil);
}
NSLog(@"无法通过业务流程");

if (_delegate && [_delegate respondsToSelector:@selector(asyncTaskDidFinish:error:)]) {
[_delegate asyncTaskDidFinish:NO error:nil];
}
}
}
else {
GT3Error *anErr = [GT3Error errorWithDomainType:GT3ErrorTypeExtern originalError:err withGTDesciption:@"Error occur."];

if (completion) {
completion(NO, anErr);
}
if (_delegate && [_delegate respondsToSelector:@selector(asyncTaskDidFinish:error:)]) {
[_delegate asyncTaskDidFinish:NO error:anErr];
}
NSLog(@"error:\n%@", err.localizedDescription);
}
}
else {
GT3Error *err = [GT3Error errorWithDomainType:GT3ErrorTypeExtern originalError:error withGTDesciption:@"Error occur."];

if (completion) {
completion(NO, err);
}

if (_delegate && [_delegate respondsToSelector:@selector(asyncTaskDidFinish:error:)]) {
[_delegate asyncTaskDidFinish:NO error:err];
}
NSLog(@"error:\n%@", error.localizedDescription);
}
}];

[self.validateDataTask resume];
}

- (void)executeRegisterTaskWithCompletion:(void (^)(GT3RegisterParameter * _Nullable, GT3Error * _Nullable))completion {
EnvModel *model = [EnvSettingCenter defaultCenter].currentEnv;

/**
* TO-DO 从接口解析是否开启极验3.0, 并解析和配置验证参数
* 不要重复调用, 在交互上需要处理用户的短时间内多次点击的问题
*/
NSString *ts = [NSString stringWithFormat:@"%.0f", [[NSDate date] timeIntervalSince1970] * 1000];
NSURLQueryItem *queryItem = [NSURLQueryItem queryItemWithName:@"t" value:ts];
NSURLComponents *comp = [NSURLComponents componentsWithURL:[NSURL URLWithString:model.registerAPI] resolvingAgainstBaseURL:NO];
comp.queryItems = @[queryItem];
NSURLRequest *request = [NSURLRequest requestWithURL:comp.URL];

NSURLSession *session = [NSURLSession sharedSession];
self.registerDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:(NSJSONReadingOptions)0 error:&error];

if (!error && dict) {
NSString *geetest_id = [dict objectForKey:@"gt"];
NSString *geetest_challenge = [dict objectForKey:@"challenge"];
NSNumber *geetest_success = [dict objectForKey:@"success"];

if (geetest_id && geetest_challenge && geetest_success) {
GT3RegisterParameter *param = [[GT3RegisterParameter alloc] init];
param.gt = geetest_id;
param.challenge = geetest_challenge;
param.success = geetest_success;

if (completion) {
completion(param, nil);
}
}
}
}
else {
GT3Error *err = [GT3Error errorWithDomainType:GT3ErrorTypeExtern originalError:error withGTDesciption:@"Error occur."];

if (completion) {
completion(nil, err);
}
NSLog(@"error:\n%@", error.localizedDescription);
}
}];

[self.registerDataTask resume];
}

- (void)cancel {
if (self.registerDataTask) {
[self.registerDataTask cancel];
}

if (self.validateDataTask) {
[self.validateDataTask cancel];
}
}

@end

- (GT3CaptchaManager *)manager {
if (!_manager) {
BOOL debugable = [EnvSettingCenter defaultCenter].debugEnable;
BOOL httpsEnable = [EnvSettingCenter defaultCenter].httpsEnable;
BOOL naEnable = [EnvSettingCenter defaultCenter].naNodeEnable;

GT3LanguageType langType = [EnvSettingCenter defaultCenter].langType;
EnvModel *model = [EnvSettingCenter defaultCenter].currentEnv;
// 占位即可
_manager = [[GT3CaptchaManager alloc] initWithAPI1:model.registerAPI API2:model.validateAPI timeout:5.0];
_manager.delegate = self;
_manager.viewDelegate = self;

[_manager enableDebugMode:debugable];
[_manager disableSecurityAuthentication:!httpsEnable];
[_manager useLanguage:langType];
if (naEnable) [_manager useServiceNode:GT3CaptchaServiceNodeNA];
[_manager useVisualViewWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
// _manager.maskColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.4];
}
return _manager;
}

- (void)_init {
DemoAyncTask *asyncTask = [[DemoAyncTask alloc] init];
asyncTask.delegate = self;
[self.manager registerCaptchaWithCustomAsyncTask:asyncTask completion:nil];

/// asyncTask 被管理器内部以弱引用方式持有,
/// 所以需要开发者自己在调用类中保持,以保证管理器在后续流程中能正常访问到该对象
self.asyncTask = asyncTask;
}

- (void)startCaptcha {
[self.manager startGTCaptchaWithAnimated:YES];
}

- (void)stopCaptcha {
[self.manager stopGTCaptcha];
}

如何查看 SDK 版本?

找到 GT3Captcha.framework 文件,选中 -> 双击进入目录 -> 找到 Info.plist -> 双击打开,找到 Bundle version string (short) 对应的值,即为 SDK 的版本,如下图所示:

linkerflags

如何获取二次验证的参数?

  1. 异步任务集成时,二次验证的参数在 - (void)executeValidationTaskWithValidateParam:(GT3ValidationParam *)param completion:(void (^)(BOOL, GT3Error * _Nullable))completion 回调方法中的 param 参数的 result 中,param.result,如下图所示:

    linkerflags

  2. 非异步任务集成时,二次验证的参数在 - (void)gtCaptcha:(GT3CaptchaManager *)manager didReceiveCaptchaCode:(NSString *)code result:(NSDictionary *)result message:(NSString *)message 回调方法的 result 中,如下图所示:

    linkerflags

如何排查 3840 及 101603 错误?

出现这两个错误码,一般是 api1 请求返回的数据格式有问题,请检查 api1 接口返回的数据格式:

  1. 返回的数据不是 json 格式

  2. 返回的 json 格式与极验官方要求的不一致

  3. 正确的 json 格式为下面两种:

    {
    "challenge": "90977bef1d2c8a8efaea1e41b1d7b19b",
    "gt": "4faf9dd37eb683586977947ce87a866a",
    "new_captcha": 1,
    "success": 1
    }

    {"data":
    {
    "challenge": "90977bef1d2c8a8efaea1e41b1d7b19b",
    "gt": "4faf9dd37eb683586977947ce87a866a",
    "new_captcha": 1,
    "success": 1
    }
    }

registerCaptcha: 在什么时候回调

  • 回调仅在首次注册成功后回调,且根据验证状态,并不一定每次回调
  • 流程和方法之间的关系见下图

work sequence

gtCaptcha:didReceiveCaptchaCode:result:message: 中 code 返回 0,建议作何处理?

只要终端用户没有完成验证,返回就是0;只有完成通过,才会返回 code = 1 以及相应的二次校验

result 数据。code = 0 的具体场景,比如在滑动验证的时候,没有滑动正确。当 code = 0 时,验证码会自动刷新,终端用户充实即可。并且重试有最大的重试次数,根据验证类型不同,最大重试次数默认是 5-6 次。

Xcode 编译报 GT3Captcha 相关的符号无法找到(symbol not found)

Xcode 编译错误提示 symbol not found,说明相关的符号无法被链接器访问到。排查方式:先确认缺失库的名称,再确认相关的库是否正确导入(手动导入时勾选 Copy items if need 等)。否则可以尝试重启 Xcode 或者系统。

Xcode 编译报符号重复(Duplicated symbol)

如果 Xcode 提示 Duplicated symbol,说明有符号冲突(代码冲突)。排查方式:确实冲突的符号所属的库,在确认不同的库怎么被同时引入,并反馈给极验。

Xcode 编译报 No such module ‘GT3Captcha’

请检查 GT3Captcha.framework 是否正确被导入工程,且 Xcode 的 Build Setting 中的 Search Path 进行了正确的配置。

验证 3.0 是否有 Flutter 2.0 支持示例

极验官方已提供官方 Flutter 插件:

在工程 pubspec.yamldependencies 块中添加下列配置

Github 集成

dependencies:
gt3_flutter_plugin:
git:
url: https://github.com/GeeTeam/gt3_flutter_plugin.git
ref: master

pub 集成

dependencies:
gt3_flutter_plugin: ^0.0.6

Github:gt3_flutter_plugin

验证 3.0 NA 亚马逊节点、NG 谷歌云节点的使用说明

  • NG 节点已经销毁,已不再使用
  • 使用海外接口需要跟极验技术沟通并进行报备
  • 海外终端用户并不是必须使用海外节点,也可以访问国内节点
  • 当国内终端用户较多的时候建议,还是以国内节点为主
  • 使用海外节点必须配套使用海外集群的 register 接口,同时在 Android 和 iOS 的 SDK 中设置 serviceNode 为相应节点

验证 3.0 加载耗时较长或报错,这个该如何处理?

  • 网络请求时间较长,但能成功:可能当前网络质量不好,建议更换网络重试。
  • 网络请求超时而失败:建议检查超时的请求。如果是极验的接口,检查一下终端用户所在网络到极验接口的可达性。简单的自检工具可以访问 http://webtest.geetest.com。
  • 网络相关的报错:请查看相应产品各个端对应的错误码清单,进行排查和处理。如果在国内区域性的报SSL证书错误,可能是遇到了区域性的 DNS 劫持。

项目目标最低版本在 9.0 以下时,使用 Xcode 14.3 运行报链接不到 ARC 库的错

原因是 Xcode 14.3 移除了 ARC 相关的库,iOS 9.0 及以上系统内置了ARC 库,所以有两种解决方案:

  1. 将项目目标最低版本设置为 9.0 或以上
  2. 将在 14.3 以下的 Xcode 版本中的 ARC 库拷贝到 Xcode 14.3 版本对应路径上