<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hector Moreno</title>
    <description>The latest articles on DEV Community by Hector Moreno (@hecmsc).</description>
    <link>https://dev.to/hecmsc</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F923103%2F6ff108c6-ca09-46ef-921e-49a77914f754.jpeg</url>
      <title>DEV Community: Hector Moreno</title>
      <link>https://dev.to/hecmsc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hecmsc"/>
    <language>en</language>
    <item>
      <title>MVVM Background Video</title>
      <dc:creator>Hector Moreno</dc:creator>
      <pubDate>Tue, 18 Aug 2026 21:36:53 +0000</pubDate>
      <link>https://dev.to/hecmsc/mvvm-videoplay-swiftui-flc</link>
      <guid>https://dev.to/hecmsc/mvvm-videoplay-swiftui-flc</guid>
      <description>&lt;p&gt;Hey hola este es mi primer post y quiero compartirles como generar una vista con video en MVVM en SwiftUI&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Crearemos nuestro proyecto en SwiftUI.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dentro de la carpeta de nuestro proyecto, crearemos otra carpeta para guardar los videos que utilizaremos en este caso usaremos el nombre de &lt;strong&gt;Resources&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv6gw0pdjyhdh8707em59.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv6gw0pdjyhdh8707em59.png" alt=" " width="300" height="276"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copiamos nuestros archivos en formatos (.MOV, .MP4, .M4V, .HLS / .M3U8) a nuestra carpeta Resources.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb0v4c8840klz3qa4sg9t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb0v4c8840klz3qa4sg9t.png" alt=" " width="318" height="103"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Para tener ordenado nuestro código generaremos dos carpetas la primera sera View y la segunda carpeta sera nuestro ViewModel, dentro de nuestro ViewModel generaremos un EmptyFile y lo llamaremos VideoLooper.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgj35nl4r5u9zxstfco6n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgj35nl4r5u9zxstfco6n.png" alt=" " width="288" height="183"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dentro del archivo VideoLooper haremos un import de SwiftUI y AVFoundation &lt;br&gt;
esta libreria de AVFoundation contiene las herramientas para trabajar con Audio y Video permite la crear, editarm procesar archivos multimedia etc), nosotros la usaremos para insertar un video y reproducirlo infinitamente.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;una vez importadas las librerias usaremos la macro &lt;a class="mentioned-user" href="https://dev.to/observable"&gt;@observable&lt;/a&gt; esta reemplazara el antiguo protocolo ObservableObject y ya no requiere de la macro @Published.&lt;/p&gt;

&lt;p&gt;dentro de la clase generaremos dos variables la primera sera un private(set) que sera de tipo AVQueuePlayer y quizá te preguntes por que private(set) es por que permite que la vista pueda leer el valor al momento de realizar el cambio de estado, la segunda variable a generar será nuestra variable privada looper de tipo AVPlayerLooper esta variable nos permitirá generar el loop sin interrupciones manteniendo una reproducción fluida y es ideal para videos cortos o para usarla en fondos animados, terminando de declarar nuestras variables generaremos una función que llamaremos setUpVideo y al cual le pasaremos el nombre del video que guardamos en el paso 3.&lt;/p&gt;

&lt;p&gt;En nuestra función obtendremos la url de nuestros videos al ser un recurso local usaremos un &lt;strong&gt;guard let Bundle.main.url(forResource: String, withExtension: String)&lt;/strong&gt;&lt;br&gt;
este bundle nos permitira buscar en nuestra carpeta de resources el nombre del video con la extensión del mismo de una manera segura sin generar algun crash dentro de la aplicación.&lt;/p&gt;

&lt;p&gt;debajo de nuestro guard generaremos un item con &lt;strong&gt;AVPlayerItem(url: URL)&lt;/strong&gt; al cual le pasaremos la url  de nuestro archivo, el AVPlayerItem nos permite saber si el archivo multimedia esta listo para reproducirse, también nos proporciona información sobre la duración y progreso del video.&lt;/p&gt;

&lt;p&gt;declararemos un queuePlayer que sera de tipo &lt;strong&gt;AVQueuePlayer&lt;/strong&gt;, al queuePlauyer le pasaremos el item generado con el &lt;strong&gt;AVPlayerItem(url: URL)&lt;/strong&gt; , el queuePlayer lo usaremos para mutear el video ya que sera un video de fondo lo cual quedaria como &lt;strong&gt;queuePlayer.isMuted = true&lt;/strong&gt;, posteriormente a nuestra variable looper le asignaremos el queuePlayer y el item de nuestro video de la siguiente manera &lt;/p&gt;

&lt;p&gt;&lt;code&gt;self.looper = AVPlayerLooper(player: queuePlayer, templateItem: item)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;y a nuestra variable player le pasaremos el queuePlayer.&lt;/p&gt;

&lt;p&gt;Dentro de la clase VideoLooper terminaremos de poner los controles de nuestro video con la función play y pausa.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhpr9i2grv90mynt5wumc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhpr9i2grv90mynt5wumc.png" alt=" " width="611" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Generaremos ahora nuestra vista, debido a que SwiftUI no incluye una vista nativa avanzada para manejar de manera directa todas las capas del video envolveremos nuestra vista en UIKit usando el UIViewRepresentable.&lt;/p&gt;

&lt;p&gt;Primero generaremos el contenedor de nuestra vista la llamaremmos PlayerContainerView de tipo UIView para asegurarnos que se maneje de forma correcta crearemos una subclase de UIView que sobre escribe el layer para que devuelva un AVPlayerLayer.&lt;/p&gt;

&lt;p&gt;Luego generaremos el puente a SwiftUI  el cual será un struc de tipo UIViewRepresentable.&lt;br&gt;
Crearemos la vista con una funcion que llamaremos makeUIView la cual contendra el contexto de nuestra app y devolvera un UIView dentro de makeUIView sobre escribiremos el layer donde ira nuestro video y realizaremos la configuración que requeriremos para la vista en este caso usaremos el videoGravity .resizeAspectFill para que el video se pueda mostrar de manera correcta en toda nuestra pantalla y posterior a esto tendremos el updateUIView el cual nos ayudara a actualizar nuestra vista cada que detecte un cambio esto nos ayudara a evitar crashes en caso de que no contenga ningún video. &lt;/p&gt;

&lt;p&gt;al finalizar nuestro file VideoView quedaria de la siguiente manera.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbwzf916gjfcxzjtkw6no.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbwzf916gjfcxzjtkw6no.png" alt=" " width="416" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;esto se puede separar en archivos diferentes pero al ser pequeños y pertenecer a la creación de la vista decidi que se quedaran en el mismo.&lt;/p&gt;

&lt;p&gt;al finalizar en nuestro ContentView usaremos un @State looper para manejar nuestro video, dentro del body use un ZStack para poder usar componentes sobre componentes en nuestro caso usare un botón que permita controlar el cambio del video.&lt;/p&gt;

&lt;p&gt;generamos una función que detecte que cuando cambia se presione el botón cambie el booleano y ponga un video u otro al final nuestro código queda de la siguiente manera:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F35uuw5xtk8f3z7h74owu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F35uuw5xtk8f3z7h74owu.png" alt=" " width="556" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;y al final agrege un VStack y un spacer para poner el bóton hasta abajo del video  el resultado final es:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F75uphnspb12ma7yerxgb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F75uphnspb12ma7yerxgb.png" alt=" " width="800" height="1712"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8vd1upz1urhy9heiot1e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8vd1upz1urhy9heiot1e.png" alt=" " width="800" height="1516"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ios</category>
      <category>swiftui</category>
      <category>iosdevelopers</category>
      <category>swift</category>
    </item>
  </channel>
</rss>
