移动端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
    }
    }