至此,我們完成了插件的使用,整個(gè)過程我們都是在 HBuilder X 中進(jìn)行,接下來我們進(jìn)入開發(fā)插件階段。
概述
uni-app原生插件是基于開源項(xiàng)目 weex 的 0.26.0 版本架構(gòu)的擴(kuò)展機(jī)制,同樣支持Module(非UI控件)和Component(原生UI控件)兩種擴(kuò)展類型,因此可以非常方便的將weex擴(kuò)展插件移植到uni-app原生插件中。 在uni-app中支持vue和nvue兩種頁面,vue頁面是基于小程序引擎渲染,nvue頁面是基于weex引擎渲染。
vue頁面中僅支持使用Module類型uni-app原生插件,不支持調(diào)用同步方法返回?cái)?shù)據(jù)
nvue頁面中支持使用Module和Component類型uni-app原生插件
參考下面兩個(gè)個(gè)官方文檔,完成基礎(chǔ)的開發(fā)環(huán)境準(zhǔn)備:
uni-app原生插件(native plugin)開發(fā)指南:https://ask.dcloud.net.cn/article/35428
環(huán)境準(zhǔn)備
iOS uni-app原生插件開發(fā)文檔 1.2 文檔如下:
在您閱讀此文檔時(shí),首先要知道 uni-app 支持 weex 插件,同時(shí)我們假定您已經(jīng)具備了相應(yīng) iOS 應(yīng)用開發(fā)經(jīng)驗(yàn),學(xué)習(xí)過 weex 知識(shí)并能夠理解相關(guān)概念。此外,您也應(yīng)該對 HTML , JavaScript , CSS 等有一定的了解,并且熟悉在JavaScript 和 Objective-C 環(huán)境下的 JSON 格式數(shù)據(jù)操作等。
開發(fā)前準(zhǔn)備:
- OS X 10.14.0+
- Xcode 11.0+
- 學(xué)習(xí) weex 0.26.0 版本框架 API,weex擴(kuò)展API for iOS
- 下載開發(fā)插件需要的 SDK 包 最新版本的SDK包,找到里面的 HBuilder-uniPluginDemo 文件夾,里面包含 HBuilder-uniPlugin 插件開發(fā)工程,后面會(huì)用到( 本文使用 iOSSDK@2.4.6.71959_20191210 )
- 下載開發(fā)插件時(shí)會(huì)用到的 js 代碼的開發(fā)工具 HBuilderX(下載地址),下載哪個(gè)版本的 HBuilderX 需要注意下,保持和上條中提到的 SDK 包(在下載 SDK 包的下載界面中有版本相關(guān)的文字描述如下圖),它提到的版本號(hào)一致(HBuilderX 版本 2.5.1.20200103 )
- 確定 SDK 包里的 Xcode 工程 HBuilder-uniPlugin 內(nèi)是否引用了 SDK 包里的 liblibWeex.a 庫和 weex-main-jsfm.js 文件
- 更新 uni-app 離線打包支持,需要使用 HBuilderX (2.4.6.20191210) 版本生成本地打包 APP 資源
在官方文檔中,講到需要我們 學(xué)習(xí) weex 0.26.0 版本框架 API,本文檔中可以省略掉,和 weex 相關(guān)的細(xì)節(jié)后面會(huì)講到,不用系統(tǒng)去學(xué)習(xí)。
插件開發(fā)過程梳理(個(gè)人認(rèn)為很重要,但是官方文檔里面沒有)
最新版本的 SDK 是個(gè)什么東西?
從官方提供的鏈接下載 SDK,下載之后解壓出來是這么一個(gè)文件夾 ,iOSSDK@2.4.6.71959_20191210,命令中包含了版本號(hào)和 release 日期,我們打開看一下其中內(nèi)容。

開發(fā)原生插件,我們只需要關(guān)注 HBuilder-uniPluginDemo 這個(gè)文件夾即可,其余的先不管。
HBuilder-uniPluginDemo 文件夾中包含的是一個(gè) xcode 工程,打開工程,我們發(fā)現(xiàn)其結(jié)構(gòu)如下。

這張圖 hin 重要,這張圖從終端視角,展示了 uni-app 是怎樣跑起來的。展示了我們寫的 js 代碼和我們 原生插件是如何產(chǎn)生聯(lián)系的。
其中,www 文件夾下的資源,是在 HBuilder 中打包生成的,打包入口如下。

有了這個(gè)前置知識(shí)之后,我們再畫個(gè)圖,從 APP 這個(gè)宏觀角度來理解一下 uni-app 開發(fā) 和 原生插件開發(fā) 這兩個(gè)過程。

uni-app 開發(fā) 就是在 HBuilder 中用 js 寫頁面和邏輯,原生插件以 framework 的方式提供,在 js 中按照某種規(guī)范引用(前面的文章中,有講到如何引用插件市場的原生插件,今天開發(fā)調(diào)試的時(shí)候,我們會(huì)講如何引用本地插件)。
原生插件開發(fā) 就是在 xcode 中用 oc 寫 framework,調(diào)試 framework 的時(shí)候,我們使用 www 文件下的資源模擬 js 調(diào)用。
uni-app原生插件開發(fā)(xcode 側(cè))
iOS平臺(tái)uni-app原生插件開發(fā)文檔 文檔中介紹的是制作 .a 靜態(tài)庫,本文制作的是一個(gè) .framework 靜態(tài)庫,接下來主要介紹如何在 uni-app 中基于 WeexSDK 開發(fā) iOS 原生插件。
制作 framework
新建一個(gè) framework 項(xiàng)目。
新建 framework 工程并進(jìn)行相關(guān)配置
新建一個(gè)名稱為 WBOCRUniSDK 的 framework 工程,xcode11 中,默認(rèn)創(chuàng)建的是一個(gè)動(dòng)態(tài)庫,我們將其改為靜態(tài)庫。

在下載的工程包 iOSSDK@2.4.6.71959_20191210 里找到 HBuilder-uniPluginDemo 文件夾,然后打開 HBuilder-uniPlugin工程,把剛創(chuàng)建的靜態(tài)庫工程引入到 HBuilder-uniPlugin 工程。

SDK 支持的最低 iOS 版本為 iOS 9.0,修改 iOS Deployment Target 為 9.0。

然后,在靜態(tài)庫工程 WBWeexOCRSDK 的 Header Search Paths 中添加引用 "$(SRCROOT)/../../SDK/inc" ,做這一步的目的,就是為了引入 weex SDK的一些依賴(注意:后面要選擇 recursive,否則會(huì)報(bào) 'WXModuleProtocol.h' file not found 的錯(cuò)誤!)
。
如下圖所示:

然后,在 HBuilder-uniPlugin 工程的Link Binary With Libraries中添加 WBWeexOCRSDK.framework 庫;然后,在 Target Dependencies 中,添加插件工程的 WBWeexOCRSDK 的 targets,如下圖所示:

配置需要注冊的插件
打開 HBuilder-uniPlugin 工程里的 info.plist 文件 加入節(jié)點(diǎn),需要嚴(yán)格按要求配置,如下圖所示:

其中,hooksClass 的值是類名,是給有些插件需要在 app 啟動(dòng)時(shí)做初始化或者獲取系統(tǒng)事件用的,如果沒有可以不填為空;
plugins 下的每一項(xiàng)是 weex 擴(kuò)展模塊或組件能力相關(guān)的配置信息,里面包含name(填weex模塊或組件類名對應(yīng)的js層用到的字符串, js層中將通過該字符串來使用原生層的模塊或組件),class(填weex模塊或組件類名),type(填module或component,一定不能配置錯(cuò)),注意不支持 weex 的 handler 擴(kuò)展。
至此,xcode 側(cè)的配置就完成了。
uni-app原生插件開發(fā)(HBuilder 側(cè))
我們在 uni-app 中寫一個(gè)頁面,頁面上有調(diào)用原生 SDK 的方法。
具體一點(diǎn),如下圖所示:

我在 HBuilder 中,寫一個(gè)頁面頁面上有三個(gè)按鈕,分別調(diào)起三個(gè)原生接口(獲取簽名、啟動(dòng) OCR 、和啟動(dòng)刷臉),底部展示原生接口的回調(diào)信息,默認(rèn)展示"Result"。
寫 UI 和調(diào)用原生接口的代碼,都在 index.vue 中編寫。
我先寫一個(gè)包含 三個(gè) 按鈕的的 頁面,并且給三個(gè)按鈕分別設(shè)置click 方法。
<template>
<view class="content">
<image class="logo" src="/static/logo.png"></image>
<view class="text-area">
<text class="title">{{title}}</text>
</view>
<button class="button" @click="getWBSign">獲取 OCR 簽名</button>
<button class="button" @click="ocr">啟動(dòng) OCR</button>
<button class="button" @click="face">啟動(dòng) 刷臉</button>
<text class="result">{{result}}</text>
</view>
</template>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
.button {
display: flex;
justify-content: center;
}
.result {
margin-left: auto;
margin-right: auto;
font-size: 22rpx;
}
</style>
接下來,需要在按鈕點(diǎn)擊之后調(diào)起 原生插件。在上面 xcode 配置中,我即將開發(fā)的原生插件命名為 WB-OCRSDK,所以引用插件的方法為:
const plugin = uni.requireNativePlugin('WB-OCRSDK');
引用插件之后,我需要調(diào)用獲取簽名、OCR 和 刷臉三個(gè)方法,目前在 xcode 中還沒有開發(fā),我預(yù)先給他們命名:
getWBSign() {
// 獲取簽名
ocr.getDebugParam({
appId: xxx,
orderNO: xxx,
}, result => {
console.log("【uni log】getDebugParam callback ================> result.");
console.log(result);
});
},
ocr() {
// 啟動(dòng) OCR
ocr.startOCR({
version : xxxx,
appId : xxxx,
nonce : xxxx,
userId : xxxx,
sign : xxxx,
orderNo : xxxx,
config: {
"SDKType": 2,
"needBothSidesRecognized":true
}
}, result => {
console.log("【uni log】startOCR callback ================> result.");
});
},
face() {
// 啟動(dòng) 刷臉
ocr.startFace({
version : xxx,
appId : xxx,
nonce : xxx,
userId : xxx,
sign : xxx,
orderNo : xxx
}, result => {
console.log("【uni log】startFace callback ================> result.");
});
}
以獲取簽名為例,來說明。
ocr.getDebugParam({
appId: xxx,
orderNO: xxx,
}, result => {
console.log("【uni log】getDebugParam callback ================> result.");
console.log(result);
});
我在插件中必須實(shí)現(xiàn)一個(gè) getDebugParam:callback:方法,js 傳遞參數(shù)給原生代碼,原生代碼提供一個(gè)回調(diào) callback,將結(jié)果回傳給 js。
其余兩個(gè)接口套路一樣。
至此,js 端的接口寫好,我們把 js 打包成資源文件,然后將其復(fù)制到 xcode 項(xiàng)目下的 www 文件夾下去。
js 代碼如何調(diào)用 framework
按照官方文檔,新建一個(gè) WBOCRSDKModule 類。

在 .m 中添加
WX_EXPORT_METHOD 代碼,添加完成之后,js 就可以透過 weex 來調(diào)用我們原生代碼了。
實(shí)現(xiàn)這幾個(gè)方法。
@implementation WBOCRSDKModule
WX_EXPORT_METHOD(@selector(startFace:callback:))
WX_EXPORT_METHOD(@selector(startOCR:callback:))
WX_EXPORT_METHOD(@selector(getDebugParam:callback:))
#pragma mark - Export Method
- (void)getDebugParam:(NSDictionary *)options callback:(WXModuleCallback)callback
{
/// 模擬拉取簽名地址,合作方接入時(shí),找自己后臺(tái)拉取
}
- (void)startOCR:(NSDictionary *)options callback:(WXModuleCallback)callback
{
NSLog(@"%s",__func__);
NSLog(@"%@",options);
}
- (void)startFace:(NSDictionary *)options callback:(WXModuleCallback)callback
{
NSLog(@"%s",__func__);
NSLog(@"%@",options);
}
@end
至此,走通了 js 調(diào)用 oc 的流程,接下來需要做的事兒就是填充業(yè)務(wù)代碼了。