一些開發 javaScript 與 webview 的經驗分享。
我們在開發APP的時候很常第一時間想到Android 或是 ios 透過原生的方式進行開發,只是原生開發效能雖然好,但是在更新或是上版往往有許多問題需要解決,所以很常會透過webview的方式嵌入web來呈現頁面,也就是透過網頁的方式來開發APP的頁面。
那再來問題就是要怎麼 call 原生Android 或 ios 的方法,其實不管是Android 或 ios都是把他們寫好的方法給丟到 webview 裡 global 的物件內,然後再去執行,畢竟 webview 就是瀏覽器,舉例來說如果Android有一個 getUserToken
的方法,我們要 call 它只要像這樣
window.android['getUserToken']();
// or
window.android.getUserToken();
那在 ios 上面的話是需要另外掛載 plugins,有很多的做法,在這邊的話我同事是用 WKWebViewJavascriptBridge 這套 plugins 來處理,所以我的 code 就會以這個 plugins 為範例。
WKWebViewJavascriptBridge在使用要先在code裡面加入這段
function iosCallback(callback) {
if (window.WebViewJavascriptBridge) {
return callback(WebViewJavascriptBridge);
}
if (window.WVJBCallbacks) {
return window.WVJBCallbacks.push(callback);
}
window.WVJBCallbacks = [callback];
var WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function () {
document.documentElement.removeChild(WVJBIframe)
}, 0)
}
這是WKWebViewJavascriptBridge的預載函式,透過這個 function 裡面的方法就可以去跟 ios 的 webview去做互動,像這樣
const contents = JSON.stringify(content);
iosCallback(e => e.callHandler('getUserToken', contents, res=>{
console.log(res);
}))
這邊要特別注意,調用 webview 方法的時候在ios上面是非同步的,所以如果我們要整合 android 跟 ios 的話,就會有一個是同步一個是非同步的狀況發生,那要怎麼樣處理呢?
我們可以透過把 android 跟 ios 的方法透過 Promise 給封裝起來,然後才好在我們的 web 裡面是調用,我們來看看怎麼封裝。
import device from 'current-device';
function iosCallback(callback) {
if (window.WebViewJavascriptBridge) {
return callback(WebViewJavascriptBridge);
}
if (window.WVJBCallbacks) {
return window.WVJBCallbacks.push(callback);
}
window.WVJBCallbacks = [callback];
var WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function () { document.documentElement.removeChild(WVJBIframe) }, 0)
}
export default function (event, content={}) {
const contents = JSON.stringify(content)
if(device.ios()) {
return new Promise((resolve, reject) => {
iosCallback(e => e.callHandler(event, contents, res=>{
resolve(res);
}))
});
} else if(device.android()){
return new Promise((resolve, reject) => {
let fn = window.android[event](contents);
resolve(fn);
});
}else{
const newPromise = new Promise((resolve, reject) => {
switch (event) {
case "getDomain":
resolve(process.env.DOMAIN);
break;
case "getUserToken":
resolve(process.env.TOKEN);
break;
default:
resolve(`===> default`);
break;
}
})
return newPromise;
}
}
JSON.stringfy(content)
來把要傳給 App 的參數轉成 string
,因為 android 跟 ios 沒有 javaScript 的 Object,所以只好吃 string
然後再去解析。event
,透過 resolve 把 App 回傳的參數給傳出去。這樣的話就可以再主程式裡面調用這個方法
import appWebviewFn from "./lib/appWebviewFn.js";
appWebviewFn("getUserToken");
因為它會傳是一個 Promise 我們可以這樣
import appWebviewFn from "./lib/appWebviewFn.js";
const getData = async () => {
const domain = await appWebviewFn("getDomain");
const token = await appWebviewFn("getUserToken");
/*
拿到 domain 跟 token 之後的處理需求
*/
}
getData();
搭配 async/await 使用,可以確保拿到你的資料,你的code 也不用一直 .then
的方式來寫 callBack,這樣會變得非常方便!