模塊開發(fā)指南_iOS

2022-06-06 17:47 更新

參考視頻:docs.apicloud.com/APICloud/videos-and-codes

1 模塊開發(fā)介紹

1.1 簡介

APICloud引擎通過系統(tǒng)Webkit瀏覽器,實現(xiàn)了HTML+CSS+Javascript開發(fā)語言和Objective-C/Java/C/C++等Native開發(fā)語言之間的橋接,極大的豐富和增強了標準Javascript的能力。令前端開發(fā)者通過JS即可調用移動設備的底層功能,如:電話、短信、定位、多媒體、跨域http請求等,并能將如百度地圖、支付寶等第三方廠商的SDK很容易的集成到自己的App中來。

為滿足廣大開發(fā)者自定義擴展Native module的需求,APICloud推出模塊擴展SDK,本SDK開放橋接機制,方便具有一定iOS基礎的開發(fā)者自由開發(fā)定義Native擴展模塊,豐富JS的能力,提升App的用戶體驗。

1.2 閱讀對象

本文檔面向所有使用該SDK的iOS開發(fā)人員、測試人員、合作伙伴以及對此感興趣的其他用戶。閱讀該文檔要求用戶熟悉iOS應用開發(fā),并且對Html、CSS、Javascript有一定了解。APICloud引擎強調傳輸數(shù)據(jù)的簡潔和統(tǒng)一性,因此選擇輕量級的JSON作為Javascript和Native語言之間通訊的數(shù)據(jù)載體,所以要求開發(fā)者同時要熟悉Objective-C和Javascript中JSON格式數(shù)據(jù)的操作。

1.3 開發(fā)環(huán)境

  • Xcode9.0或更高版本
  • macOS 10.12.6以上

1.4 下載SDK

前往 docs.apicloud.com/APICloud/download 下載最新版本的模塊開發(fā)SDK,找到里面的ModulesDevProject_iOS.zip,這里面包含ModuleDemo、ModulesDevProject。

2 模塊開發(fā)示例

2.1 配置module.json

module.json定義了模塊的基本信息,我們在開發(fā)模塊之前需要先定義好模塊名稱、模塊對應的類的名稱、開放給JS的方法等。

字段解釋:

  • name:對應值為模塊的名稱,JS中通過該名稱來使用模塊。
  • class:對應值為模塊對應的類的名稱,模塊類需要繼承于UZModule類。
  • methods:可選配置項。開放給JS的異步實例方法,多個方法以英文逗號隔開,此方法實現(xiàn)時需帶一個參數(shù)。(此配置方式已廢棄,在模塊類里面使用 JS_METHOD 宏來定義開放給js的方法)
  • syncMethods:可選配置項。開放給JS的同步實例方法,多個方法以英文逗號隔開,此方法實現(xiàn)時需帶一個參數(shù)。(此配置方式已廢棄,在模塊類里面使用 JS_METHOD_SYNC 宏來定義開放給js的同步方法)
  • launchClassMethod:可選配置項。若配置,引擎將在應用啟動的時候調用該方法,注意該方法需是類方法,沒有參數(shù)。(此配置方式已廢棄,在模塊類里面實現(xiàn) +(void)onAppLaunch:方法代替)
  • Build Settings:可選配置項。用于配置編譯選項,目前支持配置Other Linker Flags選項,如:

 // 當配置-force_load時,后面的路徑必須使用 $(PROJECT_DIR)/UZApp/UZModules,云編譯時所有模塊的庫文件都會放在UZModules目錄下。
 {
    "name":"moduleDemo",
    "class":"UZModuleDemo",
    "Build Settings": {
        "Other Linker Flags": "-force_load $(PROJECT_DIR)/UZApp/UZModules/libModuleDemo.a"
    }
 }

如圖,在UZApp工程中找到uz目錄下的module.json文件,在里面添加模塊的配置信息:

2.2 創(chuàng)建和配置靜態(tài)庫工程

打開Xcode,在菜單中選擇File-New-Project...,在Framework & Library中選擇Cocoa Touch Static Library,創(chuàng)建一個名為ModuleDemo的工程。

這里我們將ModuleDemo工程作為UZApp工程的一個依賴工程,這樣做的好處是運行UZApp工程時會自動編譯ModuleDemo工程,并且可以方便地在ModuleDemo工程中打斷點進行調試。

先關閉打開的靜態(tài)庫工程,然后打開UZApp工程,將ModuleDemo.xcodeproj直接拖到UZModules下,如圖:

然后再按照下圖在UZApp工程中的Linked Frameworks and Libraries處將libModuleDemo添加上:

將靜態(tài)庫工程的Build Active Architecture Only設置為No,如圖:

將靜態(tài)庫工程的iOS Deployment Target設置為7.0,如圖:

2.3 創(chuàng)建模塊類

在ModuleDemo靜態(tài)庫工程中引入必要的UZModule.h頭文件,其它頭文件根據(jù)需要引入,這些頭文件可以在下載的SDK包里面找到。

在ModuleDemo靜態(tài)庫工程中新建一個UZModuleDemo類,繼承于UZModule類,其中UZModule類為模塊的基類。模塊開發(fā)過程中文件命名時提倡加前綴,以避免和其它模塊沖突。

模塊生命周期:

  • 當在前端JS中首次調用模塊的方法時,引擎會調用模塊類的 - (id)initWithUZWebView:(UZWebView *)webView 方法進行初始化;
  • 當模塊所在的頁面被銷毀時,模塊類也會被銷毀,引擎會主動調用其 - (void)dispose 方法。

2.4 模塊方法實現(xiàn)

這里我們向js端開放一個showAlert方法,用于顯示系統(tǒng)提示框。方法使用 JS_METHOD 宏來定義,參數(shù)類型為UZModuleMethodContext,可以通過context的param屬性來獲取js傳入的參數(shù),通過context的callbackWithRet:err:delete:方法回傳數(shù)據(jù)給js端。如果要實現(xiàn)同步方法,請參考文檔第5節(jié)的同步方法。

JS_METHOD(showAlert:(UZModuleMethodContext *)context) {
    NSDictionary *param = context.param;
    NSString *title = [param stringValueForKey:@"title" defaultValue:nil];
    NSString *msg = [param stringValueForKey:@"msg" defaultValue:nil];
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        NSDictionary *ret = @{@"index":@(1)};
        [context callbackWithRet:ret err:nil delete:YES];
    }]];
    [alert addAction:[UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        NSDictionary *ret = @{@"index":@(2)};
        [context callbackWithRet:ret err:nil delete:YES];
    }]];
    [self.viewController presentViewController:alert animated:YES completion:nil];
}

實現(xiàn)模塊方法時也可以使用以前舊的方式,和上面的方式相比,在方法聲明、方法參數(shù)類型、以及回傳數(shù)據(jù)方面有所區(qū)別,已經不推薦使用舊的方式。

1、首先需要在module.json里面配置methods字段:
{
    "name":"moduleDemo",
    "class":"UZModuleDemo",
    "methods":["showAlert"]
}


2、在模塊類中實現(xiàn)showAlert方法,參數(shù)類型為NSDictionary類型。如果js端調用該方法時傳入了function,可以通過cbId字段獲取該function對應的id,然后通過模塊類的sendResultEventWithCallbackId:dataDict:errDict:doDelete方法回傳數(shù)據(jù)給js端。
- (void)showAlert:(NSDictionary *)param {
    NSInteger cbId = [param integerValueForKey:@"cbId" defaultValue:0];
    NSString *title = [param stringValueForKey:@"title" defaultValue:nil];
    NSString *msg = [param stringValueForKey:@"msg" defaultValue:nil];
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        NSDictionary *ret = @{@"index":@(1)};
        [self sendResultEventWithCallbackId:cbId dataDict:ret errDict:nil doDelete:YES];
    }]];
    [alert addAction:[UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        NSDictionary *ret = @{@"index":@(2)};
        [self sendResultEventWithCallbackId:cbId dataDict:ret errDict:nil doDelete:YES];
    }]];
    [self.viewController presentViewController:alert animated:YES completion:nil];
}

2.5 JS調用模塊方法

前端JS必須使用JSON格式數(shù)據(jù)作為JS與Native之間交換數(shù)據(jù)的傳參,APICloud引擎會對JS傳入的參數(shù)進行解析并封裝,前端JS使用模塊之前需要require模塊對象。

找到UZApp工程中widget目錄下的index.html,添加調用moduleDemo模塊的showAlert方法的代碼:

function showAlert() {
    var demo = api.require('moduleDemo');
    demo.showAlert({
        msg: 'Hello App!'
    },function(ret, err){
        var msg = "點擊了第" + ret.index + "個按鈕";
        api.toast({
            msg: msg
        });
    });
}

3 上傳模塊包

3.1 模塊包介紹

模塊包根目錄必須以該模塊的JS對象名命名,這里以moduleDemo為例,模塊包內可能包含res_moduleDemo、target、framework等文件夾以及module.json。

目錄解釋:

  • res_moduleDemo目錄:(可選配置項)放置資源文件等,此文件夾會以Create folder references方式加入工程,讀取資源文件時路徑需要加上res_moduleDemo。建議將資源文件放置在此文件夾下讀取,以防止資源文件名沖突。讀取該文件夾下資源示例代碼:

    NSString *path = [[NSBundle mainBundle].bundlePath stringByAppendingPathComponent:@"res_moduleDemo/1.png"];

  • target目錄:存放編譯生成的靜態(tài)庫文件、第三方Static類型的framework庫(直接添加到工程就能正常使用的是Static類型的,而需要在工程Embedded Binaries處添加才能正常使用的是Dynamic類型的)、以及其它需要以Create groups方式添加到應用工程中使用的文件,如bundle束、圖片等。若該目錄下存在其它的文件夾,這些文件夾會以Create folder references的方式被加入到應用工程。

  • framework目錄:(可選配置項)存放第三方Dynamic類型的framework庫(需要在工程Embedded Binaries處添加才能正常使用的是Dynamic類型的)。

  • plugins目錄:(可選配置項)存放應用擴展(App Extension)??梢詫⒕幾g好的應用擴展包(后綴為.appex)放到該目錄下,支持同時放置多個應用擴展包。注意:編譯應用擴展使用的certificate需和云編譯使用的certificate是同一個,否則會導致云編譯失敗。

  • localization目錄:(可選配置項)放置多語言本地化文件,格式如圖:

  • module.json文件:內容為JSON格式,定義了模塊的類名稱、JS對象名稱等。如:

     {
        "name":"moduleDemo",
        "class":"UZModuleDemo"
     }

一個模塊包里面也可以配置多個模塊,如:

     [{
        "name":"moduleDemo",
        "class":"UZModuleDemo"
     },
     {
         "name":"moduleDemo1",
         "class":"UZModuleDemo1"
     }]

3.2 制作moduleDemo模塊包

1、新建一個moduleDemo文件夾。

2、在moduleDemo文件夾里面創(chuàng)建一個module.json的文本文件,拷貝以下內容到module.json中,注意雙引號一定要是英文狀態(tài)下的。

 {
    "name":"moduleDemo",
    "class":"UZModuleDemo"
 }

3、在moduleDemo文件夾里面創(chuàng)建target文件夾,將靜態(tài)庫工程編譯出來的libModuleDemo.a庫拷貝到target目錄下。注意.a庫必須是真機環(huán)境的,并且需要支持armv7和arm64架構,可以在終端用以下命令查看.a庫支持的架構:

lipo -info libModuleDemo.a

4、將moduleDemo文件夾壓縮成moduleDemo.zip。

3.3 上傳模塊包到云端

登錄到APICloud網站控制臺,進入你的應用里面,在模塊欄里面找到自定義模塊選項卡,選擇上傳自定義模塊,輸入模塊信息并上傳moduleDemo.zip文件然后保存。

保存成功后將會顯示出該模塊,然后點擊模塊右上角的加號添加模塊。

4 其它SDK說明

4.1 顯示UI視圖

對于需要添加UIView類視圖的接口,需要提供fixedOn參數(shù),讓前端JS傳入frame的名字,然后將視圖添加到該frame上面,同時還應該提供fixed參數(shù),控制視圖是否隨著frame內容的移動而跟著移動。

UZModule類提供 - (BOOL)addSubview:(UIView )view fixedOn:(NSString )fixedOn fixed:(BOOL)fixed 方法,用于往指定的frame上面添加視圖。

JS_METHOD(show:(UZModuleMethodContext *)context) {
    NSDictionary *param = context.param;
    NSString *fixedOn = [param stringValueForKey:@"fixedOn" defaultValue:nil];
    BOOL fixed = [param boolValueForKey:@"fixed" defaultValue:YES];
    [self addSubview:yourView fixedOn:fixedOn fixed:fixed];
}

同時UZModule提供controller屬性來獲取當前所在視圖控制器,可通過該控制器對目標控制器進行push或者present操作。

// push
[self.viewController.navigationController pushViewController:controller animated:YES];


// present
[self.viewController presentViewController:controller animated:YES completion:nil];

4.2 文件路徑轉換

為消除iOS和Android平臺系統(tǒng)間文件路徑的差異,APICloud為前端JS提供了fs://、widget://和cache://等虛擬文件路徑協(xié)議,因此,模塊在使用JS端傳入的路徑時需要調用UZModule里面的 - (NSString )getPathWithUZSchemeURL:(NSString )url 方法來轉換成正確的絕對路徑。

NSString *path = [paramDict stringValueForKey:@"path" defaultValue:nil];
if (path) {
    NSString *fullPath = [self getPathWithUZSchemeURL:path];
}

4.3 獲取模塊配置信息

部分模塊可能要求開發(fā)者在config.xml里面配置信息,如第三方平臺申請的key之類,配置如下:

<feature name="moduleDemo">
    <param name="apiKey" value="123456" />
</feature>

那么在模塊中通過UZModule中的 getFeatureByName: 方法獲取配置信息。如果想在應用啟動時就獲取,則可以通過UZAppDelegate類的 getFeatureByName: 方法獲取。

// 在模塊類的實例方法中獲取
NSDictionary *feature = [self getFeatureByName:@"moduleDemo"];
NSString *apiKey = [feature stringValueForKey:@"apiKey" defaultValue:nil];


// 在應用啟動時獲?。ㄐ枰險ZAppDelegate.h頭文件)
NSDictionary *feature = [theApp getFeatureByName:@"moduleDemo"];
NSString *apiKey = [feature stringValueForKey:@"apiKey" defaultValue:nil];

4.4 啟動方法

如果模塊需要在應用啟動的時候就執(zhí)行一些操作,可以在模塊類中重載實現(xiàn)+ (void)onAppLaunch:(NSDictionary *)launchOptions方法,方法會在應用啟動時被調用,如:

+ (void)onAppLaunch:(NSDictionary *)launchOptions {
    NSDictionary *feature = [theApp getFeatureByName:@"moduleDemo"];
    NSString *apiKey = [feature stringValueForKey:@"apiKey" defaultValue:nil];
}

實現(xiàn)該功能也可以使用以前舊的方式,該方式需要先進行配置,配置的方法不能帶參數(shù),因此無法獲取應用啟動時的參數(shù)信息。已經不推薦使用舊的方式。

1、首先需要在module.json里面配置launchClassMethod字段:
{
    "name":"moduleDemo",
    "class":"UZModuleDemo",
    "launchClassMethod":"onAppLaunch"
}


2、在模塊類中實現(xiàn)配置的方法。
+ (void)onAppLaunch {


}

4.5 應用程序代理方法

一些功能需要通過應用程序代理方法才能實現(xiàn),如獲取推送信息、處理第三方應用回調等。

我們在UZAppDelegate中提供了 - (void)addAppHandle:(id )handle 方法,該方法的handle參數(shù)為實現(xiàn)了UIApplicationDelegate協(xié)議的對象,引擎會對應用程序代理方法做一次分發(fā)。

注意一定要在- (void)dispose方法里面調用 - (void)removeAppHandle:(id )handle方法移除對象。

如處理應用被第三方應用調起:

- (id)initWithUZWebView:(UZWebView *)webView_ {
    if (self = [super initWithUZWebView:webView_]) {
        [theApp addAppHandle:self];
    }
    return self;
}


- (void)dispose {
    [theApp removeAppHandle:self];
}


#pragma mark - UIApplicationDelegate
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
    //處理應用被三方應用調起
    return YES;
}

5 同步方法

5.1 說明

同步方法是指在js中調用模塊方法時直接返回結果,而不使用callback的方式返回結果。方法返回的類型包括NSDictionary、NSArray、NSString、NSNumber等。

5.2 實現(xiàn)

通過 JS_METHOD_SYNC 宏來定義同步方法,參數(shù)類型為UZModuleMethodContext,可以通過context的param屬性來獲取js傳入的參數(shù)。返回數(shù)據(jù)的時候,如果是BOOL、int等基本數(shù)據(jù)類型,需要用NSNumber對象進行包裝。如:

JS_METHOD_SYNC(systemVersion:(UZModuleMethodContext *)context) {
    NSString *version = [UIDevice currentDevice].systemVersion;
    return version;
}


JS_METHOD_SYNC(applicationIconBadgeNumber:(UZModuleMethodContext *)context) {
    NSInteger badgeNumber = [UIApplication sharedApplication].applicationIconBadgeNumber;
    return @(badgeNumber);
}

js中調用示例:

var demo = api.require('moduleDemo');
var systemVersion = demo.systemVersion();
var badgeNumber = demo.applicationIconBadgeNumber();

實現(xiàn)同步方法也可以使用以前舊的方式,該方式需要先進行配置,已經不推薦使用舊的方式。

1、首先需要在module.json里面配置syncMethods字段:
{
    "name":"moduleDemo",
    "class":"UZModuleDemo",
    "syncMethods":["systemVersion"]
}


2、在模塊類中實現(xiàn)配置的方法:
- (NSString *)systemVersion:(NSDictionary *)param {
    NSString *version = [UIDevice currentDevice].systemVersion;
    return version;
}

6 Swift支持

6.1 聲明

使用Swift開發(fā)模塊時,模塊類需要繼承自UZModule類,同時需要在類的前面加上@objc聲明,例如:

@objc(UZModuleDemoSwift)
class UZModuleDemoSwift: UZModule {


}

方法聲明時也需要在方法前面加上@objc,例如:

@objc func jsmethod_showAlert(_ context:UZModuleMethodContext) {


}

6.2 集成

目前靜態(tài)庫工程不支持使用swift,所以直接將swift文件添加到UZApp主工程中測試,上傳模塊包的時候則將swift文件放到target目錄下面。

7 模塊審核規(guī)范

開發(fā)者開發(fā)的模塊務必遵守《模塊審核規(guī)范-iOS》(未處理)。

以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號