はじめに (対象読者・この記事でわかること)

この記事は、iOSアプリ開発におけるSwiftプログラミングに興味がある方、特にWKWebViewを利用したアプリ開発を行っている方を対象にしています。この記事を読むことで、WKWebView内のリンクをタップした際にSafariアプリを起動する実装方法がわかります。また、実装中によく遭遇する問題やエラーの解決策についても学べます。WKWebViewからSafariを起動する機能は、外部サイトを表示するアプリにおいて必須の技術です。本記事では、具体的なコード例を交えて分かりやすく解説します。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。 - Swiftの基本的な文法と構文 - iOSアプリ開発の基本的な知識 - StoryboardまたはSwiftUIでのUI実装経験 - WKWebViewの基本的な使い方

WKWebViewからSafariを起動する必要性と概要

iOSアプリ開発において、WKWebViewはWebコンテンツをアプリ内に表示するための強力なツールです。しかし、WKWebView内でリンクをクリックした場合、デフォルトではそのリンクが同じWebView内で開かれてしまいます。ユーザー体験を向上させるためには、特定の条件下でSafariアプリを起動し、外部サイトを開く機能を実装する必要があります。

特に、セキュリティ上の理由から外部サイトをアプリ外で開きたい場合や、ユーザーがより快適にブラウジングできるようにしたい場合にこの機能は有効です。例えば、アプリ内で広告を表示する際に広告クリックでSafariを起動したり、特定のドメイン以外はアプリ外で開いたりといった実装が考えられます。

本記事では、Swiftを使用してWKWebViewからSafariを起動する実装方法について、具体的なコード例と共に詳しく解説します。

具体的な実装方法

WKWebViewからSafariを起動する実装は、いくつかのステップに分けて行います。以下に具体的な手順を示します。

ステップ1:WKNavigationDelegateの設定

まず、WKNavigationDelegateを設定し、WebViewのナビゲーションイベントを監視します。ViewControllerにWKNavigationDelegateを準拠させ、以下のメソッドを実装します。

Swift
import UIKit import WebKit class ViewController: UIViewController, WKNavigationDelegate { @IBOutlet weak var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() webView.navigationDelegate = self // ローカルHTMLファイルの読み込み if let url = Bundle.main.url(forResource: "index", withExtension: "html") { webView.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent()) } } // リンククリック時の処理 func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { guard let url = navigationAction.request.url else { decisionHandler(.cancel) return } // 特定のドメイン以外はSafariで開く if url.host != "example.com" { UIApplication.shared.open(url, options: [:], completionHandler: nil) decisionHandler(.cancel) return } decisionHandler(.allow) } }

このコードでは、webView(_:decidePolicyFor:decisionHandler:)メソッドをオーバーライドすることで、リンクがクリックされた際の挙動を制御しています。リンクのURLが特定のドメイン(この例ではexample.com)でない場合、UIApplication.shared.openを使用してSafariでそのURLを開き、WebViewでのナビゲーションはキャンセルしています。

ステップ2:Info.plistの設定

アプリが外部URLを開くために、Info.plistに以下の設定を追加します。

Xml
<key>LSApplicationQueriesSchemes</key> <array> <string>https</string> <string>http</string> </array>

この設定により、アプリが特定のURLスキーム(http, https)を扱うアプリ(Safariなど)を検出できるようになります。この設定がない場合、一部のデバイスやiOSバージョンでSafariが正しく起動しないことがあります。

ステップ3:HTMLファイルの準備

WebViewで表示するHTMLファイルを準備します。以下は簡単な例です。

Html
<!DOCTYPE html> <html> <head> <title>WKWebView Sample</title> </head> <body> <h1>WKWebView Sample</h1> <p><a href="https://www.example.com">Example.com (同じドメイン)</a></p> <p><a href="https://www.apple.com">Apple.com (外部ドメイン)</a></p> </body> </html>

このHTMLファイルでは、2つのリンクを用意しています。1つは同じドメイン内のリンク(WebView内で開く)、もう1つは外部ドメインのリンク(Safariで開く)です。

ステップ4:WebViewの表示

StoryboardまたはSwiftUIを使用してWebViewを表示します。Storyboardを使用する場合、WKWebViewをViewControllerに接続します。SwiftUIを使用する場合は、以下のように実装できます。

Swift
import SwiftUI import WebKit struct WebView: UIViewRepresentable { func makeUIView(context: Context) -> WKWebView { return WKWebView() } func updateUIView(_ uiView: WKWebView, context: Context) { if let url = Bundle.main.url(forResource: "index", withExtension: "html") { uiView.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent()) } } } struct ContentView: View { var body: some View { WebView() .edgesIgnoringSafeArea(.all) } }

ハマった点やエラー解決

実装中にいくつかの問題に遭遇することがあります。

問題1:Safariが起動しない

特定の条件下でSafariが起動しないことがあります。これは、URLスキームが正しく設定されていないか、Info.plistの設定が不適切な場合に発生します。

問題2:WebView内でリンクが開かれる

すべてのリンクがWebView内で開かれてしまい、Safariが起動しないことがあります。これは、navigationActionの処理が正しく実装されていない場合に発生します。

問題3:セキュリティエラー

外部サイトを開く際にセキュリティエラーが発生することがあります。これは、アプリの権限設定が不足している場合に発生します。

解決策

これらの問題を解決するための具体的な方法を以下に示します。

解決策1:Safari起動の確認

Safariが起動しない場合は、以下の点を確認してください。 - URLが正しいかどうか - Info.plistにLSApplicationQueriesSchemesが正しく設定されているか - UIApplication.shared.openの呼び出しが正しく行われているか

特に、iOS 9以降では、LSApplicationQueriesSchemesの設定が必須となっているため、この設定漏れによる起動失敗がよくあります。

解決策2:WebView内でのリンク開かない問題

WebView内でリンクが開かれる問題を解決するには、navigationActionの処理を正しく実装してください。特に、decisionHandlerの呼び出し忘れや、条件分岐の誤りがないか確認してください。

以下に修正例を示します。

Swift
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { guard let url = navigationAction.request.url else { decisionHandler(.cancel) return } // 特定のドメイン以外はSafariで開く if url.host != "example.com" { // 新しいウィンドウで開くかどうかの確認 if navigationAction.targetFrame == nil || navigationAction.targetFrame?.isMainFrame == false { UIApplication.shared.open(url, options: [:], completionHandler: nil) decisionHandler(.cancel) return } } decisionHandler(.allow) }

この修正では、navigationAction.targetFrameをチェックすることで、新しいウィンドウで開かれるリンク(target="_blank"など)を正しく処理できるようにしています。

解決策3:セキュリティエラーの解消

セキュリティエラーを解消するには、Info.plistに以下の設定を追加してください。

Xml
<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>

ただし、セキュリティ上の理由から、本番環境ではより厳格な設定を推奨します。特定のドメインのみを許可する場合は、以下のように設定します。

Xml
<key>NSAppTransportSecurity</key> <dict> <key>NSExceptionDomains</key> <dict> <key>example.com</key> <dict> <key>NSExceptionAllowsInsecureHTTPLoads</key> <true/> <key>NSExceptionMinimumTLSVersion</key> <string>TLSv1.1</string> <key>NSIncludesSubdomains</key> <true/> </dict> </dict> </dict>

まとめ

本記事では、Swiftを使用してWKWebViewからSafariを起動する実装方法について解説しました。WKNavigationDelegateの設定、Info.plistの変更、HTMLファイルの準備といった基本的な手順から、実装中によく遭遇する問題とその解決策まで網羅しました。

この機能を実装することで、ユーザー体験を向上させ、より使いやすいiOSアプリを作成できます。特に、外部サイトへのリンクを扱う際には、ユーザーが期待する挙動(Safariで開く)を実装することが重要です。

今後は、より高度なWebViewの活用方法や、WebViewとネイティブコードの連携についても記事にする予定です。

参考資料