iOS 9でCordovaアプリを開発する上での問題は、このシルバーウィーク中に2本既に書いています。
どちらも、iOS 9のUIWebViewがダメダメなので、代わりにWKWebViewを使うしかなさそうだという話に基づいています。
Cordova iOSも最新リリースになるはずの4.0ではWKWebViewが採用されるようなので、大勢としてはUIWebViewからWKWebViewに置き換わっていくのは間違いありません。(ただ、WKWebViewにはUIWebViewにはない制限もあるらしいので、それが致命的なものであれば少なくともiOS上ではハイブリッドアプリの命脈は絶ちきられることになりかねないのですが。。。)
ここでいま一度、iOS 9のUIWebViewにどういう問題があるのか、jQuery Mobile視点でまとめておこうと思います。
App Transport Securityは対応必須
jQuery Mobileとは直接の関係はないのですが、iOS 9から新たに加わったセキュリティ機能であるApp Transport Securityは、HTTPでの通信が禁止されるのでXHRがことごとく失敗するので問題です。
ただ、これは回避策があって、下記の設定をInfo.plistに加えればHTTP通信ができるようになります。
NSAppTransportSecurity
NSAllowsArbitraryLoads
window.historyがボロボロ
iOS 9のUIWebViewでは、window.historyがまともに機能しません。
jQuery Mobileにおいては、ヘッダ部に配置するBackボタンや、ダイアログのcloseに大きな影響を及ぼします。
hashListeningEnabledをfalseにする
Stack overflowのProblems with window.history using JQuery/Javascript on Cordova app in IOS9というディスカッションにあるように、下記のコードを追加することで多少の改善が得られます。
$(document).on("mobileinit", function() {
$.mobile.hashListeningEnabled = false;
}
jQuery Mobile 1.4.5(現在の最新版)において、例えばmobile.pagecontainerのgoメソッドは下記のようなコードです。
go: function( steps ) {
//if hashlistening is enabled use native history method
if ( $.mobile.hashListeningEnabled ) {
window.history.go( steps );
} else {
//we are not listening to the hash so handle history internally
var activeIndex = $.mobile.navigate.history.activeIndex,
index = activeIndex + parseInt( steps, 10 ),
url = $.mobile.navigate.history.stack[ index ].url,
direction = ( steps >= 1 )? "forward" : "back";
//update the history object
$.mobile.navigate.history.activeIndex = index;
$.mobile.navigate.history.previousIndex = activeIndex;
//change to the new page
this.change( url, { direction: direction, changeHash: false, fromHashChange: true } );
}
},
hashListeningEnabledがtrueの場合、window.history.goで処理するので、iOS 9のUIWebViewではまともに動作しません。
一方、mobileinitでhashListeningEnabledをfalseに変更しておいた場合、jQuery Mobile独自の仕組みで処理が行われます。window.historyに頼らないので正常に動作しそうです。
しかし、そうも上手くいきません。
少なくとも、私が試した限りでは、下記のような動作がありました。
例えば、下記のような3ページ構成のアプリがあったとします。
jQuery MobileではSPA(Single Page Architecture)で複数ページを構成するのが定石なので、ありふれた構造でしょう。
- トップページ(index.html)
- ページ1(index.html#page1)
- ページ2(index.html#page2)
ここで、トップページ→ページ1→トップページ→ページ2のように遷移した場合、$.mobile.navigate.historyにどのようにスタックされるでしょうか。
- index.html
- #page1
- index.html
- #page2
であれば嬉しいのですが、実際にはこのようになります。(少なくとも、なることがあります)
- index.html
- #page1
- #page2
では、ページ2から戻る遷移(Backボタンやダイアログを閉じる)があると、UIWebViewにはどの画面が表示されるでしょうか。
望ましいのはトップページですが、実際には予想どおりページ1が表示されます。
考えられる解決策
安直ですが、window.historyや$.mobile.navigate.historyに依存しない画面遷移を行えば、この問題は解決しそうです。
つまり、Backさせるのではなく、遷移先(戻り先)の画面を直接指定してしまうのです。
ただ、それが難しそうな場面も予想されます。例えば、ダイアログです。
jQuery Mobileではダイアログも1つの画面に過ぎません。ダイアログの表示は単なるポップアップではなく、画面が遷移しているのです。そのため、ダイアログを閉じるということは前の画面に戻るということと同義です。
jQuery Mobileにおけるdialogのcloseメソッドは、内部的に上記のmobile.pagecontainerのgoメソッドを使用するので、closeメソッドを使う以上、この問題は避けられないのです。
jQuery Mobileとしてはバグではない
jQuery MobileのGitHubで、1.4.5 – ios9 window.history.back or link does not workというIssueが報告されています。
それに対する回答は下記のようなもので、このIssueはクローズされてしまいました。
“Closing because not a bug.”
そら、そうやけどな。
一方で、下記のような回答もあります。
We solved the problem, if someone googles it, the solutions is to use WKWebview instead of the UIWebview.
So it was not a jquery mobile problem, sorry for the trouble.
なるほど。結局、UIWebViewは諦めてWKWebView使うしかないのか。。。
iOS 9.0.1でどうにかならないのか?
別件ですが、iOS 9には「スライドでアップグレード」問題というのがあって、これはAppleも公式に認めています。
その対応として、iOSアップデート(iOS 9.0.1)がまもなくリリースされるということになっています。
iOS 9.0.1での改善内容が、さすがにこの問題1つだけということはないと思うので、それでUIWebViewも問題も治ったりしないかなと期待したいところですが。。。微かな微かな期待を。。。
【続報(2015/9/24)】
本日、iOS 9.0.1のバージョンアップが始まりましたが、UIWebViewの動きにはとくに違いがないようです。残念。。。
WKWebViewに移行するしか
結局、UIWebViewのことはきれいさっぱり忘れて、WKWebViewに移行するしかないのか。
Cordova iOSはまだリリースされていないけどWKWebViewへの移行を進めているわけだし、その流れに乗るしかないのかもしれません。
9月中にリリースされるというCordova iOS 4.0の出来に期待したい!(としか、言えない。)