UIWebView
WKWebView
webview migration
iOS development
Apple frameworks

Migrating from UIWebView to WKWebView

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

UIWebView is deprecated, and WKWebView is the maintained replacement for embedded web content on Apple platforms. The migration is not just a class rename: WKWebView has a different process model, different delegation points, and different behavior for JavaScript, cookies, and local files.

Start with a Real WKWebView Configuration

The usual migration path is to build the web view in code, attach a WKWebViewConfiguration, and then move any existing delegate logic into the WKNavigationDelegate or WKUIDelegate APIs.

swift
1import UIKit
2import WebKit
3
4final class BrowserViewController: UIViewController, WKNavigationDelegate {
5    private var webView: WKWebView!
6
7    override func viewDidLoad() {
8        super.viewDidLoad()
9
10        let configuration = WKWebViewConfiguration()
11        configuration.allowsInlineMediaPlayback = true
12
13        webView = WKWebView(frame: view.bounds, configuration: configuration)
14        webView.navigationDelegate = self
15        webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
16
17        view.addSubview(webView)
18
19        let url = URL(string: "https://example.com")!
20        webView.load(URLRequest(url: url))
21    }
22
23    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
24        print("Page loaded")
25    }
26}

If your old code used outlets, that is still possible, but many teams find it easier to migrate in code first so configuration is explicit.

Update JavaScript Interaction and Messaging

A common UIWebView pattern was calling synchronous JavaScript evaluation. WKWebView uses asynchronous evaluation instead, which means you need to move follow-up logic into a completion handler.

swift
1webView.evaluateJavaScript("document.title") { result, error in
2    if let error {
3        print("JavaScript error: \(error)")
4        return
5    }
6
7    if let title = result as? String {
8        print("Title: \(title)")
9    }
10}

For page-to-native communication, use script message handlers instead of ad hoc URL interception:

swift
1import WebKit
2
3final class MessageHandler: NSObject, WKScriptMessageHandler {
4    func userContentController(_ userContentController: WKUserContentController,
5                               didReceive message: WKScriptMessage) {
6        guard message.name == "appBridge" else { return }
7        print("Received: \(message.body)")
8    }
9}
10
11let controller = WKUserContentController()
12let handler = MessageHandler()
13controller.add(handler, name: "appBridge")
14
15let configuration = WKWebViewConfiguration()
16configuration.userContentController = controller

That approach is cleaner than parsing fake navigation requests and makes the bridge contract explicit.

Local Files, Cookies, and Storage Behave Differently

This is where many migrations fail. WKWebView does not behave exactly like UIWebView for local content or shared state.

If you are loading bundled HTML, use the file-loading API that grants read access to a directory:

swift
1if let fileURL = Bundle.main.url(forResource: "index", withExtension: "html") {
2    let accessURL = fileURL.deletingLastPathComponent()
3    webView.loadFileURL(fileURL, allowingReadAccessTo: accessURL)
4}

That is important because a local page may need permission to read related assets such as JavaScript, CSS, or images from the same directory tree.

Cookies also need attention. WKWebView uses website data stores rather than behaving like the old shared UIWebView path. If your app depends on session cookies, logins, or injected headers, verify that behavior explicitly during migration.

For custom request logic, you may need to move code that once lived in shouldStartLoadWith into:

  • 'WKNavigationDelegate decisions'
  • custom URL scheme handling
  • server-side changes that remove fragile client interception

Migration Strategy That Reduces Risk

Do not swap classes and hope for the best. Migrate feature by feature:

  1. Replace the view and basic loading.
  2. Rewire navigation delegates.
  3. Rebuild JavaScript bridging with message handlers.
  4. Test login, cookies, downloads, popups, and local assets.
  5. Remove every remaining UIWebView symbol from the project and dependencies.

That last step matters. Even if your own code no longer uses UIWebView, an old third-party library can still block release readiness.

Common Pitfalls

The biggest pitfall is assuming delegate methods translate one for one. UIWebView and WKWebView expose similar concepts, but the timing and APIs differ.

Another frequent problem is local file loading. A page can appear to load while its scripts or styles silently fail because read access was not granted to the containing directory.

Teams also get tripped up by JavaScript timing. Since evaluateJavaScript is asynchronous, code that used to assume an immediate result must be restructured.

Finally, test authentication carefully. Cookie and storage behavior often differ enough to break an existing sign-in flow even when normal page rendering looks fine.

Summary

  • 'WKWebView migration is an architectural update, not a search-and-replace.'
  • Move loading and navigation logic into WKWebViewConfiguration and delegates.
  • Replace synchronous JavaScript assumptions with completion handlers.
  • Use loadFileURL(_:allowingReadAccessTo:) for bundled local content.
  • Re-test cookies, authentication, and third-party dependencies before shipping.

Course illustration
Course illustration

All Rights Reserved.