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)")
}
}
}
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.
Related snips
import Combine
import Foundation
class SearchViewModel: ObservableObject {
@Published var searchQuery = ""
@Published var results: [SearchResult] = []
Combine operators for data transformation
import SwiftUI
struct ContentView: View {
@State private var username = ""
@State private var isLoggedIn = false
@StateObject private var viewModel = LoginViewModel()
SwiftUI declarative UI with state management
import SwiftUI
struct CardModifier: ViewModifier {
var backgroundColor: Color = .white
var cornerRadius: CGFloat = 12
var shadowRadius: CGFloat = 5
Custom SwiftUI view modifiers for reusability
import Foundation
import UIKit
class ImageProcessor {
// Background processing with main thread updates
func processImage(_ image: UIImage, completion: @escaping (UIImage?) -> Void) {
Grand Central Dispatch for concurrency
import SwiftUI
enum Route: Hashable {
case postDetail(id: Int)
case userProfile(id: Int)
case settings
Navigation patterns with NavigationStack
import SwiftUI
// MARK: - Semantic Colors in SwiftUI
struct AdaptiveColorsView: View {
var body: some View {
VStack(spacing: 20) {
Dark mode support and adaptive colors
Share this code
Here's the card — post it anywhere.