網站在Webview模式的踩雷經驗

2021/04/16

前言

有的時候整個專案會因為為了節省App開發成本

會將某些功能例如登入、會員、註冊使用webview完成

並透過JavascriptInterface或flutter來做App跟webview之間的溝通

但這個過程中會出現一堆一般web開發不會遇到的問題

以下將依序紀錄說明

 

Apple相關問題

Apple堅持只要是IOS App中有第三方登入的功能

一定要實做Apple ID登入

並且還要將Apple登入放在第一順位

 

Google Ouath問題

 

403 disabled user agent

Google Oauth會擋mobile的useragent

導致這個錯誤

因此要使用setUserAgentString這個method來調整webview啟動的useragent

參考影片(5:57左右)

 

Google登入在輸入帳號密碼頁面會顯示瀏覽器不安全的問題

一樣是useragent的問題

以下為測試過確認ok可以使用的useragent值

Mozilla/5.0 (Linux; Android 8.0.0; SM-C900Y) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.105 Mobile Safari/537.36

 

小心App去覆寫原生JS導致錯誤

照理來說這個不該發生因為真的很瞎

但在開發經驗上

居然還是遇到

原因是App開發人員在webview啟動後去改寫原生的console.log、console.warn功能

導致web用到這些原生console功能的地方

直接噴console.warn is not a function的錯誤

導致一堆流程直接炸掉

 

Java的callback參數或回傳型態要確認好

有的時候會因為丟進callback的型態不正確

導致明明有丟值進callback

app卻一直接不到值

原因是因為Java在執行callback的時候發現值不對會直接變成null

 

Java bridge method can't be invoked on a non-injected object

這個是安卓的JavascriptInterface callback call不到的錯誤訊息

這個不是每次安卓開發都會遇到的問題

但就有時候會遇到不確定是否為安卓開發人員寫法不同導致的

 

總之

遇到這個問題web寫法需要在callback外面再包一層method

不能直接call

 

例如原本的方法

window.JavascriptInterface[methodName](...args)

 

要改成

const callback = (...args) => {
  return window.JavascriptInterface[methodName](...args)
}

 

開發過程debug建議

 

獨立debug頁面方便隔離測試

在使用webview的開發上常常會遇到問題

然後看不出是App還是Web哪邊的問題

因此我自己都是會獨立一個webview debug頁面

這個頁面可以獨立測試JavascriptInterface、flutter的callback

遇到問題的時候來這個頁面先call測試的callback

將問題獨立開來方便debug

 

web開發時先寫一個mock的JavascriptInterface

並可以透過指定的localstorage啟用這個mock的JavascriptInterface

來確保該call的時候有正常call到method

class mockJavascriptInterface {
  getFingerprint(fingerprint) {
    console.warn('[Webview Callback] getFingerprint', fingerprint)
  }

  getToken(token) {
    console.warn('[Webview Callback] getToken', token)
  }

  getEnduringToken(token) {
    console.warn('[Webview Callback] getEnduringToken', token)
  }

  loginSuccessfully() {
    console.warn('[Webview Callback] loginSuccessfully')
  }
}

windows.JavascriptInterface = new mockJavascriptInterface()