开发

扩展实现

1.功能说明

1.1 扩展介绍

宿主方可根据自己的业务需求对小程序的能力进行扩展。扩展分为如下三种:

  • 纯前端扩展:如果只想扩展前端组件,则参考前端扩展,前端开发 extension.js 并集成到客户端。
  • 纯客户端扩展:如果能力只涉及客户端,则参考如下文档。
    • 跳转到宿主 App 的页面,无需开发端能力,参考 2.1
    • 获取宿主 App 的数据或提供宿主 App 的组件,需要开发端能力,参考 2.2
  • 扩展既包含前端也包含客户端,则 extension.js 及端能力都需要开发,参考 2.3

1.2 扩展流程

扩展端能力使用协议分发(scheme)的方式,下面是扩展流程图
图片

1.3 端能力介绍

1.3.1 协议介绍

小程序中的端能力是通过协议方式进行调起的,具体格式如下:

1
scheme://component/action?params=$params&callback=$callback
  • scheme:为宿主方 App 的协议头,宿主开源平台上注册的 schemeHead ;
  • component 与 module:component 表示业务类型标识,小程序标识为 swanAPI ,module 表示模块类型标识,默认为空,扩展无需关注 module
  • action:端能力的方法。
  • params:小程序传递给端的参数、params 格式为 json 数组。
  • callback:协议中的一级回调函数,端能力调用成功或失败,NA 端都必须进行回调

Demo 工程的扩展协议示例如下:

1
2
// 扩展示例,详见 Demo 工程
swandemo://swanAPI/exampleOfExtension?params=$params&callback=$callback

1.3.2 端能力介绍

端能力指的是是客户端提供的能力,可提供宿主的特定组件或宿主数据。扩展需要增加 BBASMPlugin 类的分类并提供实现方法。

2.开发指南

2.1 扩展层 - 小程序跳转到宿主特定页面

说明:如宿主有私有小程序需要打开宿主的指定页面,则可以通过实现如下方法来打开指定页面,可参考 Demo 中 BBASMUtilImplement 类的 pageTransition 方法

1
2
3
4
5
6
+ (void)pageTransition:(NSDictionary *)optionsDict
callback:(void (^)(BOOL success))callback
secondCallback:(void (^)(NSString *status, NSString *message, NSDictionary * _Nullable data))secondCallback {

// 解析 optionsDict 参数,跳转到指定宿主页面
}

2.2 扩展层 - 小程序端能力开发

新建 BBASMPlugin 的分类,需要 #import <BBAMNP/BBASMPlugin.h> 详见 Demo 层 SwanExtPlugin 中的 BBASMPlugin+Test 类

2.2.1 写端能力描述表

小程序 SDK 2.23.0 后需对扩展的端能力上方加 端能力描述注释(必须要加,否则端能力无法调通)。描述注释具体格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 描 述: 扩展端能力示例,仅供宿主参考,宿主无需拷贝到工程
* 示 例: swandemo://swanAPI/exampleOfExtension?params=$params&callback=$callback
* 文 档: ****
* 备 注:
* ********** description ************
* @name: swandemo.exampleOfExtension
* @authority: swanAPI
* @path: /exampleOfExtension
* @args: [{"name": "cb", "value": "string"}, {"name": "param1", "value": "string="}, {"name": "param2", "value": "object="}, {"name":"param3","value":"boolean="}, {"name":"param4","value":"number="}, {"name": "param5", "value": {"arrayOf": "string="}}]
* @config: {"swan": {"web": {"invoke": "swan.message.url", "handler": "bridge"}, "jsc": {"invoke": "swan.method.url", "method": "_naSwan.bridge.postMessage"}}}
*/

  • 描述:端能力描述
  • 示例:端能力协议示例,宿主需要改协议头(swandemo)和 方法(exampleOfExtension),其它无需修改
  • 文档:端能力文档, 百度系宿主需要在 es 平台上注册,并提供文档链接
  • 备注:
    • description *
  • @name: 宿主hostname.端能力方法名
  • @authority: 固定为 swanAPI ,宿主无需修改
  • @path: /端能力方法
  • @args: 小程序传递给端能力的参数,json格式的键值对:[{“key” : “参数名”, “value” : “参数类型”}]
  • @config: 固定格式,宿主无需修改

注意点

  1. 参数中 value 如果包含=说明此参数非必传,必传参数如果不传,能力会返回失败
  2. 参数里如果包含 cb 表明此端能力有异步操作的情况,前端会等待客户端回调结果后再返给小程序开发者成功的回调
  3. 端能力注释需要写到 .h 文件的端能力函数申明前
  4. 写完后需要执行端能力描述表,重新收集端能力

2.2.2 开发实现层

详见 Demo 层 SwanExtPlugin 中的 BBASMPlugin + Test.m ,主要是进行参数校验,客户端逻辑,执行一级回调和二级回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
- (void)exampleOfExtension:(BBASchemeDispatcher *)dispatcher {
// 1: 参数校验
NSDictionary *params = dispatcher.optionsDict;
NSString *appID = [Pyramid.bba_MNPUtilities currentAppID:dispatcher];
if (CHECK_DICTIONARY_INVALID(params) || CHECK_STRING_INVALID(appID)) {
// 参数校验失败,使用doCallbackWithStatus一级回调返给前端失败的回调。
[dispatcher doCallbackWithStatus:kBBASDCallBackParamsErrorStatus
message:kBBASDCallBackParamsErrorMSG
data:nil];
}

// 2: 执行端上代码
NSString *color = @"#FF00FF";

// 3: 使用doCallbackWithStatus一级回调返给前端成功的回调。
[dispatcher doCallbackWithStatus:kBBASDCallBackSuccessStatus
message:kBBASDCallBackSuccessMSG
data:@{@"color": color}];


// 4: 如果参数中cb有值,则说明还需要二级回调(二级回调指的是需要端上异步取值的情况)。需执行doCallbackWithJSMethod返给前端回调结果
NSString *cb = params[@"cb"];
if (CHECK_STRING_VALID(cb)) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[dispatcher doCallbackWithJSMethod:cb
data:@{kBBASMPluginKeyStatus : kBBASDCallBackSuccessStatus,
kBBASMPluginKeyMessage : kBBASDCallBackSuccessMSG,
kBBASMPluginKeyData : @{@"color": color}}];
});
}
}

2.2.3 联调端能力

开发者打开百度开发者工具,调用 swan.hostname.action(),hostname.action() 是具体的端能力描述表中定义端能力的 name,例如 Demo 中端能力名为 swandemo.exampleOfExtension,编译预览生成二维码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
swan.swandemo.exampleOfExtension({
param1: "param1",
param5: ["a", "b", "c"],
success: res => {
swan.showModal({
title: 'success',
content: '调用成功'
});
},
fail: err => {
swan.showModal({
title: 'fail',
content: "调用失败"
});
}
});

端上用 App 扫码打开此小程序,如断点执行到端上的逻辑的话说明端能力联调成功。

2.3 开发的能力包含前端 extension.js

2.1 前端 extension.js 包集成

按照前端的扩展功能,开发扩展前端能力,生成 extension.js(可能还有 extension.css)文件,对文件进行 zip 格式压缩为 extension.zip ,同时需要一个 extension-config.json 文件描述 extenson 包的版本号;宿主工程中需要预置这个扩展包。extension-config.json 文件格式:(必须要有 version name 和 version code )

1
2
3
4
{
"sm-core-extension-version-name":"1.20.6",
"sm-core-extension-version-code" : 1020006
}

注:extension.zip 包如果包含多个文件,压缩 zip 包时一定要注意了,在 mac 上选中多个文件进行压缩成 zip 文件,如果单独为文件夹压缩,扩展包的路径会找不到,导致扩展的端能力就调不起来。在沙箱里面的 extension 包的路径为:Documents/SwanCaches/swan-core-extension/preset (或者 remote)/1.0.0(版本号)/(包含 extension.js、extension.css )

2.2 实现扩展协议接口

需要实现配置中的“Extension 配置”接口:具体实现参考如下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
+ (NSString *)presetExtensionPackageBundlePath {
NSString *extensionPath = [[NSBundle mainBundle] pathForResource:@"BBAMNPPyramid.bundle" ofType:nil];
if (CHECK_STRING_VALID(extensionPath)) {
NSString *extensionPackagePath = [NSString stringWithFormat:@"%@/sm-core-extension.zip", extensionPath];
return extensionPackagePath;
}
return nil;
}

/// 从extension-config.json读取version name
+ (NSString *)presetExtensionPackageVersion {
NSDictionary *presetExtConDic = [self presetExtConDic];
if (CHECK_DICTIONARY_VALID(presetExtConDic)) {
return presetExtConDic[@"sm-core-extension-version-name"];
}
return nil;
}

/// 从extension-config.json读取version code
+ (NSNumber *)presetExtensionPKGVersionCode {
NSDictionary *presetExtConDic = [self presetExtConDic];
if (CHECK_DICTIONARY_VALID(presetExtConDic)) {
return presetExtConDic[@"sm-core-extension-version-code"];
}
return nil;
}