插件开发手册-iOS(Beta版)


目录

第一章 SDK简介

第二章 阅读对象

第三章 SDK功能说明

第四章 开发前准备

第五章 使用SDK开发Plugin模块

第六章 资源使用说明

第七章 测试Plugin模块

第八章 打包Plugin插件

附录 开发模版下载地址

第一章 SDK 简介


369Cloud iOS引擎通过对系统的WebKit浏览器的封装和扩展,实现了HTML+CSS+JavaScript开发语言与Objective-C/C/C++等Native开发语言之间的桥接,极大的增强了标准的JavaScript的能力。前端开发者可以通过JS即可调用移动设备的底层功能。

第二章 阅读对象


本文档面对所有使用该SDK的相关人员。该文档需要阅读者具有一定的iOS应用开发经验,并且对Html、CSS、JavaScript有一定的了解,同时对JSON、xml等数据格式有一定了解。

第三章 SDK功能说明


开发者可以通过iOS SDK快速开发出369Cloud跨平台移动应用开发引擎的Plugin。引擎工程中添加该Plugin以后就可以通过JavaScript调用该Plugin的函数使用Plugin的功能。

什么是Plugin

Plugin也可以叫做插件、组件、模块,一般来说每一个Plugin都是一个功能模块,可以实现该模块的一系列的初始化,传参,函数(功能)调用等。Plugin是相对独立于引擎的工程,但是又必须依赖于引擎实现功能的调用。比如SMSVerify(短信验证)的Plugin,通过regist函数传入appKey、appSecret等参数来初始化该Plugin的相关属性,然后通过getVerifyCode等函数来实现其他功能的调用。SMSVerify完成的是短信验证的相关功能。当然也可以开发一些功能比较复杂的Plugin,比如云知声、百度地图等,可能需要十几甚至几十个函数功能才可以完成该Plugin的完整功能。

框架设计

本SDK开放桥接机制,方便具有一定iOS基础的开发者自由开发定义Plugin扩展模块,丰富JS的能力,提升App的用户体验。

369Cloud引擎框架桥接层设计如下图:

image

第四章 开发前准备


1.开发环境:

  • Xcode 6.0或者更高版本(请从AppStore下载官方正版)
  • Mac OS X 10.9以上

2.开发帮助参考:

Apple在线API文档:https://developer.apple.com/library/

Javascript规范及入门:http://www.w3school.com.cn

JSON数据在线 Viewer:http://www.bejson.com/go.html?u=http://www.bejson.com/jsonview2/

第五章 使用SDK开发Plugin模块


5.0 工作机制

1. 设计原理

因为Javascript无法访问本地功能,所以设计了一个Javascript桥可以将类似rd.插件名.功能名(参数)或者var plugin = rd.require('插件名'); plugin.功能名(参数);的调用方式桥接到本地代码:
* 方式1 - rd.插件名.功能名(参数);:
    比如一个插件叫做imageTools,有一个功能convertToPNG(uri, to, callback)(将地址所制定的图片转换为PNG)。
    调用方式为:rd.imageTools.convertToPNG('/folder/1.jpg', '/folder/1.png', function(uri){alert(uri);});
    功能描述:将目录folder下的1.jpg图片转换为PNG并将转换得到的图片命名为1.png保存到目录folder里。
* 方式2 - var plugin = rd.require('插件名'); plugin.功能名(参数);:
    还是以imageTools为例,我们的调用方式就变为:
    var imageTools = rd.require('imageTools');
    imageTools.convertToPNG('/folder/1.jpg', '/folder/1.png', function(uri){alert(uri);});
2. 运行过程
还是以imageTools为例,引擎根据plugin.xml里的配置对插件进行初始化(这里又会通过domain来确定插件名字,通过scope确定生命周期范围)。
* 初始化时:引擎在APP载入时根据scope和domain,如果scope为非createNew时初始化一个Javascript对象实例并将之附着到rd实例上,如果scope为createNew就在rd.require函数里加上一段初始化此对象的代码,在调用到require时再初始化,然后将初始化后的对象返回。
* 调用:
    * 引擎初始化插件对象时生成的JS对象可以认为是插件在JS端的一个代理,插件的每一个属性都会映射成JS端对象的property,每一个方法映射成JS端对象的function。每一个function中都包含一段特殊定义的字符串用于描述调用哪个插件的哪个方法。
    * 在插件功能调用时,引擎负责搜索哪个插件哪个方法,并将参数包装成字符串数组传入插件的方法(插件方法的申明形式:public void convertToPNG(RDCloudView view, String[] params)),这里的params即是JS端传入的参数。如果有返回值且是同步返回,则引擎根据配置将返回值返回到JS端,如果是异步返回方式则需要手动调用JsCallback方法。

5.1 工程结构说明

请先下载开发模版压缩包,插件开发模版压缩包下载地址

5.1.1 压缩包结构说明

解压压缩包。解压后文件夹包含三个子文件夹和一个build的脚本文件。子文件夹分别为demo,entry,RDPluginDemo。

5.1.2 entry

entry目录包含一个entry工程,本工程为开发者用于开发调试工程。工程结构如下图:

image

RDEngine为引擎目录。该目录下包含了引擎静态库稳文件libRDEngine.a及其头文件,还包括工程中用到的第三方库的头文件。开发者可以直接导入头文件使用。

Resource目录中包含的是引擎中使用到的资源文件。

RDPlugin目录中包含的是本Plugin实现功能的Native代码。

Supporting Files目录下的hybrid目录为js端代码目录。如果您还不是很了解该目录机构,可以查看app包结构说明文档。一般js端测试代码可以放在hybrid/app/component/main/下。

5.1.3 RDPluginDemo

本目录包含了一个RDPluginDemo工程。主要用来最后打包Plugin的.a文件。下文会说明如果进行打包.a文件。

5.1.4 demo

该目录为用户最终需要上传的文件夹。下文会具体讲述目录结构以及各个文件内容。

5.1.5 build

该脚本文件用来自动打包plugin的脚本文件。下文会进行详细说明。

5.2 开发工程配置

打开entry工程。

RDEngine目录下包含三部分,第一部分是引擎的静态库,第二部分是引擎的头文件,第三部分是引擎里用到的第三方库文件。RDResource中包含了引擎中用到的bundle文件和静态库文件。在开发中可以直接使用这些文件。在RDPlugin目录下有一个RDPluginDemo的类,类里包含了一些在hybrid开发中常用到的一些实现方法。

res目录为空目录,用于存放该工程可能会用到的资源文件(非代码文件)。如果用到下面的打包脚本,请务必确保插件资源文件在res文件夹下面,如第三方的.a、.bundle、.framework、.png、.plist、.mp3等。向工程中添加资源的时候Added folders方式一定要选择Create Groups,而且一定要选中Copy items if beeded,以保证文件会拷贝到res文件夹下。

image

Support Files目录下hybrid目录,请不要改变该目录的引用方式。该项目中包含了js端代码示例,代码位置在hybrid/app/compontent/main/下。js文件中实现了与RDPluginDemo类进行交互的js端代码。

5.3 开发Plugin模块


注意:由于引擎机制问题,build一次工程以后,会把hybrid目录拷贝到沙盒目录,下次运行的时候检测目录如果存在文件则不进行拷贝。所以build一次以后,如果再次修改hybrid目录下的文件时,需要先删除手机或模拟器里之前的的工程

5.3.1 创建类

以下以PluginDemo类为例,映射的JS对象调用方式:rd.require("PluginDemo")或者 rd.PluginDemo(根据配置在plugin.xml里面的scope区分调用方式)

建议开发者新建自己的类进行开发,因为头文件重复的话可能会导致该插件编译打包失败,导致不能使用该插件。新建类根据功能可以选择是都导入系统Foundation和UIkit。新建类需要导入RDPublicHeader类。并继承自RDPluginBase,其中RDPluginBase类为模块的基类,插件模块开发过程中文件命名时加上前缀,以避免和其他模块冲突。

5.3.2 创建方法

函数需要传且仅传一个RDBrowserArguments类型的对象。js端传入的参数都可以在该对象的m_arguments属性中获取到(注意:js端传递的参数都会包装成字符串数组通过m_arguments获取),webView的相关信息可以通过该参数的m_brwView属性获得。前端开发者就可以在html页面中即可使用“rd.PluginDemo.函数名”的方式直接调用native的函数,并进行相关操作。

pluginDemo头文件示例如图:

image

pluginDemo实现文件示例如图:

image

工程中包含了可以与Swift进行混编的桥接文件(如果使用OC进行开发可以直接忽略该文件和SwiftDemo.swift类)。桥接文件示例如图:

image

该文件默认导入RDPublicHeader头文件,该头文件中包含了一些常用的类。开发者可以根据自己需求继续添加其他用到的头文件。

SwiftDemo.swift文件示例如图:

image

5.3.3 方法类别一般分为三种:无返回值、有返回值、有回调方法

  • 无返回值方法与普通void方法一样。

  • 有返回值方法的返回类型可以是JSString、JSNumber、JSObject,如果返回null就不会给等号左边的变量赋值。有返回值有分为两种:

    • 异步回调:即是有回调函数,js前端在调用的时候需要传递回调函数(js函数)给Plugin端等待触发,如不传递回调函数将无法执行回调事件。此方式适用于执行耗时费事的任务,在任务结束时再通过回调函数返回结果。

    • 同步回调:即是有返回值的方法,此方法会在执行完所有代码后才返回。适用于任务量小,不耗时不费事的任务,以免阻塞主线程导致卡顿。

  • 无返回值

     OC:
    - (void)log:(RDBrowserArguments *)array
    {
        NSLog(@"%@", array.m_brwView);
        NSLog(@"%@",array.m_arguments[0]);
    }

    swift:
    public func log(array: RDBrowserArguments) {
        NSLog("%@",array.m_arguments);
        NSLog("%@",array.m_brwView);
    }

此方法在Native端声明以后,还需要在plugin.xml里配置接口的接口名、是否有返回值等属性。 如在plugin.xml里进行以下配置,则表示log为无返回值函数。

<method name="log" return="false"></method>
  • 同步回调

    OC:

    • (void)returnMethod:(RDBrowserArguments *)array { [RDUtility webView:array.m_brwView returnJS:@"'returnJSString'"]; }

      Swift: public func returnMethod(array: RDBrowserArguments) { RDUtility.webView(array.m_brwView, returnJS:"'returnJSString'") }

此方法在Native端声明以后,还需要在plugin.xml里配置接口的接口名、是否有返回值等属性。 如在plugin.xml里进行以下配置,则表示returnMethod为有返回值函数。返回值为字符串"'returnJSString'"(有单引号)。如果想直接给js返回对象,则不需要加单引号。

<method name="returnMethod" return="true"></method>
  • 异步回调

    OC:

    • (void)callbackMethod:(RDBrowserArguments *)array { [RDUtility webView:array.m_brwView callBackFunction:array.m_arguments[0] delete:YES paramsFormat:@"%d,%@",123,@"'成功回调'"];//该方法可设置参数格式 // 或 // [RDUtility webView:array.m_brwView callBackFunction:callbackFunction delete:YES,@"123",@"'成功回调'",nil]; }

      Swift: public func swiftCallbackMethod(array: RDBrowserArguments) { let callBackFunc: String = array.m_arguments[2] as! String//设置成功回调 let args: [CVarArgType] = [123, "'成功回调'"] withVaList(args) {

        (pointer: CVaListPointer) in
        return RDUtility.webView(array.m_brwView, callBackFunction:callBackFunc, delete:true,paramsFormat:"%d,%@", arguments:pointer)
      

      } } 注意:args为返回的数据,paramsFormat后的参数类型与之对应

此方法在Native端声明以后,还需要在plugin.xml里配置接口的接口名、是否有返回值等属性。 callBackFunction是在js端

<method name="callbackMethod" return="false"></method>

5.3.4 调用方法

369Cloud引擎使用String格式数据作为JS与Plugin之间交换数据的传参。引擎会对JS传入的参数进行以数组形式进行封装,例如js端传给native端两个参数,native插件则通过array.m_arguments[0]获取第一个参数值,array.m_arguments[1]获取第二个参数值。 例如 Html页面这样写(此处confirm对应native端UIAlertView,此处仅做调用方法示例说明,未实现confirm全部功能):

image

同时JS端也可以传入JS端的回调函数,如图传入了successFunc和cancelFunc,那么在插件中则可以这样获取JS传入的参数值,然后将操作结果回调至JS。

native端实现:

image

image

这里采用了异步回调的方式,Plugin做完相关操作以后才会将操作结果通知给js页面或者通知页面本次操作成功与否、错误提示等。

说明:
    1 因为返回值会按字符串拼接,如果返回是JS的字符串,加单引号,如果是JSON对象,Number,不用加引号。
    2 delete表示执行完回调后是否在JS端删除回调方法,也可以单独删除
    +(void)webView:(RDBrowserView*)brwView deleteCallBackFunction:(NSString*)function;

PS.如果demo中的cancelButtonTitle和otherButtonTitle值为js传参方式应该怎么修改?开发者可以尝试进行修改一下,可以帮助理解。

如果模块需要在应用启动的时候执行一些操作,首先需要在 plugin.xml中配置启动方法,然后在模块里面实现该方法,当应用启动的时候该方法就会被执行。

+(void)startUp
{

}

// 在xml中配置启动方法
<plugin domain="demo" classname = "RDPluginDemo" version = "1.0" scope = "createNew"  startup = "true">
</plugin>

当startup 为true的时候,此模块会执行startUp启动方法。

5.3.5 配置插件信息(名字、属性、方法等待)

当native类中写完功能后,需要把插件的一些信息配置到相应位置或文件,这样引擎在初始化时,会根据配置信息转为JS属性、对象,供调用。

5.3.5.1 第三方插件配置plugin.xml

对于第三方插件,369Cloud引擎要求Plugin模块开发者必须在plugin.xml文件中声明被映射native类的全限定名(className),以及其所映射的JS对象名称(domain)。

369Cloud引擎根据该文件在native端寻找相应OC类,并在适当时候将其初始化。

plugin.xml文件固定存放在Plugin工程的根目录。当工程需要添加多个插件时,开发者不需要在本地hybrid/plugin/plugins.xml配置文件里添加每个插件的配置信息,该信息会在编译时会动将所有依赖的插件的plugin.xml内容进行合并。

所以开发者需要在开发过程中定义好当前正在开发的plugin的相关信息,以便在合并的时候根据本插件的plugin.xml文件内容合并到工程中。

<?xml version="1.0" encoding="utf-8"?>
<plugin domain="demo" classname = "RDPluginDemo" version = "1.0" scope = "createNew">
    <method name="setProperty" return="false"></method>
    <method name="log" return="false"></method>
    <method name="callbackMethod" return="false"></method>
    <method name="returnMethod" return="true"></method>
    <method name="confirm" return="true"></method>
    <property name="STRING_KEY" value="'propertyString'"></property>
    <property name="NUMBER_KEY" value="0"></property>
    <property name="OBJECT_KEY" value="{key1:'value',key2:'value'}"></property>
</plugin>

属性解释:

  • domain:定义该模块所对应的JS对象名,类似于标准JS中的window、document、Math等对象。
  • className:定义该扩展模块对应被JS映射的Native端的OC类名。
  • version:插件版本号
  • scope:生命周期,不同的作用域,生命周期也就不同。有app、window、local、createNew四种,详情参考Scope生命周期
  • method:对应模块的方法,分为有返回值和无返回值,当return= "true"表示方法有返回值(不添加字段默认为"false")
  • property:对应模块的属性,name表示属性名称,value表示属性的值。因为property会按字符串拼接,如果返回是JS的字符串,加单引号,如果是JS的JSON,Number对象,不用加引号。
  • startup:app启动后是否执行startup方法(不添加字段默认为"false")
  • lifeCycleCallback:是否注册ApplicationDelegate回调,需要插件实现协议UIApplicationDelegate(不添加字段默认为"false")
  • isUI:是否是UI插件。如果为"true",需要实现Join_UI方法,返回视图(UIView),UI插件自动实现了如下5个方法(不添加字段默认为"false")
    • setFrame:设置视图frame
    • show:显示视图
    • hide:隐藏视图
    • remove:移除视图
    • isShowing:判断视图是否显示

5.3.6 生命周期


5.3.6.1 Scope生命周期
  • app:作用域是全局,插件生命周期跟app生命周期相同,调用对象方式rd.JS对象名(例如rd.demo)。
  • window:作用域是窗口,插件生命周期跟window生命周期相同。调用对象方式rd.JS对象名(例如rd.demo)。
  • local:作用域是webview,插件生命周期跟webview生命周期相同。调用对象方式rd.JS对象名(例如rd.demo)。
  • createNew:作用域是webview,每次使用都会创建一个新实例。调用对象方式rd.require('JS对象名')(例如rd.require('demo'))。

Plugin有UI插件和非UI插件之分,不同插件需要通过设置不同的scope。带UI的插件scope必须设为createNew,其它的根据情况设置。

5.3.6.2 native端

当前端js中调用模块方法时,模块首先会被初始化,引擎会调用:

- (id)initWithBrwView:(RDBrowserView *)brwView arguments:(NSMutableArray *)arguments
{
    if (self=[super init])
    {

    }
    return self;
}

当模块所在的页面需要被销毁时,引擎会调用:

-(void)dealloc
{

}

5.3.7 编译期属性配置

有一些功能在开发时需要申请第三方服务,比如QQ分享:需要申请微信开发平台账号,再通过scheme或Bundle Identifier去申请功能的服务id,这些id可能需要在 pluginConfigs.xml里配置,此小段专门为处理这种情况而生。关于pluginConfigs.xml文件的详细说明,可以点击这里查看。

5.3.7.1 Scheme

pluginConfigs.xml文件的存在是由于某些插件需要编译期添加配置信息,pluginConfigs.xml就可以配置这些信息。示例:

<config pluginName="qq">
     <ios>
         <param key="Scheme" value="tencent100371282"></param>
         <param key="Scheme" value="QQ05FB8B52"></param>
     </ios>
     <android>
     </android>
 </config>

pluginName为Plugin的domain名称。key值标明该信息为scheme,value为该scheme的值。

5.3.7.1 其他需要配置的信息

其他需要配置的信息也是通过pluginConfigs.xml文件来进行配置的。示例:

<config pluginName="JPush">
     <ios>
         <param key="JPUSH_CHANNEL" value="channel01"></param>
         <param key="JPUSH_APPKEY" value="c3110...a96bdff205"></param>
         <param key="APS_FOR_PRODUCTION" value="1"></param>
     </ios>
     <android>
     </android>
 </config>

5.3.8 显示UI视图

需要添加视图的frame,通过RDPluginBase.h中的方法获取:

-(UIWebView*)getBrwView;          // 获取当前webview

-(UIScrollView*)getScrollView;  // 获取当前scrollView

-(UIView*)getBrwWindow;           // 获取当前window

-(UIViewController*)getBrwController;  // 获取当前controller

然后直接使用下面方法添加视图

- (void)addSubview:(UIView *)view;

5.3.9 第三方常用库

为方便模块开发,引擎集成的第三方库开放如下:

  • AFNetworking: 网络通信
  • CocoaAsyncSocket: Socket通信
  • CTImageChooser: 多图片选取
  • GDataXML: XML解析
  • JSONKit: JSON数据处理
  • MBProgressHUD: 提示框
  • MJRefresh: 刷新
  • SSZipArchive: 解压
  • VoiceConvert: 音频转换

5.4 使用Swift开发Plugin模块


开发模板中默认带有一个桥接文件entry-Bridging-Header.h,该文件中默认导入RDPublicHeader.h,开发者可以直接使用Swift进行开发。此外开发模板中包含一个Swift类SwiftDemo.swift,该类中对方法的调用进行了示例。

使用Swift开发时需要注意以下几点:

  • 必须要使用@objc(xxx),xxx为该swift类类名,该名称即为plugin.xml的classname字段。如果没有@objc描述,会导致无法调用该插件
  • 需要被调用的类和方法变量等需要public
  • 由于现在SDK限制,当修改Swift类后再次进行调试时需要先编译一下,确保在entry-Swift.h中刚才的修改已经生效
  • 目前支持swift以源文件形式进行加载,相关注意事项可以点击查看swift相关

其他方面与前面OC类调用方式相同。

第六章 资源使用说明


6.1 文件路径转换

协议路径是混合引擎封装的协议。旨在开发中易于访问APP包内沙盒目录。(需注意:在正常模式和appLoader模式下对应的真实路径一样,Android在两种模式下路径是不同的)。

  • res:程序资源路径,相当于app目录
  • data:用户自定义数据路径,相当于数据目录
  • cache:缓存路径
  • cpts:components路径,相当于component所在的上级目录
  • cpt:当前component所在的路径

采用文件虚拟路径协议消除平台之间文件路径的差异,在使用js端传入的路径时,调用

// 将虚拟路径转换为绝对路径
+ (NSString *)getRealPathFromProtocolPath:(NSString*)protocolPath brwView:(RDBrowserView*)brwView verifyPath:(BOOL)verify;

说明:protocolPath为虚拟路径;verify表示是否验证。

此方法转换成正确的绝对路径,以下是协议路径与真实路径的映射关系:

res://    // 程序资源路径     --> Documents/hybrid/app
 cpts://   // component路径  --> Documents/hybrid/app/component
 cpt://    // component下资源路径    --> Documents/hybrid/app/component/...
 data://   // 用户自定义数据路径  --> Documents/hybrid/data
 cache://  // 缓存路径       --> Library/cache/369cloud/cache

更多信息请参考协议路径

6.2 键值存取器

  1. properties为键值存取器,native和js有统一的接口。其中properties文件夹包含一级文件夹(domain),存取文件".properties"为后缀的文件。
  2. 以"key=value"方式填写内容(如shareSDK_apiKey=7f9f3f803d7f),多个key=value需要换行。
  3. 创建RDPropertyManager类读取文件,如下所示

     RDPropertyManager *propertyManager = [[RDPropertyManager alloc]initWithDomain:@"share" file:@"share"];
    
  1. 使用RDPropertyManager相关api函数对这些key进行统一管理,如下所示

     NSString * shareSDK_apikeyValue = [propertyManager getStringValue:@"key"];
    

第七章 测试Plugin插件

  • 配置测试文件:
1.测试需要使用JS编写测试页面,在这之前需要配置application.xml和component.xml。

2.application.xml是app的全局配置文件,配置entry属性。

![image](http://school.pan.369cloud.com/school/369cloud/huanjing/ios/applicationEntryDes.png)

3.component.xml是一个模块的配置文件,配置name和url属性。

![image](http://school.pan.369cloud.com/school/369cloud/huanjing/ios/componentDes.png)
  • 编写测试页面并运行:
    1. 创建测试页面,在hybrid/app/component/main目录下建立加入测试页面,编译时测试页面会自动同步到app的Documents沙河目录下。
    2. 编写测试代码
    3. 确定application.xml的entry为main
    4. 确定component/main目录下的component.xml的url为你要启动的第一个html页面名称(本工程PluginDemo为empty.html)。
    5. 点击build运行工程。

第八章 打包Plugin插件


8.1 打包工程配置


8.1.1 新建打包工程并拷贝

打开Xcode,在菜单中选择File->New->Project...,在iOS分类下选择Framework & Library,然后选择Cocoa Touch Static Library。为了避免添加在工程中与其他插件造成冲突,静态库文件(.a)会在编译期进行去重操作,所以最好给工程取一个有特殊意义的名称。

Xcode6和Xcode7在创建静态库文件上稍有不同,下面是Xcode6.4和Xcode7.1创建时的截图。

Xcode6.4创建图示:

image

Xcode7.1创建图示:

image

点击Next,工程取一个有意义的名称。因为该名称会在编译阶段会去掉重复的.a文件,以避免库与库之间的冲突,所以,如果取一个太大众化的名称,容易被去重,从而导致没法使用该插件。

本文以工程名为RDPluginDemo为例进行说明。新建完工程后工程结构如图

image

把entry工程里的Engine和res目录拖进RDPluginDemo工程。步骤如图:

image

拷贝过程中请一定不要破坏文件的相对路径,否则可能会在编译过程中打包失败或App找不到该Plugin的资源而引起的崩溃。不需要选中Cpoy itemss if needed选项。

image

点击Finish完成拷贝。

删除RDPluginDemo里默认的两个文件。选中文件以后点击右键选择delete

image

弹出删除选项里选择Move to Trash

image

然后,开发者需要把entry工程里写的代码也拷贝到RDPluginDemo工程。操作步骤和注意事项与拷贝Engine和res文件夹相同。注意不要把类拖到Engine和res下。

为了避免打包时的冲突,需要删掉工程中的所有静态库.a文件,包括libRDEngine.a文件和res下的第三方.a。在Build Phases里,点击Link Binary With Libraries,转中所有的静态库文件(.a),然后点击下方的减号删除。如图所示:

image

弹出删除选项里选择Remove References

image

修改工程配置

点击工程的TARGETS,选择Build Settings。找到Other Linker Flags,添加-ObjC

image

点击PROJECT进入info选项页,修改将iOS Deployment Target设置为iOS7.0。

image

编译工程.a文件

Xcode6请选择iOS Device模式,Xcode7选择Generic iOS Device模式。然后点击Product->Archive。最终编译得到包含armv7和arm64的静态库。打包上传时需要编译release模式的包。

Xcode6图示:

image

Xcode7图示:

image

在工程目录下的build/Release-iphoneos/RDPluginDemo目录下找到生成的.a文件(如果是替身文件的话在该文件上点击右键选择显示原身即可)。

8.2 模块包说明


模块包根目录必须以该模块的JS对象名命名,模块包内包涵depend、res文件夹、pluginSupport.plist以及plugin.xml,模块包压缩为zip。

  • depend文件说明:denpend文件内容为插件工程中所添加的系统framework,dylib。内容示例:

     frameworks=CoreLocation.framework,CoreTelephony.framework,LocalAuthentication.framework[option]
     libraries=libz.1.1.3.dylib,libstdc++.dylib,libsqlite3.dylib
    

    frameworks为所需要添加的系统的framework。libraries为所需要添加的系统的dylib文件。库与库之间需要用英文半角逗号分隔开。第三方库文件只需要添加到res目录下即可,不需要在depend文件中说明。由于引擎所支持版本为7.0及以上,所以会有一些需要在8.0或者9.0以上版本才可以支持的库。这些8.0及以上系统才支持的库需要在添加库引用方式为option,所以需要在库后面添加[option]来标记说明,否则会引擎程序崩溃。例如LocalAuthentication.framework[option]。如果不进行说明默认为该库文件为7.0以上系统支持,将会用requere方式进行添加。

  • res文件夹:存放编译生成的静态库文件、第三方framework和.a库、bundle等(第三方的静态库最好放在res文件夹中,编译期会去重,避免添加在工程中与其他插件造成冲突)。res目录下允许存放文件夹,但是文件夹下的文件也要是资源文件。

  • plugin.xml:内容为xml格式,定义了模块的类名称、JS对象名称以及方法和属性等。

  • pluginSupport.plist:该文件为swift源码形式插件时需要。文件中会列出swift与OC的桥接文件中所导入的头文件

swift相关


如果是swift文件以源码形式进行加载,则不需要新建打包工程。但是需要把相关源文件放在res目录下,编译时期会把res目录下的源码添加到工程中。

如果在switf工程中使用到了一些第三方文件时,需要在桥接文件中导入该头文件,同时需要新建一个名为pluginSupport的plist文件,来说明需要导入哪些头文件。文件结构如图:

image

如图所示,entry-Bridging-Header对应一个数组(entry-Bridging-Header字段名不可更改),数组内容即为所有需要导入的头文件名称。如图示例中需要添加的头文件为SwiftFw_o.h、SwiftFw_a.h、SwiftFw_c.h。

8.3 制作压缩包


8.3.1 使用自动打包脚本进行打包

下载的插件开发模板中附带一个名为build的打包脚本。在使用脚本自动打包的时候请保证对工程配置都已经按上文描述中进行了修改,如果没有进行修改,需要未修改部分手动添加到最终需要上传的文件夹下,并按照规定格式添加

8.3.2 脚本功能说明

脚本实现功能主要包括以下部分:

  • 1.创建存放以下文件文件夹

  • 2.自动生成depend文件

  • 3.拷贝plugin.xml

  • 4.拷贝res目录

  • 5.自动提取桥接文件所引用的头文件并生成pluginSupport.plist(swift)

8.3.3 脚本使用说明

使用build脚本进行压缩。脚本文件所在位置没有限制,只要传参传正确脚本即可正确执行。

脚本需要传两个参数。

参数1:开发工程路径。(entry.xcodeproj文件的上级目录)
参数2:开发工程target名称。本工程target名称为entry
参数3:可选参数,该参数只针对swift工程适用。改参数为entry工程下桥接文件路径

打开终端(terminal)程序。

例如本项目entry工程文件所在路径为/Users/showhilllee/Documents/Demo/entry。 build脚本所在路径为/Users/showhilllee/Documents/Demo/。首先先进入脚本所在目录,在终端执行以下命令(开发者需要替换成自己的实际路径):

cd /Users/showhilllee/Documents/Demo

进入该目录以后可以使用ls命令查看是否正确。

路径示例:

image

执行命令

./build /Users/showhilllee/Documents/Demo/entry entry 

说明: 这里区分一下entry工程和新建的工程。新建的工程只是用来打包.a文件。这里执行的命令是传entry的路径。

如果是swift源文件形式的插件,命令为

./build /Users/showhilllee/Documents/Demo/entry entry /Users/showhilllee/Documents/Demo/entry/RDPlugin/entry-Bridging-Header.h

注意: 如果出现Permission denied提示信息,说明操作该目录需要较高的权限。则需要在执行命令之前加sudo。即执行命令为:

sudo ./build /Users/showhilllee/Documents/Demo/entry entry /Users/showhilllee/Documents/Demo/entry/RDPlugin/entry-Bridging-Header.h

权限被拒绝示例:

image

使用sudo权限执行命令回车以后需要输入管理员密码。

执行该命令以后开始执行压缩。当看到输出done.时表明执行结束。执行完成以后会生成一个名为插件名称(target)+plugin + 时间戳的文件夹。

注意:RDPluginDemo工程打包编译完成以后,需要手动把生成的静态库.a文件拷贝到生成文件夹下的res目录下。文件夹目录结构示例:

image

拷贝完成以后开始打zip的压缩包。方法为全选新生成文件夹下的所有文件,然后右键选择压缩。如图:

image

注意:一定不要在新建的文件夹上点击右键进行压缩。这种压缩方式和上面全选文件方式压缩后,最终的目录结构是不同的。如果目录结构不对会导致该插件无法使用!

压缩完成以后会在当前文件夹下多出一个zip的压缩包。该压缩包即需要在控制台中上传的文件之一。

开发模版下载地址