# API/组件自定义
# 1. 注册自定义API
如果小程序里需要调用一些宿主 App 提供的能力,而 “人民网+”小程序 SDK 未实现或无法实现时,就可以通过注册自定义 API 来实现,使得小程序里也能够调用 App 中注册的 API 了。
注册自定义 API 分两个场景:
- 注册给原生小程序使用的自定义 API;
- 注册给小程序中 WebView 组件加载的 H5 使用的自定义 API。
# 1.1 注册小程序异步API
注册自定义的异步API的函数
/**
注册扩展Api
@param extApiName 扩展的api名称
@param handler 回调
@return 返回注册结果
*/
- (BOOL)registerExtensionApi:(NSString *)extApiName handler:(void (^)(FATAppletInfo *appletInfo, id param, FATExtensionApiCallback callback))handler;
比如,我这里注册一个自定义Login
,以便小程序中可直接使用。
首先,App里集成SDK后,注册自定义的api:
[[FATClient sharedClient] registerExtensionApi:@"Login" handler:^(FATAppletInfo *appletInfo, id param, FATExtensionApiCallback callback) {
// xxxx
callback(FATExtensionCodeSuccess, nil);
}];
然后,在小程序的根目录创建 FinClipConf.js
文件,配置示例如下:
module.exports = {
extApi:[
{ //普通交互API
name: 'Login', //扩展api名 该api必须Native方实现了
sync: false, //是否为同步api
params: { //扩展api 的参数格式,可以只列必须的属性
url: ''
}
}
]
}
注意:extApi 是个数组,所以,您可以注册多个自定义API。
最后,在小程序里调用自定义的API,示例代码:
ft.Login({
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);
}
});
# 1.2 注册小程序同步API
小程序里使用的API,既有异步API,也有同步API。从2.36.1开始,“人民网+”小程序 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;
}];
2).在小程序的根目录创建 FinClipConf.js
文件,并添加该同步api
module.exports = {
extApi:[
{ //普通交互API
name: 'Login', //扩展api名 该api必须Native方实现了
sync: false, //是否为同步api
params: { //扩展api 的参数格式,可以只列必须的属性
url: ''
}
},
{
name: 'TestSync',
sync: true, // 是否为同步api
params: {
name:'',
title:''
}
}
]
}
3).小程序里调用
const res = ft.TestSync({'name':'张三', 'title':'“人民网+”'});
console.log(res.title);
注意: 自定义同步api的入参是字典,返回值也必须是字典类型,且内部不能包含无法json化的对象(比如view、自定义model)。 FinClipConf.js中的params声明的参数就必须得在调用的时候传递。比如我上面示例里声明了要有name和title两个参数,如果我使用
const res = ft.TestSync({'name':'张三'})
、const res = ft.TestSync({})
、const res = ft.TestSync()
都会导致报错,无法将事件发送至原生。 所以FinClipConf.js中的params 最好是不加,或者声明为{}。
# 1.3 注册 JS API
小程序里可使用web-view组件加载H5,如果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文件,即可调用上面的注册的方法了。
HTML内调用注册的方法示例:
window.ft.miniProgram.callNativeAPI('js2AppFunction', {name:'getLocation'}, (result) => {
console.log(result)
});
# 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文件。
然后,在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) {
}];
# 3. 注册原生组件
由于资源有限,livePusher 和livePlayer等原生组件的实现可能需要借助外部的第三方控件,这时候就可以注册原生组件。我们现在支持注册的原生组件有三个:Camera、LivePlayer、LivePusher。
# 3.1 实现自定义的原生组件
首先,创建组件视图,实现其协议方法。
.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];
# 3.2 原生给组件发送消息
宿主App给原生组件发送消息,是通过nativeViewManager来实现的。
/// 给nativeView 发送事件(前台运行的小程序)
/// @param eventName 事件名称
/// @param nativeViewId native-view id
/// @param detail 事件详细参数
/// @param completion 完成回调:error code为FATErrorCodeAppletNotFound,未找到前台运行的小程序
- (void)sendEvent:(NSString *)eventName nativeViewId:(NSNumber *)nativeViewId detail:(NSDictionary *)detail completion:(void (^)(id result, NSError *error))completion;
/// 给nativeView 发送事件(appletId指定的小程序)
/// @param eventName 事件名称
/// @param appletId 小程序的appId, 不能为空
/// @param nativeViewId native-view id
/// @param detail 事件详细参数
/// @param completion 完成回调:error code为FATErrorCodeForegroundAppletNotFound,未找到appletId指定小程序
- (void)sendEvent:(NSString *)eventName applet:(NSString *)appletId nativeViewId:(NSNumber *)nativeViewId detail:(NSDictionary *)detail completion:(void (^)(id result, NSError *error))completion;
示例代码
[[FATClient sharedClient].nativeViewManager sendEvent:@"eventName" nativeViewId:@(1234) detail:@{} completion:^(id result, FATError *error) {
}];
# 3.3 原生给小程序发送全局消息
/// 发送 全局 事件(前台运行的小程序)
/// @param detail 事件详细参数
/// @param completion 完成回调:error code为FATErrorCodeAppletNotFound,未找到前台运行的小程序
- (void)sendCustomEventWithDetail:(NSDictionary *)detail completion:(void (^)(id result, NSError *error))completion;
/// 发送 全局 事件(appletId指定的小程序)
/// @param detail 事件详细参数
/// @param appletId 小程序的appId, 不能为空
/// @param completion 完成回调:error code为FATErrorCodeForegroundAppletNotFound,未找到appletId指定小程序
- (void)sendCustomEventWithDetail:(NSDictionary *)detail applet:(NSString *)appletId completion:(void (^)(id result, NSError *error))completion;
示例代码:
[[FATClient sharedClient].nativeViewManager sendCustomEventWithDetail:@{} completion:^(id result, NSError *error) {
}];