如何使用Objective-C 呼叫 JavaScript - Part I 基礎篇


之前工作需要在ios上開發Ethereum 類電子錢包的SDK,(離線狀態)做交易的打包,簽章,呼叫ABI等等之類的,Geth, ethereum.io都有部分達不到我們需求的地方,最後採用Web3.js

Objective-C經驗不是很多,JavaScript根本就不會,所以在找solution的過程,吃了不少苦頭。找了不少資料,看到的都是透過這兩種方式呼叫
  • WebView:JavaScript是寫在html裡的,所以需要多一個html檔案
  • JavaScriptCore:直接呼叫 .js檔案,類似node.js的方式,比較直接

網路上的範例大概都長得像這個樣子,直接把功能寫在html裡
<html>
    <head>
        <script>
            function myFunction()
            {
                alert("Hello World!");
            }
        </script>
    </head>
    <body>
        <button onclick="myFunction()">Try me</button>
    </body>
</html>

Objective-C的部分大概長得像這樣,一定要在有UI的檔案裡
// @implementation ViewController - (void)viewDidLoad { [super viewDidLoad];
// 先取得html檔案路徑,使用webview loads html NSBundle *thisBundle = [NSBundle mainBundle]; NSString *path = [thisBundle pathForResource:@"first" ofType:@"html"]; NSURL *instructionsURL = [NSURL fileURLWithPath:path]; UIWebView* webView = [[UIWebView alloc] init]; [webView loadRequest:[NSURLRequest requestWithURL:instructionsURL]];
// 然後呼叫 js 的function, 最後的()是需要的喔!! [webView stringByEvaluatingJavaScriptFromString:@"myFunction()"];
}

那...如果需要帶參數怎麼辦勒?!
假設你的js function長這樣
<html>
    <head>
        <script>
            function myFunction(msg)
            {
                alert("Hello " + msg);
            }
        </script>
    </head>
    <body>
        <button onclick="myFunction()">Try me</button>
    </body>
</html>

那Obj-C最後在呼叫函數的部分就變成
// 然後呼叫 js 的function [webView stringByEvaluatingJavaScriptFromString:@"myFunction('Kimi')"];

那...如果,參數多一點,結構複雜一點勒?! 這種方式寫起來很麻煩,而且很容易忘記要加'()'
我個人是比較喜歡另一種方式,由JavaScriptCore幫開發者包裝參數部分
javascript檔案
"use strict"
// 這裡就不要用alert囉,因為他不會理你,懂前端的人應該就不會犯這種很笨的錯 XD
function myJSFunction(a, b) {
    return a+b;
}
Obj-C
// 需要import JavaScriptCore
#import <JavaScriptCore/JavaScriptCore.h>

// 先把 js的檔案載入
JSContext* jsContext  = [[JSContext alloc] init];
NSURL *scriptURL = [NSURL URLWithString:@"myJavaScript.js"];
NSString *script = [NSString stringWithContentsOfURL:scriptURL encoding:NSUTF8StringEncoding error:nil];
[jsContext evaluateScript:script];
// 呼叫 js 的funciton
JSValue *jsValue = jsContext[@"myJSFunction"];
//NSLog(@"%@", [jsValue toString]); //這行會show出myJSFunction的內容
[jsValue callWithArguments:@[1,2]];
對於不熟前端的人來說,這樣是不是親切許多,不需要再去多寫一個html檔案,而且傳參數方便多了(不會有單引號 雙引號的問題)

原則上,Obj-C怎麼呼叫JS大概就介紹完畢了,好像很簡單對不對。對!沒錯。不過...在真實世界的狀況,哪有這麼簡單,讓你去呼叫 js 的 alert就好,以我的例子,就是需要呼叫一整個函式庫,程式碼就會分檔案,就會需要import/include/require其他的檔案,以我的例子,Web3.js裡的index.js,一開頭就是一堆的require。
"use strict"

var version = require('../lerna.json'); 
var core = require('../packages/web3-core'); 
var Eth = require('../packages/web3-eth'); 
var Net = require('../packages/web3-net'); 
var Personal = require('../packages/web3-eth-personal'); 
var Shh = require('../packages/web3-shh'); 
var Bzz = require('../packages/web3-bzz'); 

var utils = require('../packages/web3-utils');

如果你用上述的作法,就會碰到'ReferenceError: Can't find variable: require' 的錯誤,至於這個的解法尋找了很久,似乎很少人遇到這樣的問題,或是說很少人有這樣的需求。

因為篇幅過長,所以Part II 實戰篇繼續介紹!


ref:
https://stackoverflow.com/questions/14334047/how-to-call-javascript-function-in-objective-c
https://stackoverflow.com/questions/23508455/call-native-objective-methods-from-javascript-using-javascript-core-framework

留言

這個網誌中的熱門文章

What's New in Ethereum Serenity (2.0)

瑞士滑雪分享2 - 策馬特

動手實做零知識 - circom