A pesar de que SwiftUI incorpora buena cantidad de controles nativos aún queda mucho por hacer hasta todos los controles del ecosistema Apple sean SwiftUI puros.
De momento lo que SwiftUI nos brinda es la opción de embeber los controles de UIKit en el contenedor View
. En este artículo vamos a ver cómo añadir un reproductor de vídeo para ver las sesiones de la WWDC mediante el framework AVFoundation
Cambio de protocolo
Lo primero que tenemos que hacer es crear un struct
que represente el reproductor de vídeo, pero en lugar de implementar el protocolo View
tenemos que usar UIViewRepresentable
que es el que se emplea para embeber vistas de UIKit en vistas de SwiftUI
A view that represents a UIKit view.
Este protocolo necesita trabajar con dos tipos de datos, un Coordinator
que se encarga de coordinar nuestra vista y la de UIKit y un UIViewType
que es la vista de UIKit que queremos mostrar.
Además de estos dos tipo de datos, el protocolo tiene una serie de funciones que debemos implementar de manera obligatoria, mientras que otras tienen una implementación por defecto.
El mínimo que debemos implementar son estas dos funciones
func makeUIView(context: Context) -> UIView | |
{ | |
… | |
} | |
func updateUIView(_ view: UIView, context: Context) -> Void | |
{ | |
… | |
} |
En este caso el parámetro context
hace referencia al tipo UIViewRepresentableContext
, que contiene información sobre el contexto en el que la vista de UIKit se actualiza.
Dos de sus propiedades más útliles son environment
y transaction
- environment tiene acceso a las variables de entorno actuales para la vista. Con ella podemos saber el esquema de color que usa el sistema, el size del dispositivo, etc.
- transaction se usa para pasar una animación entre vistas.
El otro parámetro es el tipo de vista de UIKit que queremos representar, en nuestro caso vamos a mostrar un WKWebView
donde mostraremos la web oficial de la WWDC 2019.
Vamos a empezar
Lo primero que tenemos que hacer es añadir una nueva vista de SwiftUI, pero en lugar de implementar el protocolo View
vamos a implementar el protocolo UIViewRepresentable
.
Pero no todo acaba aquí, que va. En los ejemplos que se suelen ver con hacer esto basta, pero en la mayoría de casos en los que trabajamos esas UIView
deben implementar delegados en los que recibir determinados eventos.
Ese es justo nuestro caso, necesitamos que WKWebView
responda a los eventos del delegado WKNavigationDelegate
así que tenemos que hacer unos pequeños cambios en la declaración de nuestra vista.
Lo primero es que el protocolo WKNavigationDelegate
sólo se puede aplicar a tipos clase, no a estructuras, además debe implementar el protocolo NSObjectProtocol
, que se soluciona haciendo que nuestra clase herede de NSObject
y por último, otro de los requisitos que debemos cumplir es que nuestra clase sea final
.
Con todo esto nuestra vista de navegador web queda de esta manera…
import WebKit | |
import SwiftUI | |
internal final class WebBrowser: NSObject, UIViewRepresentable | |
{ | |
… | |
} | |
extension WebBrowser: WKNavigationDelegate | |
{ | |
… | |
} |
Y sí, una vista de SwiftUI no tiene porque ser necesariamente una estructura.
Implementando UIViewRepresentable
Ya hemos comentado las dos funciones que como mínimo debemos implementar para cumplir con el protocolo.
Dentro de la función makeUIView
debemos situar el código de creación de nuestra vista personalizada. En nuestro caso creamos un WKWebView
y asignamos el delegado.
internal func makeUIView(context: Context) -> WKWebView | |
{ | |
let webView = WKWebView(frame: .zero) | |
webView.navigationDelegate = self | |
return webView | |
} |
La función updateUIView
se emplea para actualizar la UIView
presetada y su coordinador con la última configuración. Esta función se invoca en determinados momentos de la vida de nuestra vista, por ejemplo cuando se va a presentar o retirar de la pantalla.
internal func updateUIView(_ webView: WKWebView, context: Context) -> Void | |
{ | |
guard let wwdcURL = URL(string: "https://developer.apple.com/wwdc19/") else | |
{ | |
return | |
} | |
let wwdcRequest = URLRequest(url: wwdcURL) | |
webView.load(wwdcRequest) | |
} |
Invocar nuestra vista basada en una UIView
se hace de la misma manera que en el resto de vistas de SwiftUI
… | |
.sheet(isPresented: $showNavigatorView) { | |
WebBrowser() | |
.edgesIgnoringSafeArea([ .bottom ]) | |
} | |
… |
El navegador aparecerá embebido contenido en un sheet
y para que ocupe toda la pantalla le indicamos que debe extenderse por la parte inferior de la Safe Area.

Quien dice UIView dice UIViewController
En este caso hemos podido solventar la papeleta con una UIView
pero habrá casos en los que necesitemos hacer uso de un UIViewController como por ejemplo si tenemos que acceder al Carrete de Fotos del dispositivo, en cuyo caso tendremos que hacer uso del controlador UIImagePickerController
La manera de hacerlo e mediante el protocolo UIViewControllerRepresentable
Implementar este protocolo es exactamente igual que la de UIViewRepresentable
, sólo debemos implementar estas dos funciones como mínimo.
/// Para crear el UIViewController | |
func makeUIViewController(context: Self.Context) -> Self.UIViewControllerType | |
/// Para actualizar el UIViewController | |
func updateUIViewController(_ uiViewController: Self.UIViewControllerType, context: Self.Context) -> Void |
Mencionar que Self.UIViewControllerType
es el tipo del controller que queremos embeber, que en este caso sería UIImagePickerController
.
Conclusión
Hasta que todos y cada uno de los widgets de los frameworks UIKit, AppKit, WatchKit y TVUIKit tenga su versión en SwiftUI, Apple nos brinda una manera fácil y elegante de incorporar los widgets que necesitemos y aún no estén en la nueva librería.
Código fuente
La versión de la app DubDub actualizada para este artículo la teneís disponible en este repositorio de GitHub