如何使用Objective-C 呼叫 JavaScript - Part II 實戰篇


前情提要,如果我需要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');

最後找到一個方法,不知道是不是唯一,也不知道是不是最好的(歡迎有經驗的人一起分享),我使用了Browserify。就我的認知,Browserify就是把require到的檔案,全部打包到一個檔案裡,也就是你最後會有一個幾千行的 .js檔案。


首先,先安裝npm(npm就是Node.js安裝管理工具,如何安裝npm,可以參考這裏
再來,寫一個 .js檔案,把你需要的套件require進來,然後assign給window這個物件,
// index.js "use strict" let Web3 = require('../3rd_party/web3.js-1.0.0'); window.Web3 = Web3;

切到index.js的目錄下,輸入
$ browserify index.js -o myWeb3.js


你可能會碰到幾個問題,第一個是npm module的路徑問題,這個細節我有點忘了,當時也是兵荒馬亂,所以可能要麻煩自己google一下。第二,是過程中會不斷地需要安裝其它套件,對!這是正常的,因為他要把檔案打包起來,所以有用的套件,都會需要下載。
過程中大概就是不斷的browserify index.js -o myWeb3.js 跟不斷地 npm install xxx。最後的產出就是myWeb3.js

理論上這個時候就可以直接使用myWeb3.js,不過browserify會用他自己的語法把每個 js套件包起來,像是namespace的概念,所以用JavaScriptCore時會找不到你要的函數,這部分我就沒繼續深入研究要怎麼呼叫。而且一般來說,也是會把需要的函數在拉成一個檔案,讓上層比較好呼叫,維護上也比較方便。不過,如果把所需的函數拉成一個 js檔,就又會碰到require的問題(因為要require myWeb3.js),所以後來就使用html作為中介,以下是我的html(main.html)
<html>
   
<head>
        <
script type="text/javascript" src="bridge/web3.js"></script>
   
</head>
   
<body> 
       <script type="text/javascript"> 
       function getAddress(prvkey)
       {
          try
          {   var web3 = new Web3();
              var account = web3.eth.accounts.privateKeyToAccount(prvkey);
              return account.address;
          }
          catch (e)
          {
              return e;
          }
       }
       </script>
   </body>
</
html>


根據基礎篇提到,html就要使用webview,可是webview呼叫 js函數又很不方便(對我來說啦 XD),後來找到一個,兩種混一起用的方式,就是由webview 載入html,再透過webview的元件把 js抽出來成JSContext(就是JavaScriptCore的方式)
最後Obj-C的部分長這樣
// 要放在global
UIWebView* _webView=nil;

-
(void)viewDidLoad {
     [super viewDidLoad];

     _webView
= [[UIWebView alloc] init];
    
NSURL* url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"main"
                                                                         ofType:
@"html"]];
    [_webView loadRequest:[
NSURLRequest requestWithURL:url]];
    JSContext
* context = [_webView valueForKeyPath:@"documentView.webView.mainFrame.
                                                     javaScriptContext"
];
    JSValue
* jsFunc = context[@"getAddress"];
    JSValue
* ret = [jsFunc callWithArguments:@"0x997630f6ad1c0d38634a0966bdbfe23344fe250
                                               118df0f97fd00a0ad5727bbb0"
];
    // 接回傳值

   
NSString* ret = [ret toString];
}

小提醒:在實際狀況,不會只在viewDidLoad中呼叫JS的函數,所以context可以設為global變數,此時,_webView也需要是global的,不然context[@"getAddress"]會回傳nil,原因不知為何,這個小細節可能要注意一下。


分享大概到這邊啦!如果有更好的方式也歡迎指教~


留言

這個網誌中的熱門文章

What's New in Ethereum Serenity (2.0)

瑞士滑雪分享2 - 策馬特

動手實做零知識 - circom