1. 程式人生 > >iOS開發-進階:JS與OC的互動

iOS開發-進階:JS與OC的互動

在移動應用的專案中, web 相比原生應用有如下優點:

  • 版本可以隨時更新, 效率高;
  • 可動態配置要展示的資料, 及資料來源.
  • 原生應用中, 如果一個頁面的展示, 需要多次呼叫不同的網路請求, 並且, 上一次請求的結果是下一次請求的引數, 這樣按順序呼叫網路並且等待網路返回的資料會消耗大量的時間嚴重降低應用效能, 影響使用者體驗.
  • 同樣, 如果在處理事件時呼叫網路請求過多, 並且返回值均是弱邏輯. 使程式碼結構複雜. 程式出口過度, 給程式碼的測試, 可讀性和修改帶來很大的困難.

面對上面的情況 (尤其是3,4條) 時, 使用 web 可以更容易的滿足專案需求, 實現難度較低, 同時可以動態配置, 一個通用功能的網頁, 可以滿足大部分類似的需求. 但是 web 的劣勢也很多, 如使用者體驗, 效能, 資料儲存, 手機硬體的呼叫等. 在 App 內使用 web 時, sometimes 會有點選網頁跳轉介面的需求. 而使  web 呼叫 Objective-C 的方法目前只知道 Javascript. 如有其它方式, 請告訴我~ 

這裡介紹二種方法實現 JS 與 OC 的互動. 

一. WebViewJavascriptBridge 是 github 上開源的第三方庫, 利用 bridge 物件實現 JS 與 OC 的互動. 用 block 傳遞引數並續寫操作. 這種方法優點是在 OC 端程式碼已經封裝好, API 簡單易用; 缺點 web 端的程式碼實現與 Android 不相容, 因此放棄使用. 

使用的方法在Demo中有詳細的說明.

二. JavaScriptCore.framework 實現了 JS 與 OC 的互動, 這個庫是在 iOS7 時推出的.

下面在一個例子中講下如何實現 JS 與 OC 互調;

1. 匯入 JavaScriptCore.framework; 

在專案 TARGETS 中 GENERAL 下的 Linked Frameworks and Libraries 中新增 JavaScriptCore.framework;


2. 現在假設 web 端 JS 有如下程式碼:

//JS 調 OC
function WebToApp(sDataKey, iAction) {
    var sJsonStr = '{"sDataKey":"' + sDataKey + '","iAction":"' + iAction + '"}';
    //$("#" + sDataKey).val("");
    WebAppObj.WebToApp(sJsonStr);
}
//OC 回撥 JS
function AppToWeb(sJsonStr)
{
    //alert(sJsonStr)
    var obj = (new Function("", "return " + sJsonStr))();
    $("#" + obj.sDataKey).val(obj.sData);
}
3. 在 OC 中建立一個類, 測試用的 WebAppObj 類

WebAppObj.h

#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>  
@class WebAppObj;

//首先建立一個實現了JSExport協議的協議
@protocol WebAppObjProtocol <JSExport>

//此處我們測試幾種引數的情況
- (void)WebToApp:(NSString*)JsonStr;
@end

@protocol WebAppObjDelegate <NSObject>
- (void)didReceiveJSData:(NSString *)JsonStr;
@end

@interface WebAppObj : NSObject <WebAppObjProtocol>
@property(nonatomic, strong) id<WebAppObjDelegate> delegate;
@end
WebAppObj.m
@implementation WebAppObj

- (void)WebToApp:(NSString *)JsonStr
{
    if ([self.delegate respondsToSelector:@selector(didReceiveJSData:)]) {
         [self.delegate didReceiveJSData:JsonStr];
    }
}
@end

4. JS 調 OC: 在webview的代理方法: - (void)webViewDidFinishLoad:(UIWebView *)中注入 JSContext, 並將之前的 WebAppObj 物件傳遞到 context 中;

然後實現 WebAppObj 的代理方法; 當網頁的操作觸發了 Function WebToApp 方法時, OC 端通過 WebAppObj 的代理方法被 JS 呼叫, 並將 JsonStr 從 web 傳遞給 OC;

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    JSContext *context=[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    WebAppObj *webObj = [WebAppObj new];
    webObj.delegate = self;
    context[@"WebAppObj"] = webObj;
}

- (void)didReceiveJSData:(NSString *)JsonStr
{
#warning mark - JS調OC要在主執行緒執行UI否則報錯!
    __unsafe_unretained __typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"%@",JsonStr);
    });
}

5. OC 調 JS: 下面的程式碼演示了 OC 呼叫 JS 的方式;
    __unsafe_unretained __typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_main_queue(), ^{
        //1.模型轉JSON
        NSString *jsonStr = [yourModel toJSONString];
        //2.OC調JS
        JSContext *context=[weakSelf.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        NSString *javaScriptStr =[NSString stringWithFormat:@"AppToWeb('%@')", jsonStr]; //準備執行的js程式碼
        [context evaluateScript:javaScriptStr];//通過oc方法呼叫js
    });