Cómo mostrar un WebView en SwiftUI
Marcelo Laprea
26 enero, 20233min de lectura
1. Crear un ObservableObject
Primero se crea un ObservableObject
para que se pueda comunicar con el UIViewRepresentable
que se va a crear más adelante.
class WebViewModel: ObservableObject {@Published var isLoading: Bool = false@Published var canGoBack: Bool = false@Published var shouldGoBack: Bool = false@Published var title: String = ""var url: Stringinit(url: String) {self.url = url}}
isLoading
: Conocer si el WebView ha terminado de cargar, así si queremos podemos crear un Loader o Spinner y mejorar la experiencia del usuario notificándole cuando ha terminado de cargar el WebView.canGoBack
: Conocer si se ha navegado a una página nueva y poder navegar hacia atrás.shouldGoBack
: Permitir o no navegar hacia atrás.title
: Título del WebView.url
: Ruta a abrir por el WebView.
Cómo vemos, éste ViewModel nos va a permitir tener un control sobre lo que está pasando con el WebView tanto en el UIViewRepresentable
que vamos a explicar a continuación, como en la vista donde añadiremos el WebView.
2. Crear un UIViewRepresentable con el WebView
import SwiftUIimport WebKitstruct WebViewContainer: UIViewRepresentable {@ObservedObject var webViewModel: WebViewModelfunc 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.coordinatorwebView.load(request)return webView}func updateUIView(_ uiView: WKWebView, context: Context) {if webViewModel.shouldGoBack {uiView.goBack()webViewModel.shouldGoBack = false}}}
func makeCoordinator()
: Se inicializa elCoordinator
, el cual va a responder alWKNavigationDelegate
, para poder hacer uso de los métodos de dicho delegado.func makeUIView(context: Context)
: Se crea el WKWebView y se carga elURLRequest
func updateUIView(\_ uiView: WKWebView, context: Context)
: En caso de que cambie el estado de la aplicación y afecte a ésta vista deUIKit
, se llamará a éste método para poder actualizar la vista. Un ejemplo es cuando la variablegoBack
de nuestroViewModel
cambia a True, el estado se actualiza y ésta función es llamada para poder llamar luego auiView.goBack()
.
Ahora se crea una extensión con el Coordinator
, que implementará el WKNavigationDelegate
con todos los métodos que se necesitan
extension WebViewContainer {class Coordinator: NSObject, WKNavigationDelegate {@ObservedObject private var webViewModel: WebViewModelprivate let parent: WebViewContainerinit(_ parent: WebViewContainer, _ webViewModel: WebViewModel) {self.parent = parentself.webViewModel = webViewModel}func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {webViewModel.isLoading = true}func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {webViewModel.isLoading = falsewebViewModel.title = webView.title ?? ""webViewModel.canGoBack = webView.canGoBack}func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {webViewModel.isLoading = false}}}
3. Usar el WebView
Una vez creado el UIViewRepresentable
ya se puede usar en cualquier vista de SwiftUI que necesites.
import SwiftUIstruct ContentView: View {@StateObject var webViewModel = WebViewModel(url: "https://asynclearn.com/")var body: some View {WebViewContainer(webViewModel: webViewModel).ignoresSafeArea()}}
En conclusión, para poder abrir un WebView
con SwiftUI tenemos que hacer uso de un UIViewRepresentable
e implementar los métodos del WKNavigationDelegate
en el Coordinator
. Puedes hacer uso de los métodos que necesites del WKNavigationDelegate
.