iOS客户端接入

本文为您介绍iOS客户端融合认证的集成方法及接口的功能示例。

下载SDK

登录号码认证产品控制台,在概览页面的右侧API&SDK区域,单击立即下载,进入API&SDK页面,根据页面提示下载并解压对应SDK。

创建认证方案

您导入项目或调用API接口时,会用到方案Code等参数信息,请先在号码认证产品控制台创建认证方案,获取方案Code等参数信息。

开始集成

说明
  • 动态库不一定包含模拟器架构,可使用lipo -info指令查询动态库支持的模拟器架构。若因包含模拟器架构而导致编译报错,使用lipo -thin指令剔除可执行文件中的模拟器架构。

  • 解压包内含AlicomFusionAuth.framework.dSYM符号文件,集成时请勿将动态库解压文件直接拖入工程中。该文件是线上crash符号翻译专用,如果放入工程中会影响App上架审核。

  • 融合认证默认提供的SDK为动态库,路径为\SDK\AlicomFusionAuthSDK\iphoneos\AlicomFusionAuth.framework

  • 若需模拟器运行,可以集成\SDK\AlicomFusionAuthSDK\iphoneos+iphonesimulator\AlicomFusionAuth.framework

    请注意:该SDK新增arm64+x86支持,只能用于调试,可以解决模拟器无法编译的问题,提交AppStore建议使用iphoneos库,因为动态库包含x86,有被拒的风险。

  1. 创建工程。

    下载并安装Xcode,点击Create a new Xcode project,根据页面引导创建一个新工程。

  2. 添加主库和Network系统依赖库。

    在菜单栏选择Targets > General > Frameworks,Libraries, and Embedded Content,添加主库AlicomFusionAuth.framework和系统依赖库Network.framework

    说明
    • 请将AlicomFusionAuth.frameworkEmbed设置为Embed & Sign。

    • 请将Network.frameworkEmbed设置为Do Not Embed

    image

  3. BuildSettings设置。

    在菜单栏选择TARGETS > BuildSettings > Other Linker Flags,Other Linker Flags增加-ObjC。image

  4. 添加ATAuthSDK.bundle资源文件。

    添加SDK文件夹中的ATAuthSDK.bundle资源文件到您的工程中,否则授权页面默认的图片或icon不显示。image

  5. 可选:友盟功能组件。

    若您之前已集成友盟基础组件,则忽略此步骤,调用相关接口关闭融合认证关联的友盟插件,即可使用您现有友盟组件。

    若您还未集成友盟组件,请将SDK文件夹的UMCommon.frameworkUMDevice.framework集成到您的工程中,Embed设置为Do Not Embed。image

鉴权

SDK在启动时需要接入方传入鉴权Token,用于合法性校验。

  • Token鉴权成功,通过onSDKTokenAuthSuccess():回调。

  • Token鉴权失败,通过onSDKTokenAuthFailure(AlicomFusionAuthToken token, AlicomFusionEvent alicomFusionEvent)回调。

更新

SDK在运行时若Token过期,请通过onSDKTokenUpdate()更新Token。

使用场景

/* 默认登录注册场景 */
 public static final String ALICOMFUSIONAUTHTEMPLATED_10001 = "100001";
 /* 默认更换手机号场景 */
 public static final String ALICOMFUSIONAUTHTEMPLATED_10002 = "100002";
 /* 默认重置密码场景 */
 public static final String ALICOMFUSIONAUTHTEMPLATED_10003 = "100003";
 /* 默认绑定新手机号场景 */
 public static final String ALICOMFUSIONAUTHTEMPLATED_10004 = "100004";
 /* 默认验证绑定手机号场景 */
 public static final String ALICOMFUSIONAUTHTEMPLATED_10005 = "100005";

类别

说明

开始场景

调用startSceneWithTemplateId(Context mContext,String templateId)startSceneWithTemplateId(Context mContext,String templateId,AlicomFusionAuthUICallBack callback)

说明

调用该接口的前提是没有正在执行的场景。

  • mContext:上下文。

  • callback:为可选参数,用于自定义UI时使用,如果默认使用内置UI,该参数可不传。

  • templateId:场景唯一标识与控制台场景ID唯一对应,用于决策启动哪个场景。

继续场景

调用continueSceneWithTemplateId(String templateId,boolean isSuccess)

说明

调用该接口的前提是有正在执行的场景。

  • templateId:场景唯一标识与控制台场景ID唯一对应,用于决策启动哪个场景。

  • isSuccess:当前场景验证是否成功,是否成功决定继续场景后的业务流程走向。

结束场景

调用stopSceneWithTemplateId(String templateId)

templateId:场景唯一标识与控制台场景ID唯一对应,用于决策结束哪个场景。

获取Token成功

SDK执行到一个节点后或通过onVerifySuccess(String token,String nodeName,AlicomFusionEvent event)回调,对接入方返回节点换号Token,接入方获取到该Token后通过接口换取号码。

中途获取Token成功

中途获取Token成功的回调,在AlicomFusionTemplateId_100002场景下验证当前手机号节点触发,回调onHalfWayVerifySuccess(String nodeName, String maskToken,AlicomFusionEvent event,HalfWayVerifyResult verifyResult)

在接入方验证完成当前手机号后,通过verifyResult告知SDK验证结果,SDK将根据该结果执行之后的流程。

获取Token失败

如果SDK在某个节点获取Token失败,通过onVerifyFailed(AlicomFusionEvent error,String nodeName)接入方在收到该回调后,可以用continueSceneWithTemplateId(String templateId,boolean isSuccess)接口选择下一节点的验证,或通过stopSceneWithTemplateId(String templateId)结束当前场景,采用其他方案进行验证。

场景结束

回调onTemplateFinish(AlicomFusionEvent event),结束场景内的所有节点。

接入方调用stopSceneWithTemplateId(String templateId)结束当前场景。

交互流程详解

完整的功能交互流程请参见融合认证的交互流程

SDK方法说明

创建实例

/**
 *  初始化,传入鉴权Token
 *  @note 由App维护Token的生命周期,可以提升SDK中Token处理效率,快速拉起场景页面
 *  @param token 鉴权Token
 *  @param schemeCode 方案号
 */
- (instancetype)initWithToken:(AlicomFusionAuthToken *)token schemeCode:(NSString *)schemeCode;

使用示例

AlicomFusionAuthToken *token = [[AlicomFusionAuthToken alloc] initWithTokenStr:tokenStr];
self.handler = [[AlicomFusionAuthHandler alloc] initWithToken:token schemeCode:@"xxx"];
说明

Token需要从服务端获取。

  • Token鉴权成功,通过onSDKTokenAuthSuccess回调,场景操作必须在回调成功后进行;Token鉴权失败,通过onSDKTokenAuthFailure回调。

  • Token过期前五分钟,通过onSDKTokenUpdate回调获取新的Token,⽤户必须实现此回调获取新的Token。

设置流程监控回调

/**
 *  设置回调
 *  @param delegate SDK核心回调
 */
- (void)setFusionAuthDelegate:(id<AlicomFusionAuthDelegate>)delegate;

使用示例

[self.handler setFusionAuthDelegate:self];

开始场景

使用默认UI

  • 该接口使用的是默认UI,接入方无法修改UI。

  • 需要Token鉴权成功后才可使用。

  • 没有其他场景正在执行方可调用,否则将报100003错误。

  • templateId请参考使用场景。

/**
 *  开始拉起场景,使用SDK内置UI
 *  @param templateId 场景唯一标识与控制台场景ID唯一对应
 *  @param controller 基础页面,SDK将从该页面弹出融合认证相关页面
 */
- (void)startSceneWithTemplateId:(NSString *)templateId viewController:(UIViewController *)controller;

使用示例

[self.handler startSceneUIWithTemplateId:AlicomFusionTemplateId_100001 viewController:controller];

使用自定义UI

  • 根据实际场景可以通过UI回调接口修改相关UI。

    • 一键登录onPhoneNumberVerifyUICustomDefined:templateId:nodeId:UIModel:

    • 短信验证码onSMSCodeVerifyUICustomDefined:templateId:nodeId:isAutoInput:view:

    • 用户主动发短信onSMSSendVerifyUICustomDefined:templateId:nodeId:smsContent:receiveNum:view:

    • 导航栏:onNavigationControllerCustomDefined:templateId:nodeId:navigation:

  • 需要Token鉴权后才可使用。

  • templateId请参考使用场景。

/**
 *  开始拉起场景,使用APP自定义UI
 *  @param templateId 场景唯一标识与控制台场景ID唯一对应
 *  @param controller 基础页面,SDK将从该页面弹出融合认证相关UI界面
 *  @param delegate UI回调,自定义UI需通过该回调实现
 */
- (void)startSceneUIWithTemplateId:(NSString *)templateId
                    viewController:(UIViewController *)controller
                          delegate:(id<AlicomFusionAuthUIDelegate> _Nullable)delegate;

使用示例

[self.handler startSceneUIWithTemplateId:AlicomFusionTemplateId_100001 viewController:controller delegate:self];

继续场景

  • 用于场景中断后恢复流程,如获取到号码校验Token后,假设服务端效验失败,可以通过该接口继续进行场景流程。

  • isSuccess的作用在于当前的认证是否成功,决定SDK的认证走向,如SDK透出Token后,进行取号操作失败,如需下一步鉴权,可通过该参数控制走向。

  • 需要在start后才能使用。

  • templateId请参考使用场景。

/**
 *  继续场景
 *  @param templateId 场景唯一标识与控制台场景ID唯一对应
 *  @param isSuccess 当前场景验证是否成功,成功与否决定继续场景后的业务流程走向
 *  @note 该接口用于场景中断后恢复流程,如获取到号码效验token后,假设服务端效验失败,可以通过该接口继续进行场景流程
 */
- (void)continueSceneWithTemplateId:(NSString *)templateId isSuccess:(BOOL)isSuccess;

使用示例

[self.handler continueSceneWithTemplateId:AlicomFusionTemplateId_100001 isSuccess:NO];

结束场景

  • 当需要结束场景时调用该接口,对应的场景将不再执行。

  • 需要在start后才能使用。

  • templateId请参考使用场景。

/**
 *  结束场景
 *  @param templateId 场景唯一标识与控制台场景ID唯一对应
 *  @note 随时调用,随时停止,与start接口对应,结束后才可以start下一次场景,不可同时start多个场景
 */
- (void)stopSceneWithTemplateId:(NSString *)templateId;

使用示例

[self.handler stopSceneWithTemplateId:AlicomFusionTemplateId_100001];

其他接口说明

友盟组件依赖设置

用户如果需要使用友盟组件的端风险检测及手机号评分功能,需要配置aar后调用下面的方法决定友盟组件的初始化位置。

说明

如果用户只是简单的调用此方法而不引入aar则设置无效,因为此功能属于插件式依赖,不属于强制性依赖。

/**
 *  设置是否使用SDK关联友盟统计组件
 *  @note SDK默认使用融合认证关联友盟组件,如果您当前APP未集成友盟组件,根据SDK集成文档接入各个SDK后,无需再关心此接口设置
 *  @note 如果您当前已经集成过友盟组件,则无需重复集成,使用此接口关闭内置链接,并确保使用本SDK前已经完成友盟组件初始化
 *  @param isUseSupply YES:使用SDK默认关联友盟组件。NO:不使用SDK关联友盟组件,即使用APP原有友盟组件,默认YES
 */

+ (void)useSDKSupplyUMSDK:(BOOL)isUseSupply;

使用示例

[AlicomFusionAuthHandler useSDKSupplyUMSDK:YES];

销毁服务

销毁服务后需要重新创建对象。

/**
 *  销毁服务
 *  @note 销毁服务后,SDK内部各个模块同步销毁,若想继续使用SDK,请重新初始化
 */
- (void)destroy;

使用示例

[self.handler destroy];
self.handler = nil;

主动更新Token

此方法为用户主动更新Token,Token相关鉴权及更新逻辑参考initWithToken中的Token逻辑。

/**
 *  主动更新鉴权Token
 *  @param Token 鉴权Token
 *  @note 鉴权Token具有时效性,App可再Token即将过期时,主动向SDK更新token
 *  @note 非必要接口:SDK内部存在Token的过期监控,过期前会通过AlicomFusionAuthDelegate回调通知APP,APP可不感知此项逻辑
 */
- (void)updateToken:(AlicomFusionAuthToken *)token;

使用示例

AlicomFusionAuthToken *token = [[AlicomFusionAuthToken alloc] initWithTokenStr:tokenStr];
[self.handler updateToken:token];

获取版本号

/**
 *  获取SDK版本号
 *  @return 版本号
 */
+ (NSString *)getSDKVersion;

使用示例

NSString *version = [AlicomFusionAuthHandler getSDKVersion];

获取场景ID

/**
 *  获取当前正在运行中的场景ID
 *  @return templateId 场景唯一标识 与控制台场景ID唯一对应
 */
- (NSString *)getCurrentTemplateId;

使用示例

NSString *templateId = [self.handler getCurrentTemplateId];

日志功能

//是否打印log,默认开启
+ (void)logEnable:(BOOL)enable;

使用示例

[AlicomFusionLog logEnable:YES];

UI接口说明

⼀键登录自定义UI参数

typedef NS_ENUM(NSUInteger, AlicomFusionPresentationDirection){
    AlicomFusionPresentationDirectionBottom = 0,
    AlicomFusionPresentationDirectionRight,
    AlicomFusionPresentationDirectionTop,
    AlicomFusionPresentationDirectionLeft,
};

/**
 *  构建控件的frame,view布局时会调用该block得到控件的frame
 *  @param  screenSize 屏幕的size,可以通过该size来判断是横屏还是竖屏
 *  @param  superViewSize 该控件的super view的size,可以通过该size,辅助该控件重新布局
 *  @param  frame 控件默认的位置
 *  @return 控件新设置的位置
 */
typedef CGRect(^AlicomFusionBuildFrameBlock)(CGSize screenSize, CGSize superViewSize, CGRect frame);

/* 一键登录自定义UI */
@interface AlicomFusionNumberAuthModel : NSObject

/// 如果将otherLoginButton重新指向自己定义的button,其他手机号登录,如果想按照编排流程走,需要点击按钮之后调用该方法
- (void)otherPhoneLogin;

/// 标题
@property (nonatomic, strong) UILabel * _Nullable nameLabel;

/// 切换手机号登录,如果该按钮移除,自己实现了一个按钮,要想继续执行SDK的内部逻辑,按钮的点击需要调用otherPhoneLogin方法实现原有逻辑
@property (nonatomic, strong) UIButton * _Nullable otherLoginButton;

/// 更多登录方式点击回调
@property (nonatomic, copy) void(^moreLoginActionBlock)(void);

/** 构建导一键登录页面“登录”文字的布局,不实现则按默认处理 */
@property (nonatomic, copy) AlicomFusionBuildFrameBlock nameLabelFrameBlock;
/** 构建导一键登录页面“其他手机号登录”按钮的布局,不实现则按默认处理 */
@property (nonatomic, copy) AlicomFusionBuildFrameBlock otherLoginButtonFrameBlock;

/**
 * 说明,可设置的Y轴距离,waring: 以下所有关于Y轴的设置<=0都将不生效,请注意
 * 全屏模式:默认是以375x667pt为基准,其他屏幕尺寸可以根据(ratio = 屏幕高度/667)比率来适配,比如 Y*ratio
 */

#pragma mark- 全屏、弹窗模式设置
/**
 *  授权页面中,渲染并显示所有控件的view,称content view,不实现该block默认为全屏模式
 *  实现弹窗的方案 x >= 0 || y >= 0 width <= 屏幕宽度 || height <= 屏幕高度
 */
@property (nonatomic, copy) AlicomFusionBuildFrameBlock contentViewFrameBlock;

#pragma mark- 竖屏、横屏模式设置
/** 屏幕是否支持旋转方向,默认UIInterfaceOrientationMaskPortrait,注意:在刘海屏,UIInterfaceOrientationMaskPortraitUpsideDown属性慎用! */
@property (nonatomic, assign) UIInterfaceOrientationMask supportedInterfaceOrientations;

#pragma mark- 导航栏(只对全屏模式有效)
/**授权页显示中,导航栏是否隐藏,默认NO*/
@property (nonatomic, assign) BOOL navIsHidden;
/**授权页push到其他页面后,导航栏是否隐藏,默认NO*/
@property (nonatomic, assign) BOOL navIsHiddenAfterLoginVCDisappear;
/**是否需要中断返回,如果设置为YES,则点击左上角返回按钮的时候默认页面不消失,同时透出状态码700010,需要自己调用TXCommonHandler cancelLoginVCAnimated方法隐藏页面,默认为NO*/
@property (nonatomic, assign) BOOL suspendDisMissVC;
/** 导航栏主题色 */
@property (nonatomic, strong) UIColor *navColor;
/** 导航栏标题,内容、字体、大小、颜色 */
@property (nonatomic, copy) NSAttributedString *navTitle;
/** 导航栏返回图片 */
@property (nonatomic, strong) UIImage *navBackImage;
/** 是否隐藏授权页导航栏返回按钮,默认不隐藏 */
@property (nonatomic, assign) BOOL hideNavBackItem;
/** 导航栏右侧自定义控件,可以在创建该VIEW的时候添加手势操作,或者创建按钮或其他赋值给VIEW */
@property (nonatomic, strong) UIView *navMoreView;

/** 构建导航栏返回按钮的frame,view布局或布局发生变化时调用,不实现则按默认处理 */
@property (nonatomic, copy) AlicomFusionBuildFrameBlock navBackButtonFrameBlock;
/** 构建导航栏标题的frame,view布局或布局发生变化时调用,不实现则按默认处理 */
@property (nonatomic, copy) AlicomFusionBuildFrameBlock navTitleFrameBlock;
/** 构建导航栏右侧more view的frame,view布局或布局发生变化时调用,不实现则按默认处理,边界 CGRectGetMinX(frame) >= (superViewSizeViewSize / 0.3) && CGRectGetWidth(frame) <= (superViewSize.width / 3.0) */
@property (nonatomic, copy) AlicomFusionBuildFrameBlock navMoreViewFrameBlock;

#pragma mark- 全屏、弹窗模式共同属性

#pragma mark- 授权页动画相关
/** 授权页弹出方向,默认AlicomFusionPresentationDirectionBottom,该属性只对自带动画起效,不影响自定义动画 */
@property (nonatomic, assign) AlicomFusionPresentationDirection presentDirection;
/** 授权页显示和消失动画时间,默认为0.25s,<= 0 时关闭动画,该属性只对自带动画起效,不影响自定义动画 **/
@property (nonatomic, assign) CGFloat animationDuration;

/** 授权页显示动画(弹窗 & 全屏),不设置或设置为nil默认使用自带动画,SDK内部会主动更改动画的一些属性(包括:removedOnCompletion = NO、fillMode = kCAFillModeForwards 及 delegate) **/
@property (nonatomic, strong, nullable) CAAnimation *entryAnimation;
/** 授权页消失动画(弹窗 & 全屏),不设置或设置为nil默认使用自带动画,SDK内部会主动更改动画的一些属性(包括:removedOnCompletion = NO、fillMode = kCAFillModeForwards 及 delegate) **/
@property (nonatomic, strong, nullable) CAAnimation *exitAnimation;

/** 授权页显示时的背景动画(仅弹窗),不设置或设置为nil默认使用自带动画,SDK内部会主动更改动画的一些属性(包括:removedOnCompletion = NO、fillMode = kCAFillModeForwards 及 delegate) **/
@property (nonatomic, strong, nullable) CAAnimation *bgEntryAnimation;
/** 授权页消失时的背景动画(仅弹窗),不设置或设置为nil默认使用自带动画,SDK内部会主动更改动画的一些属性(包括:removedOnCompletion = NO、fillMode = kCAFillModeForwards 及 delegate) **/
@property (nonatomic, strong, nullable) CAAnimation *bgExitAnimation;

#pragma mark- 状态栏
/** 状态栏是否隐藏,默认NO */
@property (nonatomic, assign) BOOL prefersStatusBarHidden;
/** 状态栏主题风格,默认UIStatusBarStyleDefault */
@property (nonatomic, assign) UIStatusBarStyle preferredStatusBarStyle;

#pragma mark- 背景
/** 授权页背景色 */
@property (nonatomic, strong) UIColor *backgroundColor;
/** 授权页背景图片 */
@property (nonatomic, strong) UIImage *backgroundImage;
/** 授权页背景图片view的 content mode,默认为 UIViewContentModeScaleAspectFill */
@property (nonatomic, assign) UIViewContentMode backgroundImageContentMode;

#pragma mark- logo图片
/** logo图片设置 */
@property (nonatomic, strong) UIImage *logoImage;
/** logo是否隐藏,默认NO */
@property (nonatomic, assign) BOOL logoIsHidden;

/** 构建logo的frame,view布局或布局发生变化时调用,不实现则按默认处理 */
@property (nonatomic, copy) AlicomFusionBuildFrameBlock logoFrameBlock;

#pragma mark- slogan
/** slogan文案,内容、字体、大小、颜色 */
@property (nonatomic, copy) NSAttributedString *sloganText;
/** slogan是否隐藏,默认NO */
@property (nonatomic, assign) BOOL sloganIsHidden;

/** 构建slogan的frame,view布局或布局发生变化时调用,不实现则按默认处理 */
@property (nonatomic, copy) AlicomFusionBuildFrameBlock sloganFrameBlock;

#pragma mark- 号码
/** 号码颜色设置 */
@property (nonatomic, strong) UIColor *numberColor;
/** 号码字体大小设置,大小小于16则不生效 */
@property (nonatomic, strong) UIFont *numberFont;

/**
 *  构建号码的frame,view布局或布局发生变化时调用,只有x、y生效,不实现则按默认处理,
 *  注:设置不能超出父视图 content view
 */
@property (nonatomic, copy) AlicomFusionBuildFrameBlock numberFrameBlock;

#pragma mark- 登录
/** 登录按钮文案,内容、字体、大小、颜色*/
@property (nonatomic, strong) NSAttributedString *loginBtnText;
/** 登录按钮背景图片组,默认高度50.0pt,@[激活状态的图片,失效状态的图片,高亮状态的图片] */
@property (nonatomic, strong) NSArray<UIImage *> *loginBtnBgImgs;
/**
 *  是否自动隐藏点击登录按钮之后授权页上转圈的 loading, 默认为YES,在获取登录Token成功后自动隐藏
 *  如果设置为 NO,需要自己手动调用 [[TXCommonHandler sharedInstance] hideLoginLoading] 隐藏
 */
@property (nonatomic, assign) BOOL autoHideLoginLoading;
/**
 *  构建登录按钮的frame,view布局或布局发生变化时调用,不实现则按默认处理
 *  注:不能超出父视图 content view,height不能小于20,width不能小于父视图宽度的一半
 */
@property (nonatomic, copy) AlicomFusionBuildFrameBlock loginBtnFrameBlock;

#pragma mark- 协议
/** checkBox图片组,[uncheckedImg,checkedImg]*/
@property (nonatomic, copy) NSArray<UIImage *> *checkBoxImages;
/** checkBox图片距离控件边框的填充,默认为 UIEdgeInsetsZero,确保控件大小减去内填充大小为资源图片大小情况下,图片才不会变形 **/
@property (nonatomic, assign) UIEdgeInsets checkBoxImageEdgeInsets;
/** checkBox是否勾选,默认NO */
@property (nonatomic, assign) BOOL checkBoxIsChecked;
/** checkBox是否隐藏,默认NO */
@property (nonatomic, assign) BOOL checkBoxIsHidden;
/** checkBox大小,高宽一样,必须大于0 */
@property (nonatomic, assign) CGFloat checkBoxWH;

/** 协议1,[协议名称,协议Url],注:三个协议名称不能相同 */
@property (nonatomic, copy) NSArray<NSString *> *privacyOne;
/** 协议2,[协议名称,协议Url],注:三个协议名称不能相同 */
@property (nonatomic, copy) NSArray<NSString *> *privacyTwo;
/** 协议3,[协议名称,协议Url],注:三个协议名称不能相同 */
@property (nonatomic, copy) NSArray<NSString *> *privacyThree;
/** 协议名称之间连接字符串数组,默认 ["和","、","、"] ,即第一个为"和",其他为"、",按顺序读取,为空则取默认 */
@property (nonatomic, copy) NSArray<NSString *> *privacyConectTexts;
/** 协议内容颜色数组,[非点击文案颜色,点击文案颜色] */
@property (nonatomic, copy) NSArray<UIColor *> *privacyColors;
/** 协议文案支持居中、居左、居右设置,默认居左 */
@property (nonatomic, assign) NSTextAlignment privacyAlignment;
/** 协议整体文案,前缀部分文案 */
@property (nonatomic, copy) NSString *privacyPreText;
/** 协议整体文案,后缀部分文案 */
@property (nonatomic, copy) NSString *privacySufText;
/** 运营商协议名称前缀文案,仅支持 <([《(【『 */
@property (nonatomic, copy) NSString *privacyOperatorPreText;
/** 运营商协议名称后缀文案,仅支持 >)]》)】』*/
@property (nonatomic, copy) NSString *privacyOperatorSufText;
/** 运营商协议指定显示顺序,默认0,即第1个协议显示,最大值可为3,即第4个协议显示*/
@property (nonatomic, assign) NSInteger privacyOperatorIndex;
/** 协议整体文案字体大小,小于12.0不生效 */
@property (nonatomic, strong) UIFont *privacyFont;
/** checkBox是否扩大按钮可交互范围至"协议前缀部分文案(默认:我已阅读并同意)"区域,默认NO */
@property (nonatomic, assign) BOOL expandAuthPageCheckedScope;

/**
 *  构建协议整体(包括checkBox)的frame,view布局或布局发生变化时调用,不实现则按默认处理
 *  如果设置的width小于checkBox的宽则不生效,最小x、y为0,最大width、height为父试图宽高
 *  最终会根据设置进来的width对协议文本进行自适应,得到的size是协议控件的最终大小
 */
@property (nonatomic, copy) AlicomFusionBuildFrameBlock privacyFrameBlock;
/**
 *  未同意协议时点击登录按钮,协议整体文案的动画效果,不设置或设置为nil默认没有动画,SDK内部会主动更改动画的一些属性(包括:removedOnCompletion = NO、fillMode = kCAFillModeRemoved 及 delegate)
 */
@property (nonatomic, strong, nullable) CAAnimation *privacyAnimation;

#pragma mark- 切换到其他方式
/** changeBtn标题,内容、字体、大小、颜色 */
@property (nonatomic, copy) NSAttributedString *changeBtnTitle;
/** changeBtn是否隐藏,默认NO*/
@property (nonatomic, assign) BOOL changeBtnIsHidden;

/** 构建changeBtn的frame,view布局或布局发生变化时调用,不实现则按默认处理 */
@property (nonatomic, copy) AlicomFusionBuildFrameBlock changeBtnFrameBlock;

#pragma mark- 协议详情页
/** 导航栏背景颜色设置 */
@property (nonatomic, strong) UIColor *privacyNavColor;
/** 导航栏标题字体、大小 */
@property (nonatomic, strong) UIFont *privacyNavTitleFont;
/** 导航栏标题颜色 */
@property (nonatomic, strong) UIColor *privacyNavTitleColor;
/** 导航栏返回图片 */
@property (nonatomic, strong) UIImage *privacyNavBackImage;

#pragma mark- 其他自定义控件添加及布局

/**
 * 自定义控件添加,注意:自定义视图的创建初始化和添加到父视图,都需要在主线程!!
 * @param  superCustomView 父视图
*/
@property (nonatomic, copy) void(^customViewBlock)(UIView *superCustomView);

/**
 *  每次授权页布局完成时会调用该block,可以在该block实现里面可设置自定义添加控件的frame
 *  @param  screenSize 屏幕的size
 *  @param  contentViewFrame content view的frame,
 *  @param  navFrame 导航栏的frame,仅全屏时有效
 *  @param  titleBarFrame 标题栏的frame,仅弹窗时有效
 *  @param  logoFrame logo图片的frame
 *  @param  sloganFrame slogan的frame
 *  @param  numberFrame 号码栏的frame
 *  @param  loginFrame 登录按钮的frame
 *  @param  changeBtnFrame 切换到其他方式按钮的frame
 *  @param  privacyFrame 协议整体(包括checkBox)的frame
*/
@property (nonatomic, copy) void(^customViewLayoutBlock)(CGSize screenSize, CGRect contentViewFrame,CGRect nameLabelFrame,CGRect otherLoginBtnFrame,CGRect navFrame, CGRect titleBarFrame, CGRect logoFrame, CGRect sloganFrame, CGRect numberFrame, CGRect loginFrame, CGRect changeBtnFrame, CGRect privacyFrame);

#pragma mark - 二次隐私协议弹窗设置
/** 二次隐私协议弹窗是否需要显示, 默认NO */
@property (nonatomic, assign) BOOL privacyAlertIsNeedShow;
/** 二次隐私协议弹窗点击按钮是否需要执行登录,默认YES */
@property (nonatomic, assign) BOOL privacyAlertIsNeedAutoLogin;
/** 二次隐私协议弹窗显示自定义动画,默认从下往上位移动画 */
@property (nonatomic, strong, nullable) CAAnimation *privacyAlertEntryAnimation;
/** 二次隐私协议弹窗隐藏自定义动画,默认从上往下位移动画 */
@property (nonatomic, strong, nullable) CAAnimation *privacyAlertExitAnimation;
/** 二次隐私协议弹窗的四个圆角值,顺序为左上,左下,右下,右上,需要填充4个值,不足4个值则无效,如果值<=0则为直角 ,默认0*/
@property (nonatomic, copy) NSArray<NSNumber *> *privacyAlertCornerRadiusArray;
/** 二次隐私协议弹窗背景颜色,默认为白色 */
@property (nonatomic, strong) UIColor *privacyAlertBackgroundColor;
/** 二次隐私协议弹窗透明度,默认不透明1.0 ,设置范围0.3~1.0之间 */
@property (nonatomic, assign) CGFloat privacyAlertAlpha;
/** 二次隐私协议弹窗标题文字大小,最小12,默认12 */
@property (nonatomic, strong) UIFont *privacyAlertTitleFont;
/** 二次隐私协议弹窗标题文字颜色,默认黑色 */
@property (nonatomic, strong) UIColor *privacyAlertTitleColor;
/** 二次隐私协议弹窗标题背景颜色,默认白色*/
@property (nonatomic, strong) UIColor *privacyAlertTitleBackgroundColor;
/** 二次隐私协议弹窗标题位置,默认居中*/
@property (nonatomic, assign) NSTextAlignment privacyAlertTitleAlignment;
/** 二次隐私协议弹窗协议内容文字大小,最小12,默认12 */
@property (nonatomic, strong) UIFont *privacyAlertContentFont;
/** 二次隐私协议弹窗协议内容背景颜色,默认白色 */
@property (nonatomic, strong) UIColor *privacyAlertContentBackgroundColor;
/** 二次隐私协议弹窗协议内容颜色数组,[非点击文案颜色,点击文案颜色],默认[0x999999,0x1890FF] */
@property (nonatomic, copy) NSArray<UIColor *> *privacyAlertContentColors;
/** 二次隐私协议弹窗协议文案支持居中、居左、居右设置,默认居左 */
@property (nonatomic, assign) NSTextAlignment privacyAlertContentAlignment;
/** 二次隐私协议弹窗按钮背景图片 ,默认高度50.0pt,@[激活状态的图片,高亮状态的图片] */
@property (nonatomic, copy) NSArray<UIImage *> *privacyAlertBtnBackgroundImages;
/** 二次隐私协议弹窗按钮文字颜色,默认黑色, @[激活状态的颜色,高亮状态的颜色] */
@property (nonatomic, copy) NSArray<UIColor *> *privacyAlertButtonTextColors;
/** 二次隐私协议弹窗按钮文字大小,最小10,默认18*/
@property (nonatomic, strong) UIFont *privacyAlertButtonFont;
/** 二次隐私协议弹窗关闭按钮是否显示,默认显示 */
@property (nonatomic, assign) BOOL privacyAlertCloseButtonIsNeedShow;
/** 二次隐私协议弹窗右侧关闭按钮图片设置,默认内置的X图片*/
@property (nonatomic, strong) UIImage *privacyAlertCloseButtonImage;
/** 二次隐私协议弹窗背景蒙层是否显示 ,默认YES*/
@property (nonatomic, assign) BOOL privacyAlertMaskIsNeedShow;
/** 二次隐私协议弹窗点击背景蒙层是否关闭弹窗 ,默认YES*/
@property (nonatomic, assign) BOOL tapPrivacyAlertMaskCloseAlert;
/** 二次隐私协议弹窗蒙版背景颜色,默认黑色 */
@property (nonatomic, strong) UIColor *privacyAlertMaskColor;
/** 二次隐私协议弹窗蒙版透明度 设置范围0.3~1.0之间 ,默认0.5*/
@property (nonatomic, assign) CGFloat privacyAlertMaskAlpha;
/** 二次隐私协议弹窗蒙版显示动画,默认渐显动画 */
@property (nonatomic, strong) CAAnimation *privacyAlertMaskEntryAnimation;
/** 二次隐私协议弹窗蒙版消失动画,默认渐隐动画 */
@property (nonatomic, strong) CAAnimation *privacyAlertMaskExitAnimation;
/** 二次隐私协议弹窗尺寸设置,不能超出父视图 content view,height不能小于50,width不能小于0,默认屏幕居中,宽为屏幕的宽度减掉80,高为200 */
@property (nonatomic, copy) AlicomFusionBuildFrameBlock privacyAlertFrameBlock;
/** 二次隐私协议弹窗标题尺寸,默认x=0,y=0,width=弹窗宽度,最小宽度为100,height=根据文本计算的高度,最小高度为15,不能超出父视图 */
@property (nonatomic, copy) AlicomFusionBuildFrameBlock privacyAlertTitleFrameBlock;
/** 二次隐私协议弹窗内容尺寸,默认为从标题顶部位置开始,最终会根据设置进来的width对协议文本进行自适应,得到的size是协议控件的最终大小。不能超出父视图 */
@property (nonatomic, copy) AlicomFusionBuildFrameBlock privacyAlertPrivacyContentFrameBlock;
/** 二次隐私协议弹窗尺寸,默认为父视图的宽度一半,居中显示。高度默认50, */
@property (nonatomic, copy) AlicomFusionBuildFrameBlock privacyAlertButtonFrameBlock;
/** 二次隐私协议弹窗右侧关闭按钮尺寸,默认宽高44,居弹窗右侧15,居弹窗顶部0*/
@property (nonatomic, copy) AlicomFusionBuildFrameBlock privacyAlertCloseFrameBlock;

短信认证自定义UI参数

#pragma mark - 自己输入手机号,10001、100002、100003、100004模板使用
/// 获取验证码父View
@property (nonatomic, strong) UIView *verifyCodeSendView;

/// 国家代码显示区
@property (nonatomic, strong) UILabel *countryCodeLabel;

/// 手机号输入
@property (nonatomic, strong) UITextField *phoneTextField;

/// 分割线
@property (nonatomic, strong) UIView *lineView;

#pragma mark - 自动填充手机号,10002、100005模板使用
/// 手机号部分
@property (nonatomic, strong) UILabel *phoneNumLabel;//带*号

@property (nonatomic, strong) NSString *phoneNumText;//真实手机号
/// 说明部分
@property (nonatomic, strong) UILabel *phoneNumExplainLabel;

#pragma mark - 按钮
/// 获取验证码按钮
@property (nonatomic, strong) UIButton *verifyCodeSendBtn;

#pragma mark 协议 100001模板使用
/// checkbox
@property (nonatomic, strong) UIButton *checkBoxBtn;

/// 隐私协议
@property (nonatomic, strong) UITextView *privacyTextView;

#pragma mark - 验证码部分
/// 提交验证码父View
@property (nonatomic, strong) UIView *verifyCodeSubmitView;

/// 发送成功之后的文案
@property (nonatomic, strong) UILabel *expainLabel;

/// 验证码输入框
@property (nonatomic, strong) AlicomFusionVerifyCodeInputView *codeView;

/// 提交验证按钮
@property (nonatomic, strong) UIButton *verifyCodeSubmitBtn;

/// 点击手势,用于收回键盘
@property (nonatomic, strong) UITapGestureRecognizer *tapGesture;

上行短信验证码自定义UI

/// 顶部说明文案背景
@property (nonatomic, strong) UIView *contentBgView;

/// 顶部说明文案
@property (nonatomic, strong) UILabel *contentLabel;

/// 短信内容标题
@property (nonatomic, strong) UILabel *upGoingTitleLabel;

/// 短信内容
@property (nonatomic, strong) UILabel *upGoingContentLabel;

/// 短信接收号码标题
@property (nonatomic, strong) UILabel *phoneTitleLabel;

/// 短信接收号码
@property (nonatomic, strong) UILabel *phoneContentLabel;

/// 去发送短信按钮
@property (nonatomic, strong) UIButton *upGoingSendBtn;

/// 我已发送短信按钮
@property (nonatomic, strong) UIButton *upGoingAlreadySendBtn;

SDK回调说明

AlicomFusionAuthDelegate

Token需要更新

每30秒重试获取一次,连续两次失败SDK将报300004错误。

/**
 *  Token需要更新
 *  @note 必选回调,handler初始化&历史Token过期前5分钟,会触发此回调,由SDK维护Token的生命周期
 *  @param handler handler
 *  @return token,APP更新最新token后,给到SDK,SDK会通过此token进行鉴权更新
 */
- (AlicomFusionAuthToken *)onSDKTokenUpdate:(AlicomFusionAuthHandler *)handler;

Token鉴权成功

请确保回调该接口后再使用SDK相关功能,否则SDK将报300001、300003、300006错误。

/**
 *  Token鉴权成功
 *  @note 必选回调,Token鉴权成功后,才可以使用startScene接口拉起场景
 *  @note 不建议在本回调中直接调用startScene接口,本回调跟随Token鉴权事件触发,可能存在多次回调
 *  @param handler handler
 */
- (void)onSDKTokenAuthSuccess:(AlicomFusionAuthHandler *)handler;

Token鉴权失败

重要

该接口回调,即Token发生错误,SDK的业务功能将不可用,请重新设置Token或销毁SDK。

/**
 *  token鉴权失败
 *  @note 必选回调,token初次鉴权失败&token更新后鉴权失败均会触发此回调
 *  @note token鉴权失败后,无法继续使用SDK的功能,请销毁SDK后重新初始化
 *  @param handler handler
 *  @param failToken 错误token
 *  @param error 错误定义
 */
- (void)onSDKTokenAuthFailure:(AlicomFusionAuthHandler *)handler
                    failToken:(AlicomFusionAuthToken *)failToken
                        error:(AlicomFusionEvent *)error;

认证成功

  • SDK根据业务流程返回认证Token,App侧需使用该Token进行换号服务,进行最终的认证流程。

  • SDK在是一键登录、短信认证、用户主动发短信时,会产生认证Token。用户主动发短信的Token可以多次使用,其他的Token只能使用一次。

  • 该接口只在认证最终阶段进行回调。

  • 若Token换号失败可在该接口中执行continueSceneWithTemplateId:isSuccess:方法进行下一步认证工作。

/**
 *  认证成功
 *  @note 必选回调
 *  @note 可以使用号码效验maskToken去APP Server做最终验证换取真实手机号码,如果换取手机号失败,可以通过SDK的continue接口继续后续场景流程
 *  @param handler handler
 *  @param nodeName 获取token的节点名称
 *  @param maskToken 号码效验token
 */
- (void)onVerifySuccess:(AlicomFusionAuthHandler *)handler
               nodeName:(NSString *)nodeName
              maskToken:(NSString *)maskToken
                  event:(AlicomFusionEvent *)event;

中途认证节点

/**
 *  中途认证节点,需要知道中途认证结果,否则影响流程继续执行,目前只有更换手机号的时候第一次验证码会回调
 *  @note 必选回调
 *  @note 可以使用号码效验maskToken去APP Server做最终验证换取真实手机号码,通过resultBlock告知SDK验证结果,如果失败则SDK不进行任何操作,成功则进入下一个节点
 *  @param handler handler
 *  @param nodeName 获取token的节点名称
 *  @param maskToken 号码效验token
 *  @param resultBlock 告知SDK校验结果
 */
- (void)onHalfwayVerifySuccess:(AlicomFusionAuthHandler *)handler
                      nodeName:(NSString *)nodeName
                     maskToken:(NSString *)maskToken
                         event:(AlicomFusionEvent *)event
                   resultBlock:(void (^)(BOOL))resultBlock;

认证失败

回调该接口即当前节点认证存在问题,可继续执行continueSceneWithTemplateId:isSuccess:方法进行其他的认证方案。

/**
 *  认证失败
 *  @note 必选回调
 *  @note 当接收到这个回调时表示在场景的某个节点出现获取token失败的情况,业务方可以根据实际情况决定是否需要执行下一个节点
 *  @param handler handler
 *  @param nodeName 获取token的节点名称
 *  @param error 错误
 */
- (void)onVerifyFailed:(AlicomFusionAuthHandler *)handler
              nodeName:(NSString *)nodeName
                 error:(AlicomFusionEvent *)error;

场景流程结束

需在此回调中执行stopSceneWithTemplateId:方法,以便下次场景再次发起。

/**
 *  场景流程结束
 *  @note 必选回调,SDK当前场景流程结束,场景正常结束和异常结束均会触发此回调
 *  @param handler handler
 *  @param event 结束事件
 */
- (void)onTemplateFinish:(AlicomFusionAuthHandler *)handler
                   event:(AlicomFusionEvent *)event;

填充手机号

  • 用于短信验证码认证页面的自动填充手机号。

  • 自动填充手机号用于更换手机号场景(AlicomFusionTemplateId_100002)的第一步校验当前手机号,验证当前手机号场景(AlicomFusionTemplateId_100005)。

  • 校验手机号一致性,用于重置密码场景(AlicomFusionTemplateId_100003),若不一致,则整个场景结束,回调onTemplateFinish:event:

/**
 *  填充手机号,用于校验手机号是否和输入的一致,或者重新绑定手机号场景自动填充手机号
 *  @note 必选回调,SDK内置UI部分手机号
 *  @note 比如重置密码场景,需要先填写原手机号码进行第一步效验,SDK需效验该填写值是否为真实的原手机号码,或者重新绑定手机号场景自动填充手机号
 *  @param handler handler
 *  @param event 事件
 *  @return 返回当前用户正在使用的手机号用于下一步操作
 */
- (NSString *)onGetPhoneNumberForVerification:(AlicomFusionAuthHandler *)handler
                                        event:(AlicomFusionEvent *)event;

点击协议富文本

注册登录场景(AlicomFusionTemplateId_100001)需要显示协议,点击相应协议的回调,App侧根据返回内容,创建webview进行展示。

/**
 *  点击协议富文本,返回协议标题以及协议URL,外部需要自定义容器打开该协议
 *  @note 必选回调,SDK协议详情页
 *  @note 一键登录的协议点击,短信验证码的协议点击
 *  @param handler handler
 *  @param protocolName 协议名称
 *  @param protocolUrl 协议URL
 *  @param event 事件
 */
- (void)onProtocolClick:(AlicomFusionAuthHandler *)handler
           protocolName:(NSString *)protocolName
            protocolUrl:(NSString *)protocolUrl
                  event:(AlicomFusionEvent *)event;

认证中断

  • 第一种认证中断指流程无法继续执行,在某节点无法校验通过,如手机号的长度不正确、未勾选隐私协议框等,App侧可以选择弹出toast提示。

  • 第二种认证中断指进行一些耗时操作,需要loading指示,如目前端风险检测开始、获取短信验证码开始、获取用户主动发短信内容开始。SDK会回调900003状态码,表示开始loading,端风险检测结束,获取短信验证码结束。获取用户主动发短信内容结束会回调900004状态码,表示loading结束。App侧根据状态码添加对应的指示器。

/**
 *  认证中断
 *  @note 必选回调
 *  @note 认证流程临时中断,App可根据不同事件显示对应的提示信息
 *  @note 触发条件:1. 未勾选隐私协议框,进行认证;2. 验证手机号码输入格式错误,3.SDK开始加载某个节点和结束加载某个节点,4、相关的接口可用校验
 *  @param handler handler
 *  @param event 中断原因
 */
- (void)onVerifyInterrupt:(AlicomFusionAuthHandler *)handler
                    event:(AlicomFusionEvent *)event;

场景事件回调

/**
 *  场景事件回调
 *  @note 可选回调,包括SDK内所有event通知,例如场景流程中各个界面点击事件、界面跳转事件、错误事件等等
 *  @note 本回调接口仅做事件通知,不可再此回调内处理业务逻辑
 *  @param handler handler
 *  @param event 点击事件,具体定义参考AlicomFusionEvent.h
 */
- (void)onAuthEvent:(AlicomFusionAuthHandler *)handler
          eventData:(AlicomFusionEvent *)event;

AlicomFusionAuthUIDelegate

一键登录自定义UI

/**
 *  一键登录自定义UI
 *  @note 自定义一键登录相关UI界面,一键登录界面不可100%完全自定义,请通过AlicomFusionNumberAuthModel参数进行修改
 *  @param handler handler
 *  @param templateId 模板id
 *  @param nodeId 节点ID
 *  @param model 自定义UI属性
 */
- (void)onPhoneNumberVerifyUICustomDefined:(AlicomFusionAuthHandler *)handler
                                templateId:(NSString *)templateId
                                     nodeId:(NSString *)nodeId
                                   UIModel:(AlicomFusionNumberAuthModel *)model;

短信验证码认证自定义UI

  • 如果自定义了发送按钮,发送按钮的点击事件请调用verifyCodeBtnClick:checked:方法进行验证码发送。

  • 如果自定义了提交按钮,提交按钮的点击事件请调用submitVerifyCodeBtnClick:code:进行验证码提交。

/**
 *  短信验证码认证自定义UI
 *  @note 短信验证码界面相关UI修改
 *  @param handler handler
 *  @param templateId 模板ID
 *  @param nodeId 节点ID
 *  @param isAutoInput 手机号是否是自动填充
 *  @param view 短信验证码界面view
 */
- (void)onSMSCodeVerifyUICustomDefined:(AlicomFusionAuthHandler *)handler
                            templateId:(NSString *)templateId
                                nodeId:(NSString *)nodeId
                           isAutoInput:(BOOL)isAutoInput
                                  view:(AlicomFusionVerifyCodeView *)view;

用户主动发短信认证自定义UI

  • 如果自定义了发送按钮,发送按钮的点击事件请调用gotoSendUpGoing:receiveNum:方法进行验证码发送。

  • 如果自定义了已发送按钮,提交按钮的点击事件请调用upGoingAlreadySend进行认证操作。

/**
 *  上行短信认证自定义UI
 *  @note 上行短信认证界面相关UI修改
 *  @param handler handler
 *  @param templateId 模板ID
 *  @param nodeId 节点ID
 *  @param smsContent 短信内容
 *  @param receiveNum 接收号码
 *  @param view 上行短信认证界面view
 */
- (void)onSMSSendVerifyUICustomDefined:(AlicomFusionAuthHandler *)handler
                            templateId:(NSString *)templateId
                                nodeId:(NSString *)nodeId
                            smsContent:(NSString *)smsContent
                            receiveNum:(NSString *)receiveNum
                                  view:(AlicomFusionUpGoingView *)view;

navigationController自定义UI

/**
 *  navigationController自定义UI
 *  @note iOS特有
 *  @param handler handler
 *  @param templateId 模板ID
 *  @param nodeId 节点ID
 *  @param naviController navigationController
 */
- (void)onNavigationControllerCustomDefined:(AlicomFusionAuthHandler *)handler
                                 templateId:(NSString *)templateId
                                     nodeId:(NSString *)nodeId
                                 navigation:(UINavigationController *)naviController;

使用示例

@interface AlicomFusionManager ()
@property (nonatomic, copy) NSString *currTemplateId;
@property (nonatomic, weak) UIViewController *currVC;
@property (nonatomic, assign) NSInteger reGetTime;
@property (nonatomic, assign) AlicomFusionNumberAuthModel *authmodel;
@property (nonatomic, strong) AlicomFusionVerifyCodeView *verifyView;
@property (nonatomic, strong) AlicomFusionUpGoingView *upGoingView;
@property (nonatomic, copy) NSString *smsContent;
@property (nonatomic, copy) NSString *receiveNum;
@end

@implementation AlicomFusionManager

+ (instancetype)shareInstance {
    static AlicomFusionManager *instance = nil ;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (instance == nil) {
            [AlicomFusionLog logEnable:YES];
            instance = [[AlicomFusionManager alloc] init];
            instance.reGetTime = 3;
        }
    });
    return instance;
}

- (void)destory {
    [self.handler destroy];
    self.handler = nil;
    self.isActive = NO;
}

- (void)start{
    [AlicomFusionAuthTokenManager getAuthToken:^(NSString * _Nonnull tokenStr,NSString *errorMsg) {
        if (errorMsg){
            dispatch_async(dispatch_get_main_queue(), ^{
                [AlicomFusionToastTool showToastMsg:errorMsg];
            });
        } else {
            [self initFusionAuth];
        }
    }];
}

- (void)startSceneWithTemplateId:(NSString *)templateId viewController:(UIViewController *)controller{
    self.currTemplateId = templateId;
    self.currVC = controller;
    [self.handler startSceneUIWithTemplateId:self.currTemplateId viewController:controller delegate:self];
}

- (void)stopScene{
    [self.handler stopSceneWithTemplateId:self.currTemplateId];
}

- (void)initFusionAuth{
    if (!self.handler){
       NSString *tokenStr = [AlicomFusionAuthTokenManager shareInstance].authTokenStr;
        AlicomFusionAuthToken *token = [[AlicomFusionAuthToken alloc] initWithTokenStr:tokenStr];
        self.handler = [[AlicomFusionAuthHandler alloc] initWithToken:token schemeCode:DEMO_SCHEME_CODE];
        [self.handler setFusionAuthDelegate:self];
    }
}

- (void)dealWithPhone:(NSString *)phoneNum {
    if (phoneNum.length < 11) {
        [AlicomFusionToastTool showToastMsg:@"获取手机号失败"];
        return;
    }
    if ([AlicomFusionTemplateId_100001 isEqualToString:self.currTemplateId]) {
        NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
        [ud setObject:phoneNum forKey:kDEMO_UD_PHONE_NUM];
        [ud synchronize];
        [AlicomFusionToastTool showToastMsg:@"登录成功"];
        [self.handler stopSceneWithTemplateId:self.currTemplateId];
    } else if ([AlicomFusionTemplateId_100002 isEqualToString:self.currTemplateId]) {
        NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
        [ud setObject:phoneNum forKey:kDEMO_UD_PHONE_NUM];
        [ud synchronize];
        [AlicomFusionToastTool showToastMsg:@"修改手机号成功"];
        [self.handler stopSceneWithTemplateId:self.currTemplateId];
    } else if ([AlicomFusionTemplateId_100003 isEqualToString:self.currTemplateId]) {
        NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
        NSString *loginPhone = [ud objectForKey:kDEMO_UD_PHONE_NUM];
        if ([phoneNum isEqualToString:loginPhone]) {
            [AlicomFusionToastTool showToastMsg:@"手机号验证通过,可以去修改密码了"];
            [self.handler stopSceneWithTemplateId:self.currTemplateId];
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                if ([self.delegate respondsToSelector:@selector(verifySuccess)]){
                    [self.delegate verifySuccess];
                }
            });
            
      
        } else {
            [AlicomFusionToastTool showToastMsg:@"手机号验证不通过"];
            [self.handler continueSceneWithTemplateId:self.currTemplateId isSuccess:NO];
        }
    } else if ([AlicomFusionTemplateId_100004 isEqualToString:self.currTemplateId]) {
        NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
        [ud setObject:phoneNum forKey:kDEMO_UD_PHONE_NUM];
        [ud synchronize];
        [AlicomFusionToastTool showToastMsg:@"新手机号绑定成功"];
        [self.handler stopSceneWithTemplateId:self.currTemplateId];
    } else if ([AlicomFusionTemplateId_100005 isEqualToString:self.currTemplateId]) {
        NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
        NSString *loginPhone = [ud objectForKey:kDEMO_UD_PHONE_NUM];
        if ([phoneNum isEqualToString:loginPhone]) {
            [AlicomFusionToastTool showToastMsg:@"手机号验证通过"];
            [self.handler stopSceneWithTemplateId:self.currTemplateId];
        } else {
            [AlicomFusionToastTool showToastMsg:@"手机号验证不通过"];
            [self.handler continueSceneWithTemplateId:self.currTemplateId isSuccess:NO];
        }
    }

    
}

#pragma mark - AlicomFusionAuthDelegate
/**
 *  token需要更新
 *  @note 必选回调,handler 初始化&历史token过期前5分钟,会触发此回调,由SDK维护token的生命周期
 *  @param handler handler
 *  @return token,APP更新最新token后,组装AlicomFusionAuthToken返回给到SDK,SDK会通过此token进行鉴权更新
 */
- (AlicomFusionAuthToken *)onSDKTokenUpdate:(AlicomFusionAuthHandler *)handler {
    NSLog(@"%s,调用",__func__);
    NSString *tokenStr = [AlicomFusionAuthTokenManager updateAuthToken];
    AlicomFusionAuthToken *token = [[AlicomFusionAuthToken alloc] initWithTokenStr:tokenStr];
    return token;
}

/**
 *  token鉴权成功
 *  @note 必选回调,token鉴权成功后,才可以调用startScene接口拉起场景
 *  @param handler handler
 */
- (void)onSDKTokenAuthSuccess:(AlicomFusionAuthHandler *)handler {
    NSLog(@"%s,调用",__func__);
    self.isActive = YES;
    self.reGetTime = 3;
}

/**
 *  token鉴权失败
 *  @note 必选回调,token初次鉴权失败&token更新后鉴权失败均会触发此回调
 *  @note token鉴权失败后,无法继续使用SDK的功能,请销毁SDK后重新初始化
 *  @param handler handler
 *  @param failToken 错误token
 *  @param error 错误定义
 */
- (void)onSDKTokenAuthFailure:(AlicomFusionAuthHandler *)handler
                    failToken:(AlicomFusionAuthToken *)failToken
                        error:(AlicomFusionEvent *)error {
    NSLog(@"%s,调用:{\n%@}",__func__,error.description);
    self.isActive = NO;
    if (self.reGetTime > 0) {
        NSString *tokenStr = [AlicomFusionAuthTokenManager updateAuthToken];
        AlicomFusionAuthToken *token = [[AlicomFusionAuthToken alloc] initWithTokenStr:tokenStr];
        [handler updateToken:token];
        self.reGetTime --;
    } else {
        self.reGetTime = 3;
        [self.handler destroy];
        self.handler = nil;
        [AlicomFusionAuthTokenManager shareInstance].authTokenStr = nil;
        return;
    }
   
}

/**
 *  认证成功
 *  @note 必选回调
 *  @note 可以使用号码效验maskToken去APP Server做最终验证换取真实手机号码,如果换取手机号失败,可以通过SDK的continue接口继续后续场景流程
 *  @param handler handler
 *  @param maskToken 号码效验token
 */
- (void)onVerifySuccess:(AlicomFusionAuthHandler *)handler
               nodeName:(nonnull NSString *)nodeName
              maskToken:(NSString *)maskToken
                  event:(nonnull AlicomFusionEvent *)event {
    NSLog(@"%s,调用.nodeName=%@",__func__,nodeName);
    //换手机号
    dispatch_async(dispatch_get_main_queue(), ^{
        [AlicomFusionToastTool showLoading];
    });
    [AlicomFusionNetAdapter verifyTokenRequest:maskToken complete:^(id data,NSError *error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [AlicomFusionToastTool hideLoading];
            if (data&&!error){
                //保存手机号
                if ([data isKindOfClass:NSDictionary.class]) {
                    NSString *verifyResult = ((NSDictionary *)data)[@"VerifyResult"];
                    if ([@"PASS" isEqualToString:verifyResult]) {
                        [self dealWithPhone:((NSDictionary *)data)[@"PhoneNumber"]];
                    } else if ([@"REJECT" isEqualToString:verifyResult] || [@"UNKNOW" isEqualToString:verifyResult]) {
                        if ([nodeName isEqualToString:AlicomFusionNodeNameNumberAuth]) {
                            [AlicomFusionToastTool showToastMsg:@"一键登录失败"];
                        } else if ([nodeName isEqualToString:AlicomFusionNodeNameVerifyCodeAuth]) {
                            [AlicomFusionToastTool showToastMsg:@"获取手机号失败,请检查验证码是否正确或是否过期"];
                        } else if ([nodeName isEqualToString:AlicomFusionNodeNameUpGoingAuth]) {
                            [AlicomFusionToastTool showToastMsg:@"获取手机号失败,请检查短信是否发送成功"];
                        }
                        
                        if (![AlicomFusionNodeNameVerifyCodeAuth isEqualToString:nodeName]) {
                            [self.handler continueSceneWithTemplateId:self.currTemplateId isSuccess:NO];
                        }
                    }
                } else {
                    if (![AlicomFusionNodeNameVerifyCodeAuth isEqualToString:nodeName]) {
                        [self.handler continueSceneWithTemplateId:self.currTemplateId isSuccess:NO];
                    }
                }
            }else{
                //结束认证
                [self.handler stopSceneWithTemplateId:self.currTemplateId];
                [AlicomFusionToastTool showToastMsg:error.userInfo[NSLocalizedDescriptionKey] ?:@"操作失败"];
            }
        });
    }];
}

- (void)onHalfwayVerifySuccess:(AlicomFusionAuthHandler *)handler nodeName:(NSString *)nodeName maskToken:(NSString *)maskToken event:(nonnull AlicomFusionEvent *)event resultBlock:(void (^)(BOOL))resultBlock {
    NSLog(@"%s,调用.nodeName=%@",__func__,nodeName);
    dispatch_async(dispatch_get_main_queue(), ^{
        [AlicomFusionToastTool showLoading];
    });
    [AlicomFusionNetAdapter verifyTokenRequest:maskToken complete:^(id data,NSError *error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [AlicomFusionToastTool hideLoading];
            if (data&&!error){
                //保存手机号
                if ([data isKindOfClass:NSDictionary.class]) {
                    NSString *verifyResult = ((NSDictionary *)data)[@"VerifyResult"];
                    if ([@"PASS" isEqualToString:verifyResult]) {
                        [AlicomFusionToastTool showToastMsg:@"校验成功"];
                        if (resultBlock) {
                            resultBlock(YES);
                        }
                    } else if ([@"REJECT" isEqualToString:verifyResult] || [@"UNKNOW" isEqualToString:verifyResult]) {
                        [AlicomFusionToastTool showToastMsg:@"校验失败"];
                    }
                } else {
                    [AlicomFusionToastTool showToastMsg:@"校验失败"];
                }
            }else{
                [AlicomFusionToastTool showToastMsg:@"校验失败"];
            }
        });
    }];
}

- (void)onVerifyFailed:(AlicomFusionAuthHandler *)handler nodeName:(nonnull NSString *)nodeName error:(nonnull AlicomFusionEvent *)error {
    NSLog(@"%s,nodeName=%@,调用:{\n%@}",__func__,nodeName,error.description);
    if ([nodeName isEqualToString:AlicomFusionNodeNameVerifyCodeAuth]) {
        if ([error.resultCode isEqualToString:AlicomFusionVerifyCodeFrequency] || [error.resultCode isEqualToString:AlicomFusionVerifyCodeRisk]) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [AlicomFusionToastTool showToastMsg:error.resultMsg];
            });
            [self.handler continueSceneWithTemplateId:self.currTemplateId isSuccess:NO];
        } else if ([AlicomFusionVerifyCodeAutoNumberShowFail isEqualToString:error.resultCode]) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [AlicomFusionToastTool showToastMsg:error.resultMsg];
            });
            [self.handler stopSceneWithTemplateId:self.currTemplateId];
        } else {
            dispatch_async(dispatch_get_main_queue(), ^{
                [AlicomFusionToastTool showToastMsg:error.resultMsg];
            });
        }
    } else {
        [self.handler continueSceneWithTemplateId:self.currTemplateId isSuccess:NO];
    }
}

/**
 *  认证结束
 *  @note 必选回调,SDK认证流程结束
 *  @param handler handler
 *  @param event 结束事件
 */
- (void)onTemplateFinish:(AlicomFusionAuthHandler *)handler event:(AlicomFusionEvent *)event {
    NSLog(@"%s,调用:{\n%@}",__func__,event.description);
    //结束认证
    [self.handler stopSceneWithTemplateId:self.currTemplateId];
}

- (void)onProtocolClick:(AlicomFusionAuthHandler *)handler protocolName:(NSString *)protocolName protocolUrl:(NSString *)protocolUrl event:(AlicomFusionEvent *)event
{
    NSLog(@"%s,调用:{\n%@}",__func__,event.description);
    AlicomFusionWebViewController *controller = [[AlicomFusionWebViewController alloc] initWithUrl:protocolUrl andUrlName:protocolName];
    UINavigationController *navigationController = self.currVC.navigationController;
    if (self.currVC.presentedViewController) {
        //如果授权页成功拉起,这个时候则需要使用授权页的导航控制器进行跳转
        navigationController = (UINavigationController *)self.currVC.presentedViewController;
    }
    [navigationController pushViewController:controller animated:YES];
}

- (void)onVerifyInterrupt:(AlicomFusionAuthHandler *)handler event:(AlicomFusionEvent *)event {
    if ([event.resultCode isEqualToString:AlicomFusionStartLoading]) {
        [AlicomFusionToastTool showLoading];
    } else if ([event.resultCode isEqualToString:AlicomFusionEndLoading]) {
        [AlicomFusionToastTool hideLoading];
    } else {
        [AlicomFusionToastTool showToastMsg:[NSString stringWithFormat:@"%@,%@", event.resultCode, event.resultMsg]];
    }
}

/**
 *  场景事件回调
 *  @note 可选回调,SDK场景流程中各个界面点击事件&界面跳转事件等UI相关回调
 *  @note 本回调接口仅做事件通知,不可再此回调内处理业务逻辑
 *  @param handler handler
 *  @param event 点击事件,具体定义参考AlicomFusionEvent.h
 */
- (void)onAuthEvent:(AlicomFusionAuthHandler *)handler
          eventData:(AlicomFusionEvent *)event {
    NSLog(@"%s,调用:{\n%@}",__func__,event.description);
}

/**
 *  填充手机号,用于校验手机号是否和输入的一致,或者重新绑定手机号场景自动填充手机号
 *  @note 必选回调,SDK内置UI部分手机号
 *  @note 比如重置密码场景,需要先填写原手机号码进行第一步效验,SDK需效验该填写值是否为真实的原手机号码,或者重新绑定手机号场景自动填充手机号
 *  @param handler handler
 *  @param event 事件
 *  @return 返回当前用户正在使用的手机号用于下一步操作
 */
- (NSString *)onGetPhoneNumberForVerification:(AlicomFusionAuthHandler *)handler
                                        event:(nonnull AlicomFusionEvent *)event {
    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
    NSString *phoneNum = [ud objectForKey:kDEMO_UD_PHONE_NUM];
    return phoneNum;
    
}


- (void)otherPhoneLoginClick {
    [self.authmodel otherPhoneLogin];
}

#pragma mark - AlicomFusionAuthUIDelegate
- (void)onPhoneNumberVerifyUICustomDefined:(AlicomFusionAuthHandler *)handler
                                templateId:(nonnull NSString *)templateId
                                    nodeId:(NSString *)nodeId
                                   UIModel:(AlicomFusionNumberAuthModel *)model {
    
    self.authmodel = model;
    model.supportedInterfaceOrientations = UIInterfaceOrientationMaskPortrait;
    model.presentDirection = AlicomFusionPresentationDirectionBottom;
    model.suspendDisMissVC = NO;
    model.navTitle = [[NSAttributedString alloc] initWithString:@"一键登录"];
    model.navColor = AlicomColorHex(0xEFF3F2);
    model.logoIsHidden = YES;
    model.numberColor = AlicomColorHex(0x262626);
    model.numberFont = [UIFont systemFontOfSize:24];
    NSDictionary *loginAttriDict = @{
        NSFontAttributeName: [UIFont systemFontOfSize:16],
        NSForegroundColorAttributeName: AlicomColorHex(0xFFFFFF)
    };
    NSMutableAttributedString *loginAttr = [[NSMutableAttributedString alloc] initWithString:@"一键登录" attributes:loginAttriDict];
    model.loginBtnText = loginAttr;
    UIImage *unSelectImage = [AlicomFusionDemoUtil demoImageWithColor:AlicomColorHex(0x0064C8) size:CGSizeMake(ALICOM_FUSION_DEMO_SCREEN_WIDTH - 32, 44) isRoundedCorner:NO radius:0.0];
    UIImage *selectImage = [AlicomFusionDemoUtil demoImageWithColor:AlicomColorHex(0x0064C8) size:CGSizeMake(ALICOM_FUSION_DEMO_SCREEN_WIDTH - 32, 44) isRoundedCorner:NO radius:0.0];
    UIImage *heighLightImage = [AlicomFusionDemoUtil demoImageWithColor:AlicomColorHex(0x0064C8) size:CGSizeMake(ALICOM_FUSION_DEMO_SCREEN_WIDTH - 32, 44) isRoundedCorner:NO radius:0.0];
    model.loginBtnBgImgs = @[unSelectImage, selectImage, heighLightImage];
    
    NSDictionary *sloganAttriDict = @{
        NSFontAttributeName: [UIFont systemFontOfSize:15],
        NSForegroundColorAttributeName: AlicomColorHex(0x555555)
    };
    NSMutableAttributedString *sloganAttr = [[NSMutableAttributedString alloc] initWithString:@"阿里云为您提供认证服务" attributes:sloganAttriDict];
    model.sloganText = sloganAttr;
    model.privacyOperatorIndex = 2;
    model.privacyOne = @[@"用户协议",@"https://terms.alicdn.com/legal-agreement/terms/product/20230213121650869/20230213121650869.html"];
    model.privacyTwo = @[@"个人信息保护政策",@"https://terms.aliyun.com/legal-agreement/terms/suit_bu1_ali_cloud/suit_bu1_ali_cloud202112211045_86198.html?spm=a2c4g.11186623.0.0.72701a9edzzvbz"];
    model.privacyConectTexts = @[@"、",@" 和 "];
    model.privacyPreText = @"我已阅读并同意 ";
    model.privacyOperatorPreText = @"";
    model.privacyOperatorSufText = @"";
    model.privacyColors = @[AlicomColorHex(0x262626), AlicomColorHex(0x262626)];
    model.privacyFont = [UIFont systemFontOfSize:14];
    model.checkBoxIsHidden = NO;
    model.checkBoxIsChecked = NO;
    model.checkBoxWH = 21;
    model.backgroundColor = AlicomColorHex(0xEFF3F2);
    model.moreLoginActionBlock = ^{
        NSLog(@"其他登录方式");
    };
    
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, ALICOM_FUSION_DEMO_SCREEN_WIDTH, 100)];
    label.text = @"登录";
    label.textColor = AlicomColorHex(0x262626);
    label.font = [UIFont systemFontOfSize:24];
    [label sizeToFit];
    model.nameLabel = label;
    
    UIButton *otherLogin = [UIButton buttonWithType:UIButtonTypeCustom];
    [otherLogin setTitle:@"其他手机号登录" forState:UIControlStateNormal];
    otherLogin.backgroundColor = UIColor.whiteColor;
    [otherLogin setTitleColor:AlicomColorHex(0x262626) forState:UIControlStateNormal];
    otherLogin.titleLabel.font = [UIFont systemFontOfSize:16];
    [otherLogin addTarget:self action:@selector(otherPhoneLoginClick) forControlEvents:UIControlEventTouchUpInside];
    model.otherLoginButton = otherLogin;
    
    model.numberFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) {
        CGFloat x = (screenSize.width - frame.size.width) * 0.5;
        CGFloat y = 214;
        CGRect rect = CGRectMake(x, y, frame.size.width, frame.size.height);
        return rect;
    };
    
    model.sloganFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) {
        CGFloat y = 252;
        CGRect rect = CGRectMake(frame.origin.x, y, frame.size.width, frame.size.height);
        return rect;
    };
    
    model.loginBtnFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) {
        CGFloat y = 318;
        CGRect rect = CGRectMake(frame.origin.x, y, frame.size.width, frame.size.height);
        return rect;
    };
    
    model.nameLabelFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) {
        return frame;
    };
    model.otherLoginButtonFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) {
        return frame;
    };
    
    model.privacyFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) {
        CGRect rect = CGRectMake(frame.origin.x, screenSize.height - 60 - ALICOM_FUSION_DEMO_STATUS_BAR_HEIGHT - frame.size.height - 34, frame.size.width, frame.size.height);
        return rect;
    };
    
    model.customViewLayoutBlock = ^(CGSize screenSize, CGRect contentViewFrame, CGRect nameLabelFrame, CGRect otherLoginBtnFrame, CGRect navFrame, CGRect titleBarFrame, CGRect logoFrame, CGRect sloganFrame, CGRect numberFrame, CGRect loginFrame, CGRect changeBtnFrame, CGRect privacyFrame) {
        
    };

    model.customViewBlock = ^(UIView * _Nonnull superCustomView) {};
    
    model.privacyAlertIsNeedShow = YES;
    model.privacyAlertIsNeedAutoLogin = YES;
    model.privacyAlertCornerRadiusArray = @[@4, @4, @4, @4];
    model.privacyAlertTitleFont = [UIFont systemFontOfSize:16];
    model.privacyAlertTitleColor = AlicomColorHex(0x262626);
    model.privacyAlertContentFont = [UIFont systemFontOfSize:16];
    model.privacyAlertContentAlignment = NSTextAlignmentCenter;
    model.privacyAlertButtonTextColors = @[AlicomColorHex(0x0064C8), AlicomColorHex(0x0064C8)];
    UIImage *imageUnselect = [AlicomFusionDemoUtil demoImageWithColor:AlicomColorHex(0xFFFFFF) size:CGSizeMake(ALICOM_FUSION_DEMO_SCREEN_WIDTH, 56) isRoundedCorner:NO radius:0.0];
    UIImage *imageSelect = [AlicomFusionDemoUtil demoImageWithColor:AlicomColorHex(0xFFFFFF) size:CGSizeMake(ALICOM_FUSION_DEMO_SCREEN_WIDTH, 56) isRoundedCorner:NO radius:0.0];
    model.privacyAlertBtnBackgroundImages = @[imageUnselect, imageSelect];
    model.privacyAlertButtonFont = [UIFont systemFontOfSize:16];
    model.tapPrivacyAlertMaskCloseAlert = NO;
    model.privacyAlertMaskColor = AlicomColorHex(0x262626);
    model.privacyAlertMaskAlpha = 0.88;
    
    model.privacyAlertFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) {
        CGRect rect = CGRectMake(27, (superViewSize.height - 200)*0.382, superViewSize.width - 54, 200);
        return rect;
    };
    
    model.privacyAlertTitleFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) {
        CGRect rect = CGRectMake(0, 32, frame.size.width, frame.size.height);
        return rect;
    };
    
    model.privacyAlertPrivacyContentFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) {
        CGRect rect = CGRectMake(24, 70, superViewSize.width - 48, frame.size.height);
        return rect;
    };
    
    model.privacyAlertButtonFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) {
        CGRect rect = CGRectMake(0, superViewSize.height - 56, superViewSize.width, 56);
        return rect;
    };
}

- (void)onSMSCodeVerifyUICustomDefined:(AlicomFusionAuthHandler *)handler
                            templateId:(nonnull NSString *)templateId
                                nodeId:(NSString *)nodeId
                           isAutoInput:(BOOL)isAutoInput
                                  view:(AlicomFusionVerifyCodeView *)view {
    self.verifyView = view;
}


- (void)onSMSSendVerifyUICustomDefined:(AlicomFusionAuthHandler *)handler
                            templateId:(nonnull NSString *)templateId
                                nodeId:(NSString *)nodeId
                            smsContent:(nonnull NSString *)smsContent
                            receiveNum:(nonnull NSString *)receiveNum
                                  view:(AlicomFusionUpGoingView *)view {
    self.upGoingView = view;
    self.smsContent = smsContent;
    self.receiveNum = receiveNum;
    NSLog(@"smsContent-====%@,receiveNum=-------%@",smsContent,receiveNum);
}


- (void)onNavigationControllerCustomDefined:(AlicomFusionAuthHandler *)handler
                                 templateId:(nonnull NSString *)templateId
                                     nodeId:(NSString *)nodeId
                                 navigation:(UINavigationController *)naviController {
    
}

UI页面接口说明

页面

方法

状态栏

prefersStatusBarHidden: 状态栏是否隐藏,默认NO。

preferredStatusBarStyle: 状态栏主题风格,默认UIStatusBarStyleDefault。

导航栏

  • navIsHidden: 设置导航栏是否隐藏。

  • navColor: 设置导航栏主题色。

  • navTitle: 设置导航栏标题内容、文字大小、颜色。

  • navBackImage: 设置导航栏返回图片。

  • hideNavBackItem: 设置导航栏返回按钮是否隐藏。

  • navMoreView: 导航栏右侧自定义控件,可以在创建该VIEW时添加手势操作,或者创建按钮或者其他赋值给VIEW。

  • navBackButtonFrameBlock: 构建导航栏返回按钮的frame,view布局或者布局发生变化时调用,不实现则默认处理。

  • navTitleFrameBlock: 构建导航栏标题的frame,view布局或布局发生变化时调用,不实现则默认处理。

  • navMoreViewFrameBlock: 构建导航栏右侧more view 的frame,view布局或布局发生变化时调用,不实现则按默认处理。

  • privacyNavColor: 协议详情页导航栏背景颜色设置。

  • privacyNavTitleFont: 协议详情页导航栏标题字体、大小。

  • privacyNavTitleColor: 协议详情页导航栏标题颜色。

  • privacyNavBackImage: 协议详情页导航栏返回图片。

Logo区

  • logoImage: logo图片设置。

  • logoIsHidden: logo是否隐藏,默认NO。

  • logoFrameBlock:构建logo的frame,view布局或布局发生变化时调用,不实现则按默认处理。

Slogan

  • sloganText: 设置slogan文案,内容、大小、颜色。

  • sloganIsHidden: 设置slogan是否隐藏、默认不隐藏。

  • sloganFrameBlock: 构建slogan的frame,view布局或者布局发生变化时调用,不实现则按默认处理。

掩码栏

  • numberColor: 设置掩码颜色。

  • numberFont: 设置掩码字体大小设置,<16则不生效。

  • numberFrameBlock: 构建号码的frame,view布局或布局发生变化时调用,只有x,y生效,不实现则按默认处理,设置不能超出父视图content view。

登录按钮

  • loginBtnText: 设置登录按钮文案、内容、大小、颜色。

  • loginBtnBgImgs: 登录按钮背景图片组,默认高度50.0pt,@[激活状态的图片、失效状态的图片、高亮状态的图片]。

  • autoHideLoginLoading: 是否自动隐藏点击登录按钮之后授权页上转圈的loading,默认为YES,在获取登录Token成功后自动隐藏,如果设置为NO,需要自己手动调用[[TXCommonHandler, sharedInstance] hideLoginLoading]隐藏。

  • loginBtnFrameBlock: 构建登录按钮frame,view布局或布局发生变化时调用,不实现则按默认处理,不能超出父视图content view,height不能小于40,width不能小于父视图宽度的一半。

切换到其他方式

  • changeBtnTitle: changeBtn标题,内容,大小,颜色。

  • changeBtnIsHidden: changeBtn是否隐藏,默认NO。

  • changeBtnFrameBlock: 构建changeBtn的frame,view布局或布局发生变化时调用,不实现则按默认处理。

自定义控件区(如其他方式登录)

  • customViewBlock: 自定义控件添加,注意:自定义视图的创建初始化和添加到父视图都需要在主线程中。

  • customViewLayoutBlock: 每次授权页布局完成时会调用该block,可以在该block实现里面设置自定义添加控件的frame。

  • 您可以在除了协议、掩码、登录按钮之外的区域添加自定义控件。

协议栏

  • checkBoxImages: 设置checkBox图片组,[uncheckedImg, checkedImg]。

  • checkBoxIsChecked: checkBox是否勾选,默认NO。

  • checkBoxIsHidden: checkBox是否隐藏,默认NO。

  • checkBoxWH: checkBox大小,高宽一样,必须大于0。

  • privacyOne: 协议1,[协议名称,协议Url], 注:三个协议名称不能相同。

  • privacyTwo: 协议2,[协议名称,协议Url], 注:三个协议名称不能相同。

  • privacyThree: 协议3,[协议名称,协议Url], 注:三个协议名称不能相同。

  • privacyColors: 协议内容颜色数组,[非点击文案颜色,点击文案颜色]。

  • privacyAligment: 协议文案支持居中,居左设置,默认居左。

  • privacyPreText: 协议整体文案,前缀部分文案。

  • privacySufText: 协议整体文案,后缀部分文案。

  • privacyOperatorPreText: 运营商协议名称前缀文案,仅支持<、(、[、{、(、【、『、。

  • privacyOperatorSufText: 运营商协议 >、)、]、}、)、】、』。

  • privacyFont: 协议整体文案字体大小,小于12.0 不生效。

  • privacyFrameBlock: 构建协议整体(包括checkBox)的frame,view布局或布局发生变化时调用,不实现则按默认处理,如果设置的width小于checkBox的宽度则不生效,最小值为0,最大width,height为父视图宽高,最终会根据设置进来的width对协议文本进行自适应,得到的size是协议空间的最终大小。

  • privacyOperatorColor:运营商协议内容颜色 ,优先级最高,如果privacyOperatorColors不设置,则取privacyColors中的点击文案颜色,privacyColors不设置,则是默认色。

  • privacyOneColor:协议1内容颜色,优先级最高,如果privacyOneColors不设置,则取privacyColors中的点击文案颜色,privacyColors不设置,则是默认色。

  • privacyTwoColor:协议2内容颜色,优先级最高,如果privacyTwoColors不设置,则取privacyColors中的点击文案颜色,privacyColors不设置,则是默认色。

  • privacyThreeColor:协议3内容颜色,优先级最高,如果privacyThreeColors不设置,则取privacyColors中的点击文案颜色,privacyColors不设置,则是默认色。

其他全屏页面属性

  • contentViewFrameBlock: 全屏、弹窗模式设置,授权页面中,渲染并显示所有空间的view,称为content view,不实现该block默认为全屏模式。

    * 实现弹窗的方案 x > 0 || y > 0, width < 屏幕宽度 || height < 屏幕高度。

  • supportedInterfaceOrientations: 横屏、竖屏模式设置,注意:在刘海屏,UIInterfaceOrientationMaskPortraitUpsideDown 属性慎用!

  • presentDirection: 授权页面弹出方向,默认PNSPresentationDirectionBottom。

标题栏

  • alertTitleBarColor: 标题栏背景颜色。

  • alertBarIsHidden: 标题栏是否隐藏。

  • alertTitle: 标题栏标题内容、大小、颜色。

  • alertCloseImage: 标题栏右侧关闭按钮图片设置。

  • alertCloseItemIsHidden: 标题栏右侧关闭按钮是否显示,默认NO。

  • alertTitleBarFrameBlock: 构建标题栏的frame,view布局或布局发生变化时调用,不实现则按默认处理,实现时仅有height生效。

  • alertTitleFrameBlock: 构建标题栏的frame,view布局或布局发生变化时调用,不实现则按默认处理。

  • alertCloseItemFrameBlock: 构建标题栏右侧关闭按钮的frame、view布局或布局发生变化时调用,不实现则按默认处理。

其他弹窗页面属性

  • contentViewFrameBlock: 全屏、弹窗模式设置,授权页面中,渲染并显示所有空间的view,称content view,不实现该block默认为全屏模式。

  • supportedInterfaceOrientations: 横屏、竖屏模式设置,注意:在刘海屏,UIInterfaceOrientationMaskPortraitUpsideDown 属性慎用!

  • presentDirection: 授权页面弹出方向,默认PNSPresentationDirectionBottom。

  • alertBlurViewColor: 底部蒙层背景色,默认黑色。

  • alertBlurViewAlpha: 底部蒙层背景透明度,默认0.5。

  • alertCornerRadiusArray: contentView的四个圆角值,顺序为左上、左下、右下、右上,需要填充4个值,不足4个值则无效,如果值<=0则为直角。

  • setTapAuthPageMaskClosePage:设置授权页弹窗模式,点击非弹窗区域关闭授权页。true,关闭。false,不关闭。

配置二次隐私协议弹窗页面

方法

参数类型

说明

privacyAlertIsNeedShow

BOOL

设置二次隐私协议弹窗是否显示。取值:

  • NO(默认值):表示不显示。

  • YES:表示显示。

说明

针对弹窗形式的授权页暂时不支持二次隐私协议弹窗页面。

privacyAlertIsNeedAutoLogin

BOOL

设置二次隐私协议弹窗点击按钮是否需要执行登录。取值:

  • NO:表示不需要执行登录。

  • YES(默认值):表示需要执行登录。

privacyAlertEntryAnimation

CAAnimation

设置二次隐私协议弹窗显示自定义动画,默认从下往上位移动画。

privacyAlertExitAnimation

CAAnimation

设置二次隐私协议弹窗隐藏自定义动画,默认从上往下位移动画。

privacyAlertCornerRadiusArray

NSArray<NSNumber *>

设置二次隐私协议弹窗的四个圆角值。

说明

顺序为左上,左下,右下,右上,需要填充4个值,不足4个值则无效,如果值小于等于0则为直角。

privacyAlertBackgroundColor

UIColor

设置二次隐私协议弹窗背景颜色。

privacyAlertAlpha

CGFloat

设置二次隐私协议弹窗透明度,默认值1.0。

说明

设置范围0.3~1.0。

privacyAlertTitleFont

UIFont

设置二次隐私协议弹窗标题文字大小。

privacyAlertTitleColor

UIColor

设置二次隐私协议弹窗标题文字颜色。

privacyAlertTitleBackgroundColor

UIColor

设置二次隐私协议弹窗标题背景颜色。

privacyAlertTitleAlignment

NSTextAlignment

设置二次隐私协议弹窗标题位置,默认居中。

privacyAlertContentFont

UIFont

设置二次隐私协议弹窗协议内容文字大小,默认值13 dp,最小值12 dp。

privacyAlertContentBackgroundColor

UIColor

设置二次隐私协议弹窗协议内容背景颜色。

privacyAlertContentColors

NSArray<UIColor *>

设置二次隐私协议弹窗协议内容颜色数组。

说明

默认值[#999999,#1890FF],[非点击文案颜色,点击文案颜色]。

privacyAlertContentAlignment

NSTextAlignment

设置二次隐私协议弹窗协议文案居中、居左,默认居左。

privacyAlertBtnBackgroundImages

NSArray<UIImage *>

设置二次隐私协议弹窗按钮背景图片。

privacyAlertButtonTextColors

NSArray<UIColor *>

设置二次隐私协议弹窗按钮文字颜色。

privacyAlertButtonFont

UIFont

设置二次隐私协议弹窗按钮文字大小,默认值18 dp,最小值10 dp。

privacyAlertCloseButtonIsNeedShow

BOOL

设置二次隐私协议弹窗关闭按钮是否显示。

  • NO:表示隐藏。

  • YES(默认值):表示显示。

privacyAlertCloseButtonImage

UIImage

设置二次隐私协议弹窗右侧关闭按钮图片。

privacyAlertMaskIsNeedShow

BOOL

设置二次隐私协议弹窗背景蒙层是否显示。

  • NO:表示隐藏。

  • YES(默认值):表示显示。

tapPrivacyAlertMaskCloseAlert

BOOL

设置二次隐私协议弹窗点击背景蒙层是否关闭弹窗。

  • NO:表示不关闭。

  • YES(默认值):表示关闭。

privacyAlertMaskColor

UIColor

设置二次隐私协议弹窗蒙版背景颜色。

privacyAlertMaskAlpha

CGFloat

设置二次隐私协议弹窗蒙版透明度,默认值0.5。

说明

设置范围0.3~1.0。

privacyAlertOperatorColor

UIColor

二次隐私协议弹窗协议运营商协议内容颜色,优先级最高,如果privacyAlertOperatorColors不设置,则取privacyAlertContentColors中的点击文案颜色,privacyAlertContentColors不设置,则是默认色。

privacyAlertOneColor

UIColor

二次隐私协议弹窗协议1内容颜色,优先级最高,如果privacyAlertOneColors不设置,则取privacyAlertContentColors中的点击文案颜色,privacyAlertContentColors不设置,则是默认色。

privacyAlertTwoColor

UIColor

二次隐私协议弹窗协议2内容颜色,优先级最高,如果privacyAlertTwoColors不设置,则取privacyAlertContentColors中的点击文案颜色,privacyAlertContentColors不设置,则是默认色。

privacyAlertThreeColor

UIColor

二次隐私协议弹窗协议3内容颜色,优先级最高,如果privacyAlertThreeColors不设置,则取privacyAlertContentColors中的点击文案颜色,privacyAlertContentColors不设置,则是默认色。

privacyAlertPreText

NSString

二次隐私协议弹窗协议整体文案,前缀部分文案,如果不赋值,默认使用privacyPreText。

privacyAlertSufText

NSString

二次隐私协议弹窗协议整体文案,后缀部分文案,如果不赋值,默认使用privacySufText。

privacyAlertMaskEntryAnimation

CAAnimation

设置二次隐私协议弹窗蒙版显示动画,默认渐显动画。

privacyAlertMaskExitAnimation

CAAnimation

设置二次隐私协议弹窗蒙版消失动画,默认渐隐动画。

privacyAlertFrameBlock

PNSBuildFrameBlock

设置二次隐私协议弹窗尺寸。

说明

默认值:20 px,(SH-100)*0.5 px,(SW-40) px,100 px。不能超出父视图,高度不小于50 px,宽度不小于0 px。

privacyAlertTitleFrameBlock

PNSBuildFrameBlock

设置二次隐私协议弹窗标题尺寸。

说明

不能超出父视图,最小宽度100 px,最小高度15 px。

privacyAlertPrivacyContentFrameBlock

PNSBuildFrameBlock

设置二次隐私协议弹窗内容尺寸。

说明

从标题顶部位置开始,不能超出父视图。

privacyAlertButtonFrameBlock

PNSBuildFrameBlock

设置二次隐私协议弹窗确认并继续按钮尺寸。

说明

居中显示,不能超出父视图。最小宽度40 px,最小高度20 px。

privacyAlertCloseFrameBlock

PNSBuildFrameBlock

设置二次隐私协议弹窗右侧关闭按钮尺寸。

说明

不能超出父视图。

privacyAlertBtnContent

NSString

二次隐私协议弹窗按钮文字内容。默认值:同意。

privacyAlertTitleContent

NSString

二次隐私协议弹窗标题文字内容。默认值:请阅读并同意以下条款。

privacyAlertCustomViewBlock

Block

二次授权页弹窗自定义控件添加。

privacyAlertCustomViewLayoutBlock

Block

二次授权页设置自定义添加控件的frame。