FinClip为企业提供小程序生态圈技术产品,开发者可在FinClip小程序开发帮助中心找到相关FinClip小程序指引

# iOS API

这里介绍 FinClip 小程序 SDK 提供了哪些能力,以及API的详细说明和使用示例。

# 1. 基础API

# 1.1 SDK初始化

在使用小程序的API之前,需要先初始化小程序SDK。只有成功初始化之后,才能使用SDK提供的API,否则 API都无法调用。

初始化SDK的API如下:

/// 初始化SDK
/// @param config 配置对象
/// @param error 初始化失败时返回的error
- (BOOL)initWithConfig:(FATConfig *)config error:(NSError **)error;

/// 初始化SDK
/// @param config 配置对象
/// @param uiConfig UI配置对象
/// @param error 初始化失败时返回的error
- (BOOL)initWithConfig:(FATConfig *)config uiConfig:(FATUIConfig *)uiConfig error:(NSError **)error;

初始化示例:

FATStoreConfig *storeConfig = [[FATStoreConfig alloc] init];
storeConfig.sdkKey = @"这里填上的SDK Key";
storeConfig.sdkSecret = @"这里填上SDK secret";
storeConfig.apiServer = @"这里填上你们的服务器地址"; // 例如:https://api.finclip.com;
storeConfig.apiPrefix = @"/api/v1/mop";
FATConfig *config = [FATConfig configWithStoreConfigs:@[storeConfig]];
    
[[FATClient sharedClient] initWithConfig:config error:nil];

注意

  1. 从2.13.109版本开始,FinClip SDK 支持配置多个服务器信息,可以同时打开多个不同服务器上的小程序,所以我们提供了配置多个服务器信息的方式。
  2. configWithAppSecret:appKey:未来会弃用,请尽早替换为configWithStoreConfigs的方式构造FATConfig。
  3. 自2.34.1开始,不再需要配置apiPrefix。

以下是配置多个服务器初始化SDK的示例:

NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"servers" ofType:@"plist"];
NSArray *array = [NSArray arrayWithContentsOfFile:plistPath];
NSMutableArray *storeArrayM = [NSMutableArray array];
for (NSDictionary *dict in array) {
    FATStoreConfig *storeConfig = [[FATStoreConfig alloc] init];
    storeConfig.sdkKey = dict[@"sdkKey"];
    storeConfig.sdkSecret = dict[@"sdkSecret"];
    storeConfig.apiServer = dict[@"apiServer"];
    storeConfig.apiPrefix = dict[@"apiPrefix"];
    storeConfig.apmServer = dict[@"apmServer"];
    if ([@"SM" isEqualToString:dict[@"cryptType"]]) {
        storeConfig.cryptType = FATApiCryptTypeSM;
    } else {
        storeConfig.cryptType = FATApiCryptTypeMD5;
    }
    
    [storeArrayM addObject:storeConfig];
}
FATConfig *config = [FATConfig configWithStoreConfigs:storeArrayM];
[[FATClient sharedClient] initWithConfig:config error:nil];

FATConfig中的配置项:

配置名称 配置描述
currentUserId 当前用户唯一标识,小程序缓存信息会存储在以userId命名的不同目录下
disableAuthorize 是否禁止SDK触发权限申请,默认为NO,如果设置为YES,则SDK内使用权限的api,不会主动申请权限。
appletAutoAuthorize 是否自动向SDK申请权限,默认为NO,申请权限时会弹出对话框让用户确认是否允许申请,如果设置为YES,自动向SDK申请权限
disableGetSuperviseInfo 是否禁用SDK的监管接口API,默认为NO,如果设置为YES,则SDK禁用监管接口API(getSuperviseInfo)
appletIntervalUpdateLimit 后台自动检查更新的小程序个数,取值范围:0~50。0代表不检查更新;不设置默认是3。
startCrashProtection 是否开启Crash防崩溃。UnrecognizedSelector、KVO、Notification、Timer、Container(数组越界,字典插入nil等)、String (越界、nil等)
enableApmDataCompression apm数据上报时,是否压缩数据
encryptServerData 平台接口返回的数据是否要加密
enableAppletDebug 是否开启全局debug模式,设置为YES后所有小程序都会显示vconsole,如果设置为NO,则开发版、体验版、IDE预览版可通过 更多菜单开启Debug模式
enableH5AjaxHook 是否开启小程序中加载的H5页面hook功能,非必填。详情见api中的注释
h5AjaxHookRequestKey 开启enableH5AjaxHook后,会hook request请求,可设置requestKey
pageCountLimit 小程序中页面栈的最大限制。默认值为0,表示不限制。

FATUIConfig中的配置项:

配置名称 配置描述
navigationTitleTextAttributes 导航栏标题的样式,目前仅支持字体的配置。
capsuleConfig 右上角胶囊的样式配置,具体参数见FATCapsuleConfig表
navHomeConfig 返回首页按钮的配置的样式配置,具体参数见FATNavHomeConfig表
progressBarColor 小程序中的web-view组件加载链接时的进度条颜色
moreMenuStyle 胶囊里更多按钮弹出的菜单视图的样式
hideForwardMenu 是否隐藏更多菜单中的转发按钮。
hideSettingMenu 是否隐藏小程序更多菜单中的设置菜单。
hideFeedbackMenu 是否隐藏更多菜单中的投诉反馈按钮。
hideBackToHome 是否隐藏导航栏中回到首页按钮。
autoAdaptDarkMode 是否适配暗黑模式。
appendingCustomUserAgent 您需要添加至userAgent中的内容。
appletText 默认为“小程序”,您需要替换展示的名称。比如设置会"轻应用",所有显示"小程序"的地方,都会变为"轻应用"
hideTransitionCloseButton 是否隐藏转场页的关闭按钮。默认为NO
disableSlideCloseAppletGesture 是否禁用侧滑关闭小程序的手势。默认为NO

FATCapsuleConfig(胶囊)中的配置项:

配置名称 配置描述
capsuleWidth 胶囊的宽度
capsuleHeight 胶囊的高度
capsuleRightMargin 胶囊的右边框距离屏幕右边框的距离
capsuleCornerRadius 胶囊的圆角半径
capsuleBorderWidth 胶囊的边框宽度
capsuleBorderLightColor 胶囊的浅色边框颜色
capsuleBorderDarkColor 胶囊的深色边框颜色
capsuleBgLightColor 胶囊的浅色下背景颜色
capsuleBgDarkColor 胶囊的深色下背景颜色
moreLightImage 胶囊的浅色更多按钮图片
moreDarkImage 胶囊的深色更多按钮图片
moreBtnWidth 胶囊的更多按钮宽度
moreBtnLeftMargin 更多按钮的左边距
closeLightImage 胶囊里的深色关闭按钮图片
closeDarkImage 胶囊里的深色关闭按钮图片
closeBtnWidth 胶囊里关闭按钮的宽度
closeBtnLeftMargin 胶囊里关闭按钮的左边距
capsuleDividerLightColor 胶囊里浅色分割线颜色
capsuleDividerDarkColor 胶囊里的深色分割线颜色

FATNavHomeConfig(返回首页按钮)中的配置项:

配置名称 配置描述
width 返回首页按钮的宽度
height 返回首页按钮的高度
leftMargin 返回首页按钮的左侧距离屏幕左边框的距离
cornerRadius 返回首页按钮的圆角半径
borderWidth 返回首页按钮的边框宽度
lightImage 返回首页按钮的浅色按钮图片
darkImage 返回首页按钮的深色按钮图片
borderLightColor 返回首页按钮的浅色边框颜色
borderDarkColor 返回首页按钮的深色边框颜色
bgLightColor 返回首页按钮的浅色背景颜色
bgDarkColor 返回首页按钮的深色背景颜色

我们的默认胶囊是这样:

image.png

那么,我们只需要这样设置,就可以变成圆角:

FATUIConfig *uiConfig = [[FATUIConfig alloc] init];
uiConfig.capsuleConfig.capsuleCornerRadius = 15.5;

效果如下:

image.png

# 1.2 打开小程序

打开小程序,其实是有两个场景:1.普通的打开小程序,这里一般只需要小程序id和服务器地址即可,只能打开上架的线上版本小程序;2.加密信息打开小程序,这个场景是扫描小程序平台上的二维码,得到二维码里的内容,然后使用二维码里的info(即加密信息)打开小程序。

# 1.2.1 普通打开小程序

打开小程序时,会先判断本地是否有缓存的小程序,如果没有,则会自动从远程服务器上下载小程序,然后打开。如果有缓存的小程序,则会先打开本地小程序,然后在后台校验服务器端是否有新版本。

如果有新版本,则下载新版小程序,下次打开时,就会使用新版小程序;如果没有新版本,则什么也不做。

/// 启动小程序
/// @param request 启动的request
/// @param parentVC 父页面
/// @param completion 完成回调
/// @param closeCompletion 关闭小程序时的回调
- (void)startAppletWithRequest:(FATAppletRequest *)request
        InParentViewController:(UIViewController *)parentVC
                    completion:(void (^)(BOOL result, FATError *error))completion
               closeCompletion:(dispatch_block_t)closeCompletion;

示例代码:

FATAppletRequest *request = [[FATAppletRequest alloc] init];
request.appletId = @"小程序id"; // 必填项
request.apiServer = @"服务器地址"; // 必填项
request.transitionStyle = FATTranstionStyleUp;
request.startParams = startParams;
    
[[FATClient sharedClient] startAppletWithRequest:request InParentViewController:self completion:^(BOOL result, FATError *error) {
    NSLog(@"打开小程序:%@", error);
} closeCompletion:^{
    NSLog(@"关闭小程序");
}];

# 1.2.2 二维码打开小程序

这种情况流程一般会复杂一些,需要先扫描FinClip小程序开放平台上的二维码,得到二维码里的内容,然后使用二维码内容调用该接口打开小程序。

/// 二维码信息启动小程序
/// @param request 请求对象
/// @param parentVC 父页面
/// @param requestBlock 校验二维码的请求完成的回调
/// @param completion 完成的回调
/// @param closeCompletion 关闭小程序时的回调
- (void)startAppletWithQrCodeRequest:(FATAppletQrCodeRequest *)request
              inParentViewController:(UIViewController *)parentVC
                        requestBlock:(void (^)(BOOL result, FATError *error))requestBlock
                          completion:(void (^)(BOOL result, FATError *error))completion
                     closeCompletion:(dispatch_block_t)closeCompletion;

示例代码:

FATAppletQrCodeRequest *qrcodeRequest = [[FATAppletQrCodeRequest alloc] init];
qrcodeRequest.qrCode = qrCode;
[[FATClient sharedClient] startAppletWithQrCodeRequest:qrcodeRequest inParentViewController:self requestBlock:^(BOOL result, FATError *error) {
    NSLog(@"请求完成:%@", error);
} completion:^(BOOL result, FATError *error) {
    NSLog(@"打开完成:%@", error);
} closeCompletion:^{
    NSLog(@"关闭");
}];

# 1.3 关闭小程序

# 1.3.1 关闭单个小程序

/**
关闭打开的指定小程序
@param animated 是否显示动画
@param completion 关闭完成的回调
*/
- (void)closeApplet:(NSString *)appletId animated:(BOOL)animated completion:(dispatch_block_t)completion;

# 1.3.2 关闭所有小程序

有些场景下,可能存在A小程序打开B小程序,B小程序打开C小程序的情况,这时想要关闭打开的所有小程序,可以使用该方法。

/**
关闭当前打开的所有小程序
@param completion 关闭完成的回调
*/
- (void)closeAllAppletsWithCompletion:(dispatch_block_t)completion;

# 1.3.3 关闭当前小程序

/**
关闭当前的小程序
@param animated 是否显示动画
@param completion 关闭完成的回调
*/
- (void)closeCurrentApplet:(BOOL)animated completion:(dispatch_block_t)completion;

# 1.3.4 其他关闭小程序

以下API都是上述api的旧版本,因为缺少了completion回调,所以未来可能会弃用。

/**
关闭打开的指定小程序
@param animated 是否显示动画
*/
- (void)closeApplet:(NSString *)appletId animated:(BOOL)animated;

/**
关闭当前打开的所有小程序
*/
- (void)closeAllApplets;

/**
关闭当前的小程序
@param animated 是否显示动画
*/
- (void)closeCurrentApplet:(BOOL)animated;

# 1.4 结束小程序

小程序被关闭后,并没有真的结束,而是在后台挂起。等下次打开小程序时,会立即将小程序切换至前台运行。 所以,如果我们希望小程序关闭后,真的被结束掉,可以根据实际情况调用以下API来结束单个小程序或所有小程序。

# 1.4.1 结束单个小程序

/**
删除内存中的某个小程序
*/
- (void)clearMemeryApplet:(NSString *)appletId;

# 1.4.2 结束所有小程序

/**
 清空内存中缓存的小程序数据
 */
- (void)clearMemoryCache;

# 1.5 删除小程序

由于小程序的运行,会将小程序包和小程序信息缓存在本地,以后打开时速度会非常快。 所以,如果想要将小程序的所有信息都删除,那么可以调用以下api删除某个小程序或者删除所有小程序。

# 1.5.1 删除单个小程序

/**
 从本地缓存中删除小程序
 
 @param appletId 小程序id
 @return BOOL 结果
 */
- (BOOL)removeAppletFromLocalCache:(NSString *)appletId;

# 1.5.2 删除所有小程序

///  删除本地缓存的小程序
- (void)clearLocalApplets;

# 1.6 搜索小程序

要搜索小程序,需要两步:

  1. 确保在初始化SDK的时候,配置了该服务器信息;
  2. 调用该搜索接口搜索小程序
/// 搜索小程序
/// @param request 搜索的request
/// @param completion 搜索结果
- (void)searchAppletsWithRequest:(FATSearchAppletRequest *)request
                      completion:(void (^)(NSDictionary *result, FATError *aError))completion;

搜索小程序的示例:

FATSearchAppletRequest *searchRequest = [[FATSearchAppletRequest alloc] init];
searchRequest.apiServer = @"https://api.finclip.com";
searchRequest.text = @"小程序";
[[FATClient sharedClient] searchAppletsWithRequest:searchRequest completion:^(NSDictionary *result, FATError *aError) {
    NSLog(@"");
}];

# 1.7 获取小程序对象信息

/// 获取当前正在运行的小程序对象
- (FATAppletInfo *)currentApplet;

# 1.8 获取小程序当前WebView信息

# 1.8.1 获取当前webView的URL

如果当前页面加载的不是H5,则会返回nil;如果加载的是H5,则会返回H5对应的NSURL。

/**
获取当前加载H5的URL
如果小程序当前页面加载的不是H5,则返回nil
*/
- (NSURL *)getCurrentWebViewURL;

# 1.8.2 获取当前webView的UserAgent

/**
获取小程序当前页面webView的userAgent
*/
- (void)getCurrentWebViewUserAgentWithCompletion:(void (^)(NSString *userAgent, NSError * error))completionHandler;

# 1.9 获取小程序页面截图

注意:这里生成的截图的宽高比是5:4。会从导航栏以下(0,0)绘制开始截取。

/**
 生成当前页面截图
 */
- (UIImage*)getCurrentAppletImage;

# 1.10 获取使用过的小程序列表

获取打开过的所有小程序列表,已删除的小程序不包含。

/**
 获取本地的小程序
 
 @return 小程序数组<FATAppletInfo>
 */
- (NSArray *)getAppletsFromLocalCache;

# 1.11 微信小程序二维码信息转换为FinClip小程序

///  解析微信小程序二维码,得到凡泰小程序信息
/// @param qrCode  微信二维码
/// @param apiServer  解析服务器url
/// @param completion 结果回调
- (void)parseAppletInfoFromWXQrCode:(NSString *)qrCode apiServer:(NSString *)apiServer completion:(void (^)(FATAppletSimpleInfo *appInfo, FATError *aError))completion;

该API的使用场景比较特别,当某小程序在微信和FinClip服务上都上架之后,可以在FinClip平台关联微信的小程序。这时FinClip平台上的线上版二维码,既可以用微信扫码打开,也可以用凡泰助手或其他集成了FinClip SDK的APP扫码打开。

流程是先扫描该二维码,得到二维码内容,然后调用该接口获取FinClip 小程序id,最后调用打开小程序的API即可。

# 1.12 代理方法

小程序的部分功能,需要原生端去实现才能调用,比如转发和获取主体App的用户信息等。

这些代理方法都声明在FATAppletDelegate中

/// 小程序灰度扩展参数
/// @param appletId 小程序id
- (NSDictionary *)grayExtensionWithAppletId:(NSString *)appletId;

#pragma mark - button open-type

/** 转发事件
 当你点击小程序右上角更多里的转发菜单,或者 点击open-type属性为share的Button时,会触发小程序里shareAppMessage方法,然后回调到原生该方法
 @param contentInfo 小程序相关信息,里面包含小程序id、小程序名称、小程序图标、小程序截图(5:4)等。
 {
    appAvatar = "小程序图标地址";
    appDescription = "小程序的描述信息";
    appId = "小程序id";
    appInfo =  {}; // 客户可在appInfo中自定义字段,appInfo内容会透传
    appStartParams =     {
        path = "点击转发时的小程序页面路径";
    };
    appThumbnail = "小程序封面图的路径,可能是网络路径或者本地路径,宽高比是5:4";
    appTitle = "小程序名称";
    userId = "小程序开发者id";
}
 @param completion 执行完后的回调,如果你转发操作执行完后,希望告知小程序端转发结果,就需要调用该block。
 */
- (void)forwardAppletWithInfo:(NSDictionary *)contentInfo completion:(void (^)(FATExtensionCode code, NSDictionary *result))completion;

/// 调用获取用户信息API(getUserInfo) 或者 点击open-type属性为getUserInfo的Button时触发
/// @param appletInfo 小程序信息
- (NSDictionary *)getUserInfoWithAppletInfo:(FATAppletInfo *)appletInfo;

/// Button open-type属性为contact,打开客服会话。
/// @param appletInfo 小程序信息
/// @param sessionFrom 会话来源
/// @param sendMessageTitle 会话内消息卡片标题
/// @param sendMessagePath 会话内消息卡片点击跳转小程序路径
/// @param sendMessageImg 会话内消息卡片图片
/// @param showMessageCard 小程序信息
- (void)contactWithAppletInfo:(FATAppletInfo *)appletInfo sessionFrom:(NSString *)sessionFrom sendMessageTitle:(NSString *)sendMessageTitle sendMessagePath:(NSString *)sendMessagePath sendMessageImg:(NSString *)sendMessageImg showMessageCard:(BOOL)showMessageCard;

/// Button open-type属性为getPhoneNumber,获取用户手机号。
/// @param appletInfo 小程序信息
/// @param bindGetPhoneNumber 获取用户手机号回调
- (void)getPhoneNumberWithAppletInfo:(FATAppletInfo *)appletInfo bindGetPhoneNumber:(void (^)(NSDictionary *result))bindGetPhoneNumber;

/// Button open-type属性为launchApp,打开APP。
/// @param appletInfo 小程序信息
/// @param appParameter 打开 APP 时,向 APP 传递的参数
/// @param bindError 打开 APP 发生错误的回调
/// @param bindLaunchApp 打开 APP 成功的回调
- (void)launchAppWithAppletInfo:(FATAppletInfo *)appletInfo appParameter:(NSString *)appParameter bindError:(void (^)(NSDictionary *result))bindError bindLaunchApp:(void (^)(NSDictionary *result))bindLaunchApp;

/// Button open-type属性为feedback,打开“意见反馈”页面。(APP 未实现时,则打开菜单栏里面的反馈)
/// @param appletInfo 小程序信息
- (void)feedbackWithAppletInfo:(FATAppletInfo *)appletInfo;

/// Button open-type属性为chooseAvatar,获取用户头像。
/// @param appletInfo 小程序信息
/// @param bindChooseAvatar 获取用户头像回调
- (void)chooseAvatarWithAppletInfo:(FATAppletInfo *)appletInfo bindChooseAvatar:(void (^)(NSDictionary *result))bindChooseAvatar;

#pragma mark - applet open and close

/**
 小程序打开完成的事件
 @param appletId 小程序id
 @param error 错误对象
 */
- (void)applet:(NSString *)appletId didOpenCompletion:(NSError *)error;

/**
 小程序关闭完成的事件
 @param appletId 小程序id
 @param error 错误对象
 */
- (void)applet:(NSString *)appletId didCloseCompletion:(NSError *)error;

/**
 小程序初始化完成,首页加载出来的事件
 @param appletId 小程序id
 @param error 错误对象
 */
- (void)applet:(NSString *)appletId initCompletion:(NSError *)error;

/**
 小程序进入活跃状态的事件
 @param appletId 小程序id
 @param error 错误对象
 */
- (void)applet:(NSString *)appletId didActive:(NSError *)error;

/**
 小程序进入非活跃状态的事件
 @param appletId 小程序id
 @param error 错误对象
 */
- (void)applet:(NSString *)appletId resignActive:(NSError *)error;

/**
 小程序出错的事件
 @param appletId 小程序id
 @param error 错误对象
 */
- (void)applet:(NSString *)appletId didFail:(NSError *)error;

/**
 小程序被销毁的事件
 @param appletId 小程序id
 @param error 错误对象
 */
- (void)applet:(NSString *)appletId dealloc:(NSError *)error;

#pragma mark - more menu

/**
 右上角胶囊中 【...】的点击事件,可在该事件中弹出自己设计的更多视图。
 因此实现了该代理事件,就不会触发下面两个自定义菜单的事件
 @param appletInfo 小程序信息
 @path 小程序页面路径,示例:pages/index/index
 */
- (void)applet:(FATAppletInfo *)appletInfo didClickMoreBtnAtPath:(NSString *)path;

/**
 更多按钮中自定义的菜单,会在页面弹出菜单时调用该api
 @param appletInfo 小程序信息
 @param path 页面路径
 */
- (NSArray<id<FATAppletMenuProtocol>> *)customMenusInApplet:(FATAppletInfo *)appletInfo atPath:(NSString *)path;

/**
 点击自定义菜单时,会触发的事件(新版)
 只有实现了该代理方法,才会触发【-clickCustomItemMenuWithInfo:completion:】
 @param contentInfo 分享信息
 @param appletInfo 小程序信息
 @param completion 分享回调(小程序分享回调:1.【code】回调状态码;2.【result】回传给小程序的回调信息)
 */
- (void)clickCustomItemMenuWithInfo:(NSDictionary *)contentInfo inApplet:(FATAppletInfo *)appletInfo completion:(void (^)(FATExtensionCode code, NSDictionary *result))completion;

代理实现示例:

1.在合适的位置赋值实现代理方法的实例对象

[FATClient sharedClient].delegate = [FATClientHelper shareInstance];

2.创建实现代理方法的类

#import <Foundation/Foundation.h>
#import <FinApplet/FinApplet.h>

@interface FATClientHelper : NSObject<FATAppletDelegate>

+ (instancetype)shareInstance;

@end

3.在实现类(比如FATClientHelper)中实现对应的代理方法。

# 1.13 处理Open URL

/*
 处理URL
 @param URL 具体的URL路由
 URL格式:${scheme}://applet/appid/${appId}?path=${path}&query=${encode过的queryDict}&apiServer=${apiServer}
 例如:fatae55433be2f62915://applet/appid/617bb42f530fb30001509b27?path=/packages/d/index&query=key%3Dvalue%26name%3Dtable&apiServer=https://www.finclip.com/
 其中,必须有的是scheme和appId,如:fatae55433be2f62915://applet/appid/617bb42f530fb30001509b27
 */
- (BOOL)handleOpenURL:(NSURL *)URL;

如果希望在Safari浏览器或其他App中打开自己的APP中的小程序,需要实现- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options方法。

示例代码:

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
    if ([[FATClient sharedClient] handleOpenURL:url]) {
        return YES;
    }
    return YES;
}
/*
 处理Universal Link
 @param URL Universal Link URL
 URL格式:${UniversalLink}?appid=${appId}&path=${path}&query=${encode过的queryDict}&apiServer=${apiServer}
 例如:https://www.finclip.com/mop/scattered-page/#/mop-download?appid=xx&query=xxx&path=xxx
 其中,必须有的是UniversalLink和appId,如https://www.finclip.com/mop/scattered-page/#/mop-download?appid=xx
 */

- (BOOL)handleOpenUniversalLinkURL:(NSURL *)URL;

同上面的handleOpenURL一样,如果想要支持universal link 的方式打开小程序。也得实现- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray<id<UIUserActivityRestoring>> * __nullable restorableObjects))restorationHandler方法。

示例代码:

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
    if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
        NSURL *url = userActivity.webpageURL;
        NSLog(@"url = %@",url.absoluteString);
        return [[FATClient sharedClient] handleOpenUniversalLinkURL:url];
    }
    return YES;
}

# 1.15 保存文件至当前小程序临时目录

SDK支持原生保存文件到小程序的缓存目录,然后返回文件的本地路径,以供小程序使用。

/**
 保存文件到小程序的缓存路径
 
 @param fileData 文件的二进制数据
 @param fileName 文件名,需要保证文件名唯一,否则可能会覆盖
 @return 文件的本地路径,例如:finfile://tmp_fdfdkfjdkfjdkjfdkjf.jgp
 */
- (NSString *)saveFile:(NSData *)fileData fileName:(NSString *)fileName;

# 1.16 小程序文件路径转换为绝对路径

在有些场景下,我们拿到小程序的文件路径,无法获取到文件数据,这时可以用该方法转换为绝对路径。

比如,在使用小程序的转发功能时,返回的图片路径是小程序文件路径,可以用该方法转换为绝对路径,然后获取到图片数据再去调起第三方分享。 再比如,自定义api里,可以传递小程序文件路径的参数,宿主app用该方法转换为绝对路径后,即可拿到文件数据。

/**
将文件路径转为绝对路径
如果是 网络文件路径,则直接返回。
如果是小程序中的路径,比如finfile://tmp_fdfdkfjdkfjdkjfdkjf.jpg ,则会转为本地绝对路径
如果是小程序包中的文件路径,比如image/xxx.jpg,也会转为绝对路径
其他情况则返回传入的路径

@param path  文件路径
@return 文件的绝对路径
*/
- (NSString *)fat_absolutePathWithPath:(NSString *)path;

# 2. 高级API

# 2.1 注册自定义API

如果小程序里需要调用一些宿主 App 提供的能力,而 FinClip SDK 未实现或无法实现时,就可以通过注册自定义 API 来实现,使得小程序里也能够调用 App 中注册的 API 了。

注册自定义 API 分两个场景:

  1. 注册给原生小程序使用的自定义 API;
  2. 注册给小程序中 WebView 组件加载的 H5 使用的自定义 API。

# 2.1.1 注册小程序异步API

注册自定义的异步API的函数

/**
 注册扩展Api
 
 @param extApiName 扩展的api名称
 @param handler 回调
 @return 返回注册结果
 */
- (BOOL)registerExtensionApi:(NSString *)extApiName handler:(void (^)(FATAppletInfo *appletInfo, id param, FATExtensionApiCallback callback))handler;

比如,我这里注册一个小程序APIfinclipLogin

[[FATClient sharedClient] registerExtensionApi:@"finclipLogin" handler:^(FATAppletInfo *appletInfo, id param, FATExtensionApiCallback callback) {
    // xxxx
    callback(FATExtensionCodeSuccess, nil);
}];

然后,在小程序的根目录创建 FinClipConf.js 文件,配置实例如下:

module.exports = {
  extApi:[
    { //普通交互API
      name: 'finclipLogin', //扩展api名 该api必须Native方实现了
      sync: false, //是否为同步api
      params: { //扩展api 的参数格式,可以只列必须的属性
        url: ''
      }
    }
  ]
}

extApi 是个数组,所以,您可以注册多个自定义API。

最后,在小程序里调用自定义的API,示例代码:

ft.finclipLogin({
    url:'https://www.baidu.com',
    success: function (res) {
        console.log("调用customEvent success");
        console.log(res);
    },
    fail: function (res) {
        console.log("调用customEvent fail");
        console.log(res);
    }
});

# 2.1.2 注册小程序同步API

小程序里调用的API,既有异步API,也有同步API。从2.36.1开始,FinClip SDK也支持注册自定义同步API了。

注册自定义的同步API的函数:

/**
 注册同步扩展Api
 @param syncExtApiName 扩展的api名称
 @param handler 回调
 @return 返回注册结果
 */
- (BOOL)registerSyncExtensionApi:(NSString *)syncExtApiName handler:(NSDictionary *(^)(FATAppletInfo *appletInfo, id param))handler;

比如,我这里注册一个同步的小程序API:

1). 在初始化SDK之后,注册并实现同步的api。

[[FATClient sharedClient] registerSyncExtensionApi:@"finclipTestSync" handler:^NSDictionary *(FATAppletInfo *appletInfo, id param) {
    NSLog(@"%p, param:%@", __func__, param);
    NSDictionary *resultDict = @{
                                 @"content":@"这是同步api返回的内容",
                                 @"title":@"这是同步api返回的标题"
    };
    return resultDict;
}];

3).在小程序的根目录创建 FinClipConf.js 文件,并添加该同步api

module.exports = {
  extApi:[
    { //普通交互API
      name: 'finclipLogin', //扩展api名 该api必须Native方实现了
      sync: false, //是否为同步api
      params: { //扩展api 的参数格式,可以只列必须的属性
        url: ''
      }
    },
    {
        name: 'finclipTestSync',
        sync: true, // 是否为同步api
        params: {
            name:'',
            title:''
        }
    }
  ]
}

4).小程序里调用

const res = ft.finclipTestSync({'name':'张三', 'title':'Finclip'});
console.log(res.title);

注意: 自定义同步api的入参是字典,返回值也必须是字典类型,且内部不能包含无法json化的对象(比如view、自定义model)。 FinClipConf.js中的params声明的参数就必须得在调用的时候传递。比如我上面示例里声明了要有name和title两个参数,如果我使用const res = ft.finclipTestSync({'name':'张三'})const res = ft.finclipTestSync({})const res = ft.finclipTestSync()都会导致报错,无法将事件发送至原生。 所以FinClipConf.js中的params 最好是不加,或者声明为{}。

# 2.1.3 注册小程序web-view组件API

小程序里加载的H5,如果也想调用宿主API的某个能力,就可以利用该方法注册一个API

/// 为HTML 注册要调用的原生 api
/// @param webApiName 原生api名字
/// @param handler 回调
- (BOOL)fat_registerWebApi:(NSString *)webApiName handler:(void (^)(FATAppletInfo *appletInfo, id param, FATExtensionApiCallback callback))handler;

我这里为小程序里的H5注册了一个叫js2AppFunction的方法,

    [[FATClient sharedClient] fat_registerWebApi:@"js2AppFunction" handler:^(FATAppletInfo *appletInfo, id param, FATExtensionApiCallback callback) {
        NSString *name = param[@"name"];
//        id params = param[@"data"];
        if ([name isEqualToString:@"getLocation"]) {
            // 执行定位逻辑
            
            // 返回结果给HTML
            NSDictionary *dict = @{@"errno":@"403", @"errmsg":@"无权限", @"result": @{@"address":@"广东省深圳市南山区航天科技广场"}};
            callback(FATExtensionCodeSuccess, dict);
        } else if ([name isEqualToString:@"getColor"]) {
            // 执行其他逻辑
            
            // 返回结果给HTML
            NSDictionary *dict = @{@"r":@"110",@"g":@"150",@"b":@"150"};
            callback(FATExtensionCodeSuccess, dict);
        }
    }];

在H5内引用我们的桥接JSSDK (opens new window)文件,即可调用上面的注册的方法了。

HTML内调用注册的方法示例:

window.ft.miniProgram.callNativeAPI('js2AppFunction', {name:'getLocation'}, (result) => {
    console.log(result)
});

# 2.2 原生调用JS API

同样的如果宿主App想要调用小程序加载的H5中的某个方法,就可以使用该API。

/**
 原生调用HTML中的JS函数(前台运行的小程序)
 @param eventName 函数名
 @param paramString 函数的参数字典转成的json
 @param pageId webView ID,可不传,默认调用最顶层页面里H5的函数
 @param handler 调用结果回调:error code为FATErrorCodeAppletNotFound,未找到前台运行的小程序
 */
- (void)fat_callWebApi:(NSString *)eventName paramString:(NSString *)paramString pageId:(NSNumber *)pageId handler:(void (^)(id result, NSError *error))handler;

/**
 原生调用HTML中的JS函数(appletId指定的小程序)
 @param eventName 函数名
 @param appletId 小程序id,指定调用的小程序
 @param paramString 函数的参数字典转成的json
 @param pageId webView ID,可不传,默认调用最顶层页面里H5的函数
 @param handler 调用结果回调:error code为FATErrorCodeForegroundAppletNotFound,未找到appletId指定小程序
*/
- (void)fat_callWebApi:(NSString *)eventName applet:(NSString *)appletId paramString:(NSString *)paramString pageId:(NSNumber *)pageId handler:(void (^)(id result, NSError *error))handler;

首先,在H5内引用我们的桥接JSSDK (opens new window)文件。

然后,在HTML里注册好方法,比如方法名叫app2jsFunction

window.ft.miniProgram.registNativeAPIHandler('app2jsFunction', function(res) {
    // app2jsFunction callback
})

最后,原生端调用如下API来调用HTML中的JS函数:

NSString *jsonParams = @""; //这里应该是参数字典转换成的json字符串。
NSNumber *pageId = @(1234); //这里是HTML中传过来的pageId
[[FATClient sharedClient] fat_callWebApi:@"app2jsFunction" paramString:jsonParams pageId:pageId handler:^(id result, NSError *error) {
        
}];

# 2.3 注册原生组件

由于资源有限,livePusher 和livePlayer等原生组件的实现可能需要借助外部的第三方控件,这时候就可以注册原生组件。我们现在支持注册的原生组件有三个:Camera、LivePlayer、LivePusher。

首先,创建组件视图,实现其协议方法。

.h

#import <UIKit/UIKit.h>
#import <FinApplet/FATAppletNativeProtocol.h>

NS_ASSUME_NONNULL_BEGIN

@interface FATNativeView : UIView <FATAppletNativeViewProtocol>
@property (nonatomic, strong) NSNumber *nativeViewId;
@property (nonatomic, strong) NSString *type;

@end

@interface FATNativeCameraView : FATNativeView <FATAppletNativeCameraProtocol>

@end

@interface FATNativeLivePlayerView : FATNativeView <FATAppletNativeLivePlayerProtocol>

@end

@interface FATNativeLivePusherView : FATNativeView <FATAppletNativeLivePusherProtocol>

@end

NS_ASSUME_NONNULL_END

.m

@implementation FATNativeView
+ (UIView *)onCreateView:(NSDictionary *)param {
    return [[self alloc] initWithParam:param];
}

- (instancetype)initWithParam:(NSDictionary *)param {
    CGRect frame = CGRectZero;
    NSDictionary *style = [param objectForKey:@"style"];
    if (style) {
        CGFloat x = [[style objectForKey:@"left"] floatValue];
        CGFloat y = [[style objectForKey:@"top"] floatValue];
        CGFloat height = [[style objectForKey:@"height"] floatValue];
        CGFloat width = [[style objectForKey:@"width"] floatValue];
        frame = CGRectMake(x, y, width, height);
    }
    self = [super initWithFrame:frame];
    if (self) {
        _type = param[@"type"];
        _nativeViewId = param[@"nativeViewId"];
    }
    return self;
}

- (void)onUpdateView:(NSDictionary *)param {
    NSDictionary *style = [param objectForKey:@"style"];
    if (style) {
        CGRect frame = CGRectZero;
        CGFloat x = [[style objectForKey:@"left"] floatValue];
        CGFloat y = [[style objectForKey:@"top"] floatValue];
        CGFloat height = [[style objectForKey:@"height"] floatValue];
        CGFloat width = [[style objectForKey:@"width"] floatValue];
        frame = CGRectMake(x, y, width, height);
        self.frame = frame;
    }
}

- (void)onDestroyView:(NSDictionary *)param {
    NSLog(@"销毁了%@",param);
}


@end


@implementation FATNativeCameraView

- (void)setCameraZoom:(NSDictionary *)param success:(FATNativeCallback)callBack {
    
}

@end

@implementation FATNativeLivePlayerView


@end

@implementation FATNativeLivePusherView


@end

然后,设置组件的视图class

[FATClient sharedClient].nativeViewManager.cameraClass = [FATNativeCameraView class];
[FATClient sharedClient].nativeViewManager.livePlayerClass = [FATNativeLivePlayerView class];
[FATClient sharedClient].nativeViewManager.livePusherClass = [FATNativeLivePusherView class];

原生给nativeView发送消息

[[FATClient sharedClient].nativeViewManager sendEvent:@"eventName" nativeViewId:@(1234) detail:@{} completion:^(id result, FATError *error) {
            
}];

原生给小程序发送全局消息

[[FATClient sharedClient].nativeViewManager sendCustomEventWithDetail:@{} completion:^(id result, FATError *error) {
            
}];

# 2.4 注册自定义菜单

如果,您想往右上角的更多菜单中添加一些自己业务相关的菜单,那么就可以注入自定义的菜单。

首先,自定义一个FATClientHelper类,实现FATAppletDelegate协议。

#import <Foundation/Foundation.h>
#import <FinApplet/FinApplet.h>

@interface FATClientHelper : NSObject<FATAppletDelegate>

+ (instancetype)shareInstance;

@end

然后,在SDK初始化成功后后,设置SDK的代理为 FATClientHelper 对象。

示例代码

[[FATClient sharedClient] initWithConfig:config error:nil];
[FATClient sharedClient].delegate = [FATClientHelper shareInstance];

最后,在FATClientHelper中实现,如下协议方法:

// 返回要注入的菜单对象,菜单对象是要实现 FATAppletMenuProtocol 协议的
// 菜单对象分为两种类型(默认为Common类型):
// 1.OnMiniProgram类型:需要后台配置+小程序配置,取相同menuId的集合显示
// 2.Common类型:配置则显示
// PS: 这里兼容旧版本,若后台未升级版本,或后台升级版本后未配置,则仅需要适配菜单对象的menuId为NSString类型
- (NSArray<id<FATAppletMenuProtocol>> *)customMenusInApplet:(FATAppletInfo *)appletInfo atPath:(NSString *)path
{
    
    FATCustomMenuModel *favModel1 = [[FATCustomMenuModel alloc] init];
    favModel1.menuId = @"WXShareAPPFriends";
    favModel1.menuTitle = @"微信好友";
    favModel1.menuIconImage = [UIImage imageNamed:@"minipro_list_wx_chat"];
    favModel1.menuType = FATAppletMenuStyleOnMiniProgram;

    FATCustomMenuModel *favModel2 = [[FATCustomMenuModel alloc] init];
    favModel2.menuId = @"WXShareAPPMoments";
    favModel2.menuTitle = @"微信朋友圈";
    favModel2.menuIconImage = [UIImage imageNamed:@"minipro_list_wx_monents"];
    favModel2.menuType = FATAppletMenuStyleOnMiniProgram;

    FATCustomMenuModel *favModel3 = [[FATCustomMenuModel alloc] init];
    favModel3.menuId = @"Restart";
    favModel3.menuTitle = @"重启";
    favModel3.menuIconImage = [UIImage imageNamed:@"minipro_list_restart"];
    favModel3.menuType = FATAppletMenuStyleCommon;

    return @[favModel1, favModel2, favModel3];
}

// 点击自定义菜单时的事件(旧版,后台未升级或升级后未配置菜单时使用)
- (void)customMenu:(id<FATAppletMenuProtocol>)customMenu inApplet:(FATAppletInfo *)appletInfo didClickAtPath:(NSString *)path
{
    NSLog(@"点击了%@",customMenu.menuTitle);
    NSLog(@"path = %@",path);
}

// 点击自定义菜单时的事件(新版,后台与小程序配置时使用)
- (void)clickCustomItemMenuWithInfo:(NSDictionary *)contentInfo completion:(void (^)(FATExtensionCode, NSDictionary *))completion 
{
    /**
    contentInfo 中包含
    {
         @"title": @"标题",
         @"description": @"描述",
         @"imageUrl": @"图片路径",
         @"path": @"点击时的小程序页面路径"",
         @"menuId": @"点击的菜单按钮标识",
         @"params": self.params     // 原始参数数据,由小程序提供
    }
    */
    // 小程序回调调用,通知小程序点击事件处理情况
    completion(FATExtensionCodeSuccess, contentInfo);
}

# 2.5 隐藏导航栏

如果想要将小程序页面的导航栏完全隐藏,自己实现导航栏,FinClip小程序SDK也是支持的。

像微信、支付宝、百度等小程序的navigationStyledefault/custom两个值,而FinClip小程序SDK在此基础上,新增了一个hide,即有default/custom/hide三个值。

所以在需要隐藏导航栏的页面,设置navigationStyle​hide即可。

# 2.6 设置灰度发布自定义规则

小程序SDK的FATAppletDelegate中一个协议方法:

- (NSDictionary *)grayExtensionWithAppletId:(NSString *)appletId

实现这个协议方法即可。

示例代码如下:

/// 小程序灰度扩展参数
/// @param appletId 小程序id
- (NSDictionary *)grayExtensionWithAppletId:(NSString *)appletId
{
    NSDictionary *grayExtension = @{@"key1":@"value1"};
//    if ([appletId isEqualToString:@"5e017a61c21ecf0001343e31"]) {
//        grayExtension = @{@"fckey1":@"fcvalue1"};
//    }
    return grayExtension;
}

然后,小程序SDK在加载小程序时,会调用该协议方法获取小程序的灰度扩展参数,透传给后台服务匹配相关规则。

# 2.7 使用路由的形式打开小程序

有些时候,我们希望能在safari加载的网页里或者其第三方app打开自己app中的小程序,就需要实现对应的配置。

首先,确保集成SDK时,配置了URL Type,就是URL Schemes(fat+sdkKey的md5)那里。

然后,确保实现了handleOpenURL

最后,在H5里调用fat{sdkKey的md5}://applet/appid/{小程序id}

示例:fat705b46f78820c7a8://applet/appid/5e017a61c21ecf0001343e31

您可以在safari浏览器的地址栏中输入完成的url(比如:fat705b46f78820c7a8://applet/appid/5e017a61c21ecf0001343e31),然后回车即可打开您的app,然后打开小程序。

# 2.8 原生给小程序发送消息

[[FATClient sharedClient].nativeViewManager sendCustomEventWithDetail:@{} completion:^(id result, NSError *error) {    
}];

# 3. 扩展SDK

扩展SDK 是对核心SDK的补充,所以要使用扩展SDK,也必须依赖核心SDK。 为了保证 SDK 的安全稳定性,将需要权限的API尽可能放到扩展SDK,FinClip 将 SDK 拆分为核心 SDK 与扩展 SDK,后者是前者的补充,因此使用扩展 SDK 也必须依赖核心 SDK。

在扩展 SDK 中,主要包含的功能见 # 3.2 小程序api一览 小节

获取扩展 SDK

您可登录 资源下载中心 (opens new window) 下载 iOS SDK 文件,扩展 SDK 也处于在所下载的压缩包中。

# 3.1 注册扩展API

扩展SDK中的API,需要注册,否则小程序中也无法调用扩展SDK中的API。

[[FATExtClient sharedClient] fat_prepareExtensionApis];

# 3.2 扩展SDK中小程序api一览

一些没那么常用的涉及到权限的api,我们将其放到了扩展SDK里。

扩展SDK(FinAppletExt)中包含的小程序api如下:

api名称 api描述信息
chooseLocation 选择位置。
getLocation 获取位置信息。
openLocation 使用地图查看位置
choosePoi 打开POI列表选择位置
startRecord 开始录音。
stopRecord 停止录音。
RecorderManager 录音管理器以及一些列api

扩展SDK-WebRTC(FinAppletWebRTC):

api名称 api描述信息
webrtc-video组件 组件以及一系列的webrtc的api

扩展SDK-BDMap(FinAppletBDMap):

api名称 api描述信息
Map组件 视图组件
MapContext 一系列的api

扩展SDK-GDMap(FinAppletGDMap):

api名称 api描述信息
Map组件 视图组件
MapContext 一系列的api

注意

1.如果集成了百度地图,并设置了使用百度地图,那么Map组件以及Map的api会使用百度地图实现。 2.如果集成了高德地图,并设置了使用高德地图,那么Map组件以及Map的api会使用高德地图实现。 3.如果既没有集成百度地图也没有集成高德地图,也未设置,那么Map组件以及Map的api会使用系统自带的地图实现。 所以,关于Map组件和相关api,只需要在以上三种方案选一个即可。

扩展SDK-BLE(FinAppletBLE):

api名称 api描述信息
蓝牙-通用 一系列的api
蓝牙-低功耗中心设备 一系列的api
蓝牙-低功耗外围设备 一系列的api
蓝牙-信标 一系列的api

最后,请注意: 由于 剪贴板、通讯录权限比较敏感,且api比较冷门。所以相关的Api,已经从扩展SDK移除。 如果,需要使用(setClipboardData、getClipboardData、addPhoneContact),请参考注入自定义api章节,自行注入实现。

注入的示例如下:

[[FATClient sharedClient] registerExtensionApi:@"setClipboardData" handler:^(FATAppletInfo *appletInfo, id param, FATExtensionApiCallback callback) {
    NSString *data = param[@"data"];
    if (!data) {
        callback(FATExtensionCodeFailure, @{@"errMsg" : @"参数data为空"});
        return;
    }
    
    [UIPasteboard generalPasteboard].string = data;
    NSDictionary *result = @{@"data" : data};
    callback(FATExtensionCodeSuccess, result);
}];
© 2021 凡泰极客

👋🏻 嘿,你好!

「FinClip」是一套基于云原生框架设计的小程序容器。能够让任何移动应用在集成小程序SDK之后,获得可用、安全的小程序运行能力。

>> 点我免费注册体验

查看产品文档
了解与 FinClip 相关的一切信息

产品博客 👈  了解产品更新与核心功能介绍
资源下载 👈  获取小程序 SDK 与开发工具
文档中心 👈  查询 FinClip 小程序开发指南与答疑

商务咨询热线
预约 FinClip 产品介绍,咨询商务报价或私有化部署事宜

400-066-00210755-86967467

获取产品帮助
联系 FinClip 技术顾问,获取产品资料或加入开发者社群

联系线上
人工客服

或 👉  点击这里,提交咨询工单