ネットを介してのWebView表示ではなく、手元にあるHTMLをWebViewで表示、scriptタグ内のJSメソッドの呼び出し方法をまとめます。
SwiftでWebViewを表示させる場合、WKWebViewを使うことがほとんどだと思います。現在ではUIWebViewは非推奨です。そちらについては以下の記事がとても参考になります。
[参考元]:UIWebViewを使わない理由とWKWebViewを使う理由
環境
OS: Mojave 10.14.5
Xcode: 10.1
Swift 4.2
HTML
今回はindex.htmlをWKWebViewでロードし、<script>
タグ内にあるJSメソッドを呼び出すことをします。
HTMLファイルはプロジェクト内に置きます。置き場は自由で構わないのですが、例として以下のように配置しておきます。
project
├─Resource
│ ├index.html
│ └sample.json
│
└─View
└SomeViewController
HTMLのロード
ローカルのHTMLファイルはパスを取得する必要があり、取得にはBundle.main.path
を使用します。
仕様ではBundle.main.path
はプロジェクトのBuild Phases
> Copy Bundle Resources
にあるファイルを取得するようです。
まずこの項目を確認し、取得したいHTMLファイルがなければ追加しておきましょう。
[参考元]:Swiftでプロジェクト内のファイルを取得する際の注意点2つ
HTMLファイルのパスをURL型にキャストしてWebViewのloadFileURL
の引数に与えます。これでWebViewにロードできました。
func loadLocalHTML() {
guard let path: String = Bundle.main.path(forResource: "index", ofType: "html") else { return }
let localHTMLUrl = URL(fileURLWithPath: path, isDirectory: false)
webView.loadFileURL(localHTMLUrl, allowingReadAccessTo: localHTMLUrl)
}
Swift側からscript内のjsを実行
実行タイミング
<script>
タグにあるメソッドは、HTML全てがロードし終わってから呼び出さないとメソッド呼び出しに失敗します。
ロードが終わったことを通知するdelegateがWKNavigationDelegate
にあります。
delegateの通知先は自分であることの宣言を忘れずに。
webView.navigationDelegate = self
extension SomeViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// ここでJSの実行をする
}
}
JSの実行
evaluateJavaScript
を使います。引数のjavaScriptString
に、実行するメソッドをString
で渡します。
また、ここのString
ではjsの文法で書く必要があります。jsの知見が乏しい筆者はここで躓きました。
[公式リファレンス]:evaluateJavaScript(_:completionHandler:)
<script>
の中身は以下のようなものと仮定して、initHTML関数に引数を渡し実行する方法を解説します。
<script>
function initHTML(id, jsonData, onload) {
console.log('initHTMLだお');
console.log(id, jsonData);
onload();
}
</script>
引数
Swiftは静的型付け、JSは動的型付けなので、ゆるい方向への値渡しです。
initHTMLの引数(id, jsonData, onload)は、Swift側ではそれぞれInt,Json,関数として渡します。
基本は文字列リテラル内での値の展開\()
を使います。
Jsonに関しては、Jsonファイルを読み取ってData
->String
に変換して、値展開で渡しています。Stringで渡してもWebView側でJsonとして扱ってくれます。
関数はjsの書き方であるアロー関数を用いて書きましょう。
extension SomeViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
let intValue = 10
guard let json = getLocalJson() else { return }
guard let strJson = String(data: json, encoding: .utf8) else { return }
let script = "initHTML(\(intValue), \(strJson), () => { console.log('Swiftから渡された値だお') });"
webView.evaluateJavaScript(script) { object, error in
print(error ?? "成功")
}
}
func getLocalJson() -> Data? {
guard let path = Bundle.main.path(forResource: "sample", ofType: "json") else { return nil }
let url = URL(fileURLWithPath: path)
let json: Data?
do { json = try Data(contentsOf: url) }
catch { json = nil }
return json
}
}
コンソールの確認
JSの実行ができたらコンソールを確認しましょう。
しかしWebViewで実行したJSコンソールログはXcodeでは見れません。
ブラウザであるSafari
で見ることができます。開発
-> 端末名
-> index.js
と選択するとWebインスペクターが開きます。
呼び出しがうまくいってログが出力されています!
表示がない場合は左上らへんにある更新マークを押しましょう。
次回
WebViewのJS内でprojectのソース(画像とか)にアクセスしたときに発生するエラー、Orijin null is not byAccess-Control-Allow-Origin
編に続く...
WWDC19あとがき
SwiftUIめっちゃすごい。iOSのUI回りに激震が走りましたね。
ここまでUIの作成ハードルが下がると我々iOSエンジニアの価値が相対的に下がっていくのでは…
Combine frameworkの登場により、Rxライブラリは今後必要なくなるのかな。ただObserverパターンの考え方は必要なのでここはそれなりにハードルは依然高そう。