How to display a WebView in SwiftUI
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 theCoordinator
, which will respond to theWKNavigationDelegate
to use its methods.func makeUIView(context: Context)
: Creates the WKWebView and loads theURLRequest
.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 thegoBack
variable in ourViewModel
changes totrue
, the state updates, and this function is called to later calluiView.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/