LoginSignup
3
6

More than 3 years have passed since last update.

[iOS] [Swift] WebViewコンテンツを取得可能か事前チェックしてOKの場合のみWebView画面に遷移したい場合の実装例

Last updated at Posted at 2019-10-15

TL;DR

表題のような要求を実現するために、
URLSessionでHTML文字列を取得し WKWebView.loadHTMLString(_:baseURL:) によってロードする、
という内容です。

特に目新しくもなく高等テクニックでもないのですが、案外気づいていない人も多いような気がして、記事にしてみました。

実現したいこと

(業務要件として)親ViewController側でサーバーレスポンスを事前チェックし、

  • 正常の場合、子のWebView画面に遷移してコンテンツを表示
  • エラーの場合、親ViewControllerに止まり何らかのエラー処理(例えばAlertを表示)

正常時(WebView画面に遷移してコンテンツを表示)
normal.png

エラー時(遷移せずにAlert表示)
error.png

実現手法

環境

  • Xcode 11.0
  • Swift 5.1

Storyboard

storyboard.png

コード

画面間のパラメータとしてHTML文字列とURLが必要になります。
(String, URL)のタプルにtypealiasによって型の別名を与えておくとリーダブルになるかと思います。
(以下のコード例ではWebViewSourceというtypealiasを付けています)

FirstViewController.swift
import UIKit

class FirstViewController: UIViewController {

    // Requestボタンtap
    @IBAction func request(_ sender: Any) {
// Good Request
        let urlOK = "https://qiita.com/y-some/items/f6bf2106597cde9f1969"
        guard let url = URL(string: urlOK) else {
            return
        }
// Bad Request(404)
//        let urlNG = "https://qiita.com/y-some/items/XXXXXXXXXXXXXXXXXXXX"
//        guard let url = URL(string: urlNG) else {
//            return
//        }

        URLSession.shared.dataTask(with: url) { (data, response, error) in
            // エラーハンドリング
            if let error = error {
                self.popErrorAlert(with: error.localizedDescription)
                return
            }
            guard let response = response as? HTTPURLResponse else {
                self.popErrorAlert(with: "bad response")
                return
            }
            if response.statusCode != 200 {
                self.popErrorAlert(with: "statusCode: \(response.statusCode)")
                return
            }
            guard let data = data, let url = response.url else {
                self.popErrorAlert(with: "bad data")
                return
            }
            // 正常系:2画面目(WebView)に遷移する。senderとしてHTML文字列とbaseURLを渡す
            let htmlString = String(data: data, encoding: .utf8) ?? ""
            let webViewSource = WebViewSource(htmlString: htmlString, baseURL: url)
            DispatchQueue.main.async {
                self.performSegue(withIdentifier: "showWebView", sender: webViewSource)
            }
        }.resume()
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // 2画面目(WebView)にパラメータとしてHTML文字列とbaseURLを渡す
        guard
            let webViewController = segue.destination as? WebViewController,
            let webViewSource = sender as? WebViewSource else {
                return
        }
        webViewController.webViewSource = webViewSource
    }

    func popErrorAlert(with message: String) {
        DispatchQueue.main.async {
            let alertController = UIAlertController(title:"Error", message: message, preferredStyle: .alert)
            alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
            self.present(alertController, animated: true, completion: nil)
        }
    }

}
WebViewController.swift
import UIKit
import WebKit

typealias WebViewSource = (htmlString: String, baseURL: URL)

class WebViewController: UIViewController {

    @IBOutlet weak var webView: WKWebView!
    var webViewSource: WebViewSource?

    override func viewDidLoad() {
        super.viewDidLoad()
        guard let webViewSource = webViewSource else {
            preconditionFailure("webViewSource is nil")
        }
        // メインコンテンツはパラメータにて受け取ったHTML文字列、CSSやJSなどのリソースはbaseURLによってロード
        webView.loadHTMLString(webViewSource.htmlString, baseURL: webViewSource.baseURL)
    }

}

補足

  • baseURLの設定を忘れるとCSSやJavaScriptがロードされません。
  • baseURLはリダイレクト後のURLを設定する必要がありますので、HTTPURLResponse.urlを使用します。
3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6