How to display a WebView in SwiftUI

AsyncLearn
3 min readDec 11, 2023

--

1. Create an ObservableObject

First, create an ObservableObject to communicate with the UIViewRepresentable that will be created later.

class WebViewModel: ObservableObject {
@Published var isLoading: Bool = false
@Published var canGoBack: Bool = false
@Published var shouldGoBack: Bool = false
@Published var title: String = ""

var url: String

init(url: String) {
self.url = url
}
}
  • isLoading: Indicates if the WebView has finished loading. You can use this to display a loader or spinner to improve the user experience.
  • canGoBack: Indicates if the WebView can navigate back to a previous page.
  • shouldGoBack: Allows or prevents navigating back.
  • title: The title of the WebView.
  • url: The URL to be opened by the WebView.

As you can see, this ViewModel allows us to have control over what is happening with the WebView, both in the UIViewRepresentable explained below and in the view where we will add the WebView.

2. Create a UIViewRepresentable with the WebView

import SwiftUI
import WebKit

struct WebViewContainer: UIViewRepresentable {
@ObservedObject var webViewModel: WebViewModel

func makeCoordinator() -> WebViewContainer.Coordinator {
Coordinator(self, webViewModel)
}

func makeUIView(context: Context) -> WKWebView {
guard let url = URL(string: self.webViewModel.url) else {
return WKWebView()
}

let request = URLRequest(url: url)
let webView = WKWebView()
webView.navigationDelegate = context.coordinator
webView.load(request)

return webView
}

func updateUIView(_ uiView: WKWebView, context: Context) {
if webViewModel.shouldGoBack {
uiView.goBack()
webViewModel.shouldGoBack = false
}
}
}
  • func makeCoordinator(): Initializes the Coordinator, which will respond to the WKNavigationDelegate to use its methods.
  • func makeUIView(context: Context): Creates the WKWebView and loads the URLRequest.
  • func updateUIView(_ uiView: WKWebView, context: Context): If the application's state changes and affects this UIKit view, this method is called to update the view. An example is when the goBack variable in our ViewModel changes to true, the state updates, and this function is called to later call uiView.goBack().

Now, create an extension with the Coordinator, which will implement the WKNavigationDelegate with all the necessary methods.

extension WebViewContainer {
class Coordinator: NSObject, WKNavigationDelegate {
@ObservedObject private var webViewModel: WebViewModel
private let parent: WebViewContainer

init(_ parent: WebViewContainer, _ webViewModel: WebViewModel) {
self.parent = parent
self.webViewModel = webViewModel
}

func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
webViewModel.isLoading = true
}

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webViewModel.isLoading = false
webViewModel.title = webView.title ?? ""
webViewModel.canGoBack = webView.canGoBack
}

func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
webViewModel.isLoading = false
}
}
}

3. Use the WebView

Once the UIViewRepresentable is created, you can use it in any SwiftUI view as needed.

import SwiftUI

struct ContentView: View {
@StateObject var webViewModel = WebViewModel(url: "https://asynclearn.com/")

var body: some View {
WebViewContainer(webViewModel: webViewModel)
.ignoresSafeArea()
}
}

In conclusion, to open a WebView with SwiftUI, you need to use a UIViewRepresentable and implement the methods of WKNavigationDelegate in the Coordinator. You can use the necessary methods from WKNavigationDelegate.

If you want to read the Spanish version of this article, you can find it here: https://asynclearn.com/blog/como-mostar-un-webview-en-swiftui/

--

--

AsyncLearn

Stay up-to-date in the world of mobile applications with our specialised blog.