WKWebView for web content display
Sofia Martinez
Jan 2026
1 tab
import UIKit
import WebKit
class WebViewController: UIViewController {
private var webView: WKWebView!
private var progressView: UIProgressView!
private let urlString: String
init(urlString: String) {
self.urlString = urlString
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
let configuration = WKWebViewConfiguration()
configuration.allowsInlineMediaPlayback = true
// JavaScript message handler
let contentController = WKUserContentController()
contentController.add(self, name: "messageHandler")
configuration.userContentController = contentController
webView = WKWebView(frame: .zero, configuration: configuration)
webView.navigationDelegate = self
webView.uiDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
setupProgressView()
setupObservers()
loadURL()
}
private func setupProgressView() {
progressView = UIProgressView(progressViewStyle: .default)
progressView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(progressView)
NSLayoutConstraint.activate([
progressView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
progressView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
progressView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}
private func setupObservers() {
webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil)
webView.addObserver(self, forKeyPath: #keyPath(WKWebView.title), options: .new, context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "estimatedProgress" {
progressView.progress = Float(webView.estimatedProgress)
progressView.isHidden = webView.estimatedProgress == 1.0
} else if keyPath == "title" {
title = webView.title
}
}
private func loadURL() {
guard let url = URL(string: urlString) else { return }
let request = URLRequest(url: url)
webView.load(request)
}
// MARK: - JavaScript Execution
func executeJavaScript(_ script: String, completion: ((Any?, Error?) -> Void)? = nil) {
webView.evaluateJavaScript(script, completionHandler: completion)
}
deinit {
webView.removeObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress))
webView.removeObserver(self, forKeyPath: #keyPath(WKWebView.title))
}
}
// MARK: - WKNavigationDelegate
extension WebViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if let url = navigationAction.request.url {
// Handle custom URL schemes
if url.scheme == "myapp" {
print("Handle custom URL: \(url)")
decisionHandler(.cancel)
return
}
// Block certain domains
if url.host == "blocked.com" {
decisionHandler(.cancel)
return
}
}
decisionHandler(.allow)
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print("Finished loading")
// Inject JavaScript
let js = """
window.webkit.messageHandlers.messageHandler.postMessage({
action: 'pageLoaded',
url: window.location.href
});
"""
executeJavaScript(js)
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
print("Failed to load: \(error.localizedDescription)")
}
}
// MARK: - WKUIDelegate
extension WebViewController: WKUIDelegate {
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in
completionHandler()
})
present(alert, animated: true)
}
}
// MARK: - WKScriptMessageHandler
extension WebViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
guard let dict = message.body as? [String: Any],
let action = dict["action"] as? String else {
return
}
switch action {
case "pageLoaded":
if let url = dict["url"] as? String {
print("Page loaded: \(url)")
}
default:
print("Unknown action: \(action)")
}
}
}
1 file · swift
Explain with highlit
WKWebView displays web content with modern WebKit rendering engine. It replaces deprecated UIWebView with better performance and security. I configure WKWebViewConfiguration to customize behavior like JavaScript, cookies, and media playback. Navigation delegate methods track loading progress, handle redirects, and decide navigation policies. JavaScript can communicate with native code via message handlers—I inject JavaScript and receive messages with WKScriptMessageHandler. For authentication, I handle challenges with WKNavigationDelegate. Cookie management uses WKHTTPCookieStore. WKWebView supports file uploads, PDF rendering, and inline media playback. Proper error handling addresses connection failures and invalid content.