CLP
CLP
  • 发布:2018-12-26 17:23
  • 更新:2024-03-15 13:58
  • 阅读:62432

iOS uni-app原生插件开发文档

分类:uni-app

请移步 新的文档 此文档已停止维护!!!

iOS uni-app原生插件开发文档

一、文档概述

本文档主要介绍如何在uni-app中基于WeexSDK 开发iOS原生插件。
更多说明请参考uni-app原生插件开发指南

1.1、阅读对象

在您阅读此文档时,首先要知道uni-app支持weex插件,同时我们假定您已经具备了相应iOS应用开发经验,学习过 weex 知识并能够理解相关概念。此外,您也应该对HTML,JavaScript,CSS等有一定的了解,并且熟悉在JavaScript和Objective-C环境下的JSON格式数据操作等。

1.2、开发前准备

  • OS X 10.14.0+
  • Xcode 11.0+
  • 学习weex 0.26.0版本框架API,weex扩展API for iOS
  • 下载开发插件需要的SDK包 最新版本的SDK包,找到里面的HBuilder-uniPluginDemo文件夹,里面包含HBuilder-uniPlugin插件开发工程,后面会用到。
  • 下载开发插件时会用到的js代码的开发工具HBuilderX(下载地址),下载哪个版本的HBuilderX需要注意下,保持和上条中提到的SDK包(在下载SDK包的下载界面中有版本相关的文字描述如下图),它提到的版本号一致。
  • 确定SDK包里的Xcode工程HBuilder-uniPlugin内是否引用了SDK包里的liblibWeex.a库和weex-main-jsfm.js文件

二、 插件开发(以RichAlert插件为例)

2.1、 首先用Xcode创建静态.a库工程libWeexDCRichAlert 或(静态Framework工程),如何创建静态库工程请自行百度,下面提供了2个连接供参考。

iOS如何制作.a库
iOS如何制作静态Framework库

2.2、 在下载的工程包里找到HBuilder-uniPluginDemo文件夹,然后打开HBuilder-uniPlugin工程,把刚创建的静态库工程引入到HBuilder-uniPlugin工程,引入后的样子如下图所示,至于如何把静态库工程引入到HBuilder-uniPlugin工程里去,请自行百度,很简单的。

然后,修改libWeexDCRichAlert工程的iOS Deployment Target为8.0,如下图所示:

然后,在静态库工程libWeexDCRichAlert的 Header Search Paths 中添加引用如下图所示:

然后,在HBuilder-uniPlugin工程的Link Binary With Libraries中添加libWeexDCRichAlert.a库;然后,在Target Dependencies 中,添加插件工程的libWeexDCRichAlert的targets,如下图所示:

2.3、 配置需要注册的插件,打开HBuilder-uniPlugin工程里的info.plist 文件 加入节点,需要严格按要求配置,如下图所示:

其中,hooksClass的值是类名,是给有些插件需要在app启动时做初始化或者获取系统事件用的,如果没有可以不填为空;
plugins下的每一项是weex扩展模块或组件能力相关的配置信息,里面包含name(填weex模块或组件类名对应的js层用到的字符串,js层中将通过该字符串来使用原生层的模块或组件),class(填weex模块或组件类名),type(填modulecomponent,一定不能配置错),注意不支持weex的handler扩展。

2.4 开始具体代码开发,分三步:其中第二步看情况(有需要在app启动时初始化或者获取系统事件用的,可以有第2步,如果没有则跳过这步)

第一步,在静态库libWeexDCRichAlert工程里,新建模块类DCRichAlertModule,注意命名加前缀防止冲突(如何具体开发模块或组件请参考,weex官方扩展iOS能力


第二步,在静态库libWeexDCRichAlert工程里,新建DCRichAlertProxy类(注意命名加前缀防止冲突),继承 NSObject
遵守UniPluginProtocol协议 ,用来在app启动时初始化或者获取系统事件用的,这个类里尽量不要有其他的过多代码逻辑。如下图:


第三步,用之前下载好的HBuilderX开发工具 新建uni-app项目,选择默认模版,然后在index.vue文件里面编写下面示例的js代码,也可以再创建一个.nvue文件编写下面示例的js代码。 注意:后缀为.vue的文件只支持使用module(module也只支持异步方法不支持同步方法),不支持使用component,但是后缀为.nvue的文件两者都可以使用并且module同步异步都支持

示例

 引用方式  
const dcRichAlert = uni.requireNativePlugin('DCloud-RichAlert');  
 调用  
dcRichAlert.show({  
                    position: 'bottom',  
                    title: "提示信息",  
                    titleColor: '#FF0000',  
                    content: "<a href='https://uniapp.dcloud.io/' value='Hello uni-app'>uni-app</a> 是一个使用 Vue.js 开发跨平台应用的前端框架!\n免费的\n免费的\n免费的\n重要的事情说三遍",  
                    contentAlign: 'left',  
                    checkBox: {  
                        title: '不再提示',  
                        isSelected: true  
                    },  
                    buttons: [{  
                            title: '取消'  
                        },  
                        {  
                            title: '否'  
                        },  
                        {  
                            title: '确认',  
                            color: '#3F51B5'  
                        }  
                    ]  
                }, result => {  
                    switch (result.type) {  
                        case 'button':  
                            console.log("callback---button--" + result.index);  
                            break;  
                        case 'checkBox':  
                            console.log("callback---checkBox--" + result.isSelected);  
                            break;  
                        case 'a':  
                            console.log("callback---a--" + JSON.stringify(result));  
                            break;  
                        case 'backCancel':  
                            console.log("callback---backCancel--");  
                            break;  
                    }  
                });

然后运行导出,可以得到编译后的代码,

然后把这些代码考到HBuilder-uniPlugin工程的Pandora目录下的www目录下

如下图,其中红矩形框起来的,control.xml里的appid和目录的名称都是appid,是从HBuilderX开发工具中导出来的 资源包里的manifest.json的id,三者值需要一致

三、本地调试插件的代码

然后就可以运行HBuilder-uniPlugin工程调试代码了,效果如下图所示:

如果调试过程中遇到问题,可能是原生的问题也可能是js代码的问题,如果是原生的问题,直接修改原生代码就可以了,但是如果是js代码的问题,需要修改.nvue或.vue的代码这个时候就需要重新导出编译后的代码(编译导出代码有可能会报错,注意控制台的日志信息,千万不要把有问题的资源拿去调试),拷贝到HBuilder-uniPlugin工程的Pandora目录下的www目录下,然后务必删除之前的app,再重新运行工程

如果运行测试没任何问题,就可以进行下面的生成插件包的步骤了。

四、生成插件包

此步骤应该在您插件所有接口封装完毕,并在调试工程中测试完成后,再进行以下操作,说明中均以示例插件libWeexDCRichAlert为例进行的操作。在实际操作时,需将所有出现的libWeexDCRichAlert替换成您自己的插件名字,而且在插件开发过程中给文件命名或类命名时提倡增加前缀,以避免和其它模块冲突
如示例中插件类Module的类名是“DCRichAlertModule” ,其中“DC”就是前缀(作者标识)!
Module扩展和Component扩展在引用中的name 需要前缀加入你自己的标识,防止插件冲突。
如示例中配置的name为“DCloud-RichAlert”,其中“DCloud”就是标识!

4.1 编译生成插件静态库.a文件

.a库必须是真机环境的,并且需要支持armv7和arm64架构,且Build Active Architecture Only设置为NO,您可以在终端用以下命令查看.a库支持的架构:
lipo -info libWeexDCRichAlert.a

4.2 编写package.json文件,按下列链接配置

点击查看详细配置说明
iOS平台 package.json 详细说明请查看这篇文档前面部分,会帮助您更好的理解如何配置 https://ask.dcloud.net.cn/article/35764

iOS 插件包 至少需要包含:package.json文件 和 ios文件夹(名称必须是小写的ios)里面包含 .a (.a库必须是lib开头命名,比如libWeexDCRichAlert.a)或framework。


4.3 生成.zip包

将DCloud-RichAlert文件夹打成zip包,zip包的名称为(DCloud-RichAlert.zip)然后准备提交插件市场,其中DCloud-RichAlert文件夹名称和package.json 里id的值一致,zip包的名称是id值

五、如果想要共享给其他开发者,把这个插件提交插件市场

不提交插件市场,也可以把生成的插件放到HBuilderX里,然后提交云端打包使用,提交到云端打包之前确保插件在本地是调试通的,否则云端打包出问题,增加排查问题的难度。

六、编写原生代码时注意事项

目前 HBuilderX 正式版云端打包机使用的Xcode版本为 10.XXcode11.0 及以上版本对@available(, *)的实现做了调整打包后的.a库不兼容低版本的Xcode, 所以如果使用Xcode 11.0及以上版本开发插件并且插件中代码用到了@available(, *)关键字请按下图修改,否则可能导致打包失败。报错 Undefined symbols "___isPlatformVersionAtLeast"
(使用 HBuilderX alpha 版本没有此问题,正式版云端打包机近期会更新到最新的Xcode版本)

Tips

开发uni-app原生插件时注意不要开发广点通广告插件和穿山甲广告插件,目前不支持。

获取 UIViewController

因为 uni 框架机制,module 的 weexInstance 没有绑定 viewController,故 weexInstance.viewController 值为 nil,如果想通过 UIViewController 来跳转页面可使用下面的方法获取 UIViewController**

// 获取当前显示的 UIViewController  
+ (UIViewController *)dc_findCurrentShowingViewController {  
    //获得当前活动窗口的根视图  
    UIViewController *vc = [UIApplication sharedApplication].keyWindow.rootViewController;  
    UIViewController *currentShowingVC = [self findCurrentShowingViewControllerFrom:vc];  
    return currentShowingVC;  
}  
+ (UIViewController *)findCurrentShowingViewControllerFrom:(UIViewController *)vc  
{  
    // 递归方法 Recursive method  
    UIViewController *currentShowingVC;  
    if ([vc presentedViewController]) {  
        // 当前视图是被presented出来的  
        UIViewController *nextRootVC = [vc presentedViewController];  
        currentShowingVC = [self findCurrentShowingViewControllerFrom:nextRootVC];  

    } else if ([vc isKindOfClass:[UITabBarController class]]) {  
        // 根视图为UITabBarController  
        UIViewController *nextRootVC = [(UITabBarController *)vc selectedViewController];  
        currentShowingVC = [self findCurrentShowingViewControllerFrom:nextRootVC];  

    } else if ([vc isKindOfClass:[UINavigationController class]]){  
        // 根视图为UINavigationController  
        UIViewController *nextRootVC = [(UINavigationController *)vc visibleViewController];  
        currentShowingVC = [self findCurrentShowingViewControllerFrom:nextRootVC];  

    } else {  
        // 根视图为非导航类  
        currentShowingVC = vc;  
    }  

    return currentShowingVC;  
}

获取页面可访问图片资源存储路径 (vue页面需要注意,nvue页面不存这个问题)

有些插件可能需要返回本地的图片路径给页面来显示,如果页面是vue的话是使用 WKWebview 渲染,访问本地图片资源可能会存在跨域问题访问不到图片资源,所以需要将图片存放到指定路径下,按照下面的方法获取图片存储路径即可

原生端实现

// 引用头文件  
#import "PDRCoreApp.h"  
#import "PDRCoreAppManager.h"  
#import "PDRCoreAppInfo.h"  

// 获取路径信息  
PDRCoreAppInfo *appinfo = [PDRCore Instance].appManager.getMainAppInfo;  
// 将图片存储到 appinfo.documentPath 路径下即可,可以创建子目录;  
// 示例,原生图片存储路径为  
NSString *imgPath = [appinfo.documentPath stringByAppendingPathComponent:@"test.png"];

js 端获取图片路径有两种方式

// 1.使用相对路径  
var relativePath = "_doc/test.png"  

// 2.使用平台的绝对路径  
var docPath = plus.io.convertLocalFileSystemURL("_doc/");  
var absolutePath = docPath + 'test.png'

欢迎有原生开发能力的朋友入群一起交流学习(入群请提供注册邮箱):
DCloud原生开发者群:
1群:256775471(已满)
2群:814228233(已满)
3群:455763866

Android原生插件开发参考:https://ask.dcloud.net.cn/article/35416
更多uni-app原生插件文档参考:uni-app原生插件开发指南

7 关注 分享
DCloud_iOS_XHY z***@qq.com 君心 3***@qq.com 小安迪 one11onew W***@163.com

要回复文章请先登录注册

7***@qq.com

7***@qq.com

回复 CLP :
这个我已经看了,并没有介绍这个协议的用法。问下uniapp怎么监听iOS系统事件?app启动,app退出之类的
2020-05-15 20:15
CLP

CLP (作者)

回复 7***@qq.com :
https://nativesupport.dcloud.net.cn/NativePlugin/README
2020-05-15 20:01
7***@qq.com

7***@qq.com

你好 这个协议 UniPluginProtocol 具体怎么用的,没有看到哪里可以设置他的代理啊
2020-05-15 19:56
启明科技

启明科技

换更新的模拟器运行又好了
2020-03-21 11:50
启明科技

启明科技

获取 UIViewController,findCurrentShowingViewControllerFrom方法里的
else if ([vc isKindOfClass:[UITabBarController class]])

报错:
dyld: lazy symbol binding failed: Symbol not found: _objc_opt_class
Referenced from: /Users/Jayden/Library/Developer/Xcode/DerivedData/HBuilder-uniPlugin-gzxmpbtbqgkigxgzcvcwotughrkh/Build/Products/Debug-iphonesimulator/libWeexFrAdGdt.framework/libWeexFrAdGdt (which was built for iOS 13.0)
Expected in: /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 12.4.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libobjc.A.dylib

dyld: Symbol not found: _objc_opt_class
Referenced from: /Users/Jayden/Library/Developer/Xcode/DerivedData/HBuilder-uniPlugin-gzxmpbtbqgkigxgzcvcwotughrkh/Build/Products/Debug-iphonesimulator/libWeexFrAdGdt.framework/libWeexFrAdGdt (which was built for iOS 13.0)
Expected in: /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 12.4.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libobjc.A.dylib

Message from debugger: Terminated due to signal 9
2020-03-21 11:31
CLP

CLP (作者)

回复 放开那段代码 :
你那个报错的地址是什么?我看看全文
2020-03-12 20:21
放开那段代码

放开那段代码

回复 CLP :
同一个证书,不使用插件打包没问题,加了插件就是这个错误。说明证书没有问题。他不是说 ipa 没签名,说的是百度的动态库(AipBase.framework)没签名。
2020-03-12 19:13
CLP

CLP (作者)

回复 放开那段代码 :
is not signed using an Apple submission certificate.
Missing or invalid signature. 你的证书不对吧
2020-03-12 17:31
放开那段代码

放开那段代码

回复 CLP :
你好,我做百度 ocr 身份证识别苹果原生插件,含有动态库。自己用 xcode 打包没有问题,uniapp 官方云打包说动态库没有签名,AipBase.framework 这个是百度身份证识别的动态库,请教下需要怎么处理?终端日志:Unable to process application at this time due to the following error: Missing or invalid signature. The bundle 'com.baidu.AipBase.AipBase' at bundle path 'Payload/HBuilder.app/AipBase.framework' is not signed using an Apple submission certificate.. (1176)

package.json 文件:
{
"name": "百度身份证识别",
"id": "mybd-OCR",
"version": "1.0.0",
"description": "身份证识别",
"_dp_type":"nativeplugin",
"_dp_nativeplugin":{
"ios": {
"plugins": [{
"type": "module",
"name": "mybd-OCR",
"class": "OCRModule"
}],
"hooksClass": "OCRProxy",
"integrateType": "library",
"deploymentTarget": "8.0",
"frameworks": [
"libc++.tbd",
"AipBase.framework",
"AipOcrSdk.framework",
"IdcardQuality.framework"
],

"embedFrameworks": [
"AipBase.framework",
"AipOcrSdk.framework",
"IdcardQuality.framework"
],

"resources": [
"AipBase.framework",
"AipOcrSdk.framework",
"IdcardQuality.framework",
"aip.license"
]
}
}
}
2020-03-12 14:12
放开那段代码

放开那段代码

回复 CLP :
你好。
2020-03-12 14:11