View in English

  • Global Nav Open Menu Global Nav Close Menu
  • Apple Developer
Search
Cancel
  • Apple Developer
  • News
  • Discover
  • Design
  • Develop
  • Distribute
  • Support
  • Account
Only search within “”

Quick Links

5 Quick Links

Videos

Abrir menú Cerrar menú
  • Colecciones
  • Temas
  • Todos los videos
  • Información

Volver a WWDC25

  • Información
  • Resumen
  • Transcripción
  • Código
  • Optimiza el rendimiento de SwiftUI con instrumentos

    Descubre la nueva herramienta SwiftUI. Hablaremos sobre cómo SwiftUI actualiza las vistas, cómo los cambios en los datos de tu app afectan esas actualizaciones y cómo el nuevo instrumento te ayuda a visualizar esas causas y efectos. Para aprovechar al máximo esta sesión, recomendamos familiarizarse con el desarrollo de apps en SwiftUI.

    Capítulos

    • 0:00 - Introducción y agenda
    • 2:19 - Descubre el instrumento SwiftUI.
    • 4:20 - Diagnostica y corrige actualizaciones extensas del cuerpo de la vista
    • 19:54 - Comprende las causas y los efectos de las actualizaciones de SwiftUI
    • 35:01 - Próximos pasos

    Recursos

    • Analyzing the performance of your visionOS app
    • Improving app responsiveness
    • Measuring your app’s power use with Power Profiler
    • Performance and metrics
    • Understanding and improving SwiftUI performance
      • Video HD
      • Video SD

    Videos relacionados

    WWDC25

    • Optimiza el rendimiento del CPU con instrumentos

    WWDC23

    • Analyze hangs with Instruments
    • Demystify SwiftUI performance
    • Explore SwiftUI animation

    WWDC22

    • Compose custom layouts with SwiftUI

    WWDC21

    • Demystify SwiftUI

    Tech Talks

    • Explore UI animation hitches and the render loop
  • Buscar este video…

    Hola. Soy Jed del equipo de Instruments. Yo soy Steven del equipo de Apple Music. Las apps increíbles tienen un gran rendimiento. Cualquier fragmento de código que se ejecute en tu app podría ralentizarla. Analiza tu app para encontrar y corregir los cuellos de botella del código y lograr un desempeño sin problemas. Veremos cómo identificar los cuellos de botella de SwiftUI y a optimizar el código para mejorar el desempeño de tu app. ¿Cómo puedes saber si tienes un problema de rendimiento? Un síntoma que puedes notar es que tu app responde menos debido a problemas o interrupciones. Las animaciones pueden pausarse o saltar, o el desplazamiento puede retrasarse. Lo mejor para identificar problemas de desempeño es crear un perfil de la app con Instruments. Hoy veremos cómo diagnosticar problemas de rendimiento en el código que usa SwiftUI. Presentaremos el nuevo instrumento de SwiftUI incluido con Instruments 26. Analizaremos una app con actualizaciones largas del cuerpo de la vista y usaremos un instrumento para solucionarlo. Por último, profundizaremos en las causas y los efectos de las actualizaciones de SwiftUI. Usaremos el instrumento para identificar actualizaciones innecesarias y eliminarlas. Nos centramos en los problemas de desempeño causados específicamente por tu uso de SwiftUI.

    Si el problema de tu app no está relacionado con SwiftUI, ve los videos “Analyze hangs with Instruments” y “Optimize CPU performance with Instruments”.

    Steven y yo estuvimos trabajando en una app. Steven, ¿puedes mostrarnos lo que creamos hasta ahora? ¡Gracias, Jed! La app se llama Landmarks y muestra algunos de los lugares más increíbles de todo el mundo. Los puntos de referencia muestran la distancia de mí, inspirando sueños tanto de viajes lejanos como rápidos. La app se ve bien, pero al realizar las pruebas noté que el desplazamiento no siempre es fluido. Me encantaría averiguar por qué. Jed, mencionaste el nuevo instrumento SwiftUI. ¿Qué tal un recorrido? ¡Claro! En Instruments 26, presentamos una nueva forma de identificar problemas de desempeño en las apps de SwiftUI: el instrumento SwiftUI de última generación.

    La plantilla SwiftUI actualizada incluye instrumentos diferentes para evaluar el rendimiento de la app. Primero, tenemos el nuevo instrumento SwiftUI, del que hablaré más adelante.

    Incluimos Time Profiler, que muestra ejemplos del trabajo que realiza tu app en el CPU en el tiempo. Y, por último, tenemos los instrumentos Hangs y Hitches, que realizan un seguimiento de la capacidad de respuesta.

    Para detectar problemas de rendimiento, verifica la información de nivel superior del instrumento SwiftUI.

    En el instrumento SwiftUI, la pista “Update Groups” muestra cuando SwiftUI está activo.

    Si el CPU se dispara cuando el carril Update Groups está vacío, quizás tu problema no tenga que ver con SwiftUI. Otros carriles de instrumentos SwiftUI señalan las largas actualizaciones de SwiftUI y sincronización.

    “Long View Body Updates” muestra cuando la propiedad cuerpo de tu vista tarda en ejecutarse. “Long Representable Updates” marca actualizaciones representables lentas de la vista o el controlador de la vista.

    Por último, Other Long Updates muestra todos los demás tipos de trabajo largo de SwiftUI. Estos tres carriles son una vista de alto nivel de las largas actualizaciones que causan problemas de rendimiento. Las actualizaciones están codificadas por colores (naranja/rojo) según la probabilidad de que provoquen problemas. Para evitarlos, investiga las actualizaciones largas, aunque pueden variar por las condiciones del dispositivo.

    Instala Xcode 26 para empezar a utilizar el instrumento SwiftUI. En el dispositivo en el que deseas ejecutar y perfilar tu app, actualiza a las versiones más recientes del SO que incluyen soporte para la grabación de rastros de SwiftUI. Creo que tenemos todo listo para crear un perfil de la app Landmarks por primera vez. -Steven, ¡muéstranos! -¡Gracias, Jed!

    El proyecto ya está abierto en Xcode. Para comenzar a crear perfiles, presionaré Comando + I, y Xcode compilará la app en modo Release e iniciará automáticamente Instruments.

    En el selector de plantillas, elegiré la plantilla SwiftUI y haré clic en el botón de grabación para iniciar la grabación.

    Empezaré desplazándome por la lista de puntos de referencia. Hay una plataforma horizontal de cada continente.

    Me desplazaré horizontalmente hasta el final de la plataforma North America para cargar más vistas.

    Luego, haré clic en dejar de grabar.

    Una vez detenida la grabación, Instruments copia los datos de creación de perfiles del dispositivo y los procesa para su análisis. Una vez completado, puedo usar el instrumento SwiftUI para determinar problemas de rendimiento potenciales. Maximizaré la ventana para que sea más fácil verlo todo.

    Empezaré inspeccionando las líneas de actualización largas de nivel superior en la pista de instrumentos SwiftUI.

    Los cuerpos de vista lentos de SwiftUI son un problema común. Comprobaré primero el carril Long View Body Updates.

    Hay algunas actualizaciones largas en naranja y rojo en esta línea, así que voy a investigarlas. Haré clic para expandir la pista de SwiftUI.

    Esto revela tres subpistas: View Body Updates, Representable Updates y Other Updates. Cada subpista tiene actualizaciones largas resaltadas en naranja y rojo al igual que las líneas de nivel superior. El resto de las actualizaciones están en gris. Seleccionaré la pista View Body Updates.

    En el siguiente panel de detalles, hay un resumen jerárquico de los cuerpos de vista ejecutados durante mi sesión de creación de perfiles. Cuando amplío el proceso de mi app, obtengo una lista de los módulos para todas las actualizaciones del cuerpo de la vista ejecutados.

    Puedo filtrarlas para ver solo las actualizaciones largas haciendo clic en el menú desplegable y seleccionando el resumen de Long View Body Updates.

    Por los recuentos me doy cuenta de que tengo varias actualizaciones largas que investigar.

    Haré clic para expandir el módulo de mi app. LandmarkListItemView tiene varias actualizaciones largas, empezaré con esa vista.

    Aparece una flecha al pasar el cursor por encima del nombre de la vista.

    Al hacer clic en la flecha, aparece un menú contextual. Elegiré “Show Updates” para ver una lista secuencial de todas las actualizaciones largas para el cuerpo de esta vista.

    Hago clic con el botón derecho en una de las actualizaciones largas y, después, en “Set Inspection Range and Zoom”.

    Esto establece la selección en mi rastro en el intervalo para esta actualización del cuerpo de la vista. A continuación, haré clic en la pista del instrumento Time Profiler.

    Aquí es donde puedo ver lo que sucede en el CPU mientras se ejecuta el cuerpo de mi vista.

    Time Profiler recopila datos muestreando lo que se está ejecutando en el CPU a intervalos regulares. Para cada muestra, verifica qué función se está ejecutando y guarda la información en la sesión de creación de perfiles. El panel de detalles del perfil que aparece ahora muestra las pilas de llamadas de las muestras grabadas en el rastro. En este caso, estas son las muestras grabadas mientras se ejecutaba el cuerpo de la vista.

    Mantendré presionada la tecla Opción y haré clic para expandir la pila de llamadas del hilo principal.

    El trabajo de SwiftUI está representado por una pila de llamadas muy profunda. Lo que más me interesa es LandmarkListItemView. Tocaré Comando + F para buscar en la pila de llamadas y escribiré el nombre en el campo de búsqueda.

    Ahí está mi cuerpo de vista. En la columna más a la izquierda, Time Profiler muestra el tiempo empleado en cada fotograma de la pila de llamadas.

    La mayor parte del tiempo del cuerpo de vista se dedicó a la propiedad calculada llamada distancia. En distancia, los dos fotogramas más pesados son llamados a dos formateadores diferentes.

    Este formateador de medidas y este formateador de números. Volvamos a Xcode para ver qué pasa en el código.

    Este es LandmarkListItemView, que es la vista de cada punto de referencia de la lista.

    Y esta es la propiedad distancia que había notado en Time Profiler. Esta convierte mi distancia desde el punto de referencia en una cadena con formato para mostrar en la vista.

    Este es el formateador de números, que Time Profiler me mostró que es costoso de crear.

    Aquí el formateador de medición crea la cadena, que también contribuía al tiempo en el cuerpo de la vista.

    En el cuerpo de la vista, estoy leyendo la propiedad distancia para crear el texto de la etiqueta. Esto sucede cada vez que se ejecuta el cuerpo de la vista y, como este se ejecuta en el hilo principal, mi app tiene que esperar a que se aplique formato al texto de distancia antes de poder seguir actualizando su IU.

    Pero, ¿por qué esto es importante? Un milisegundo para el cuerpo de una vista repercute en el tiempo total de actualización, más con muchas vistas. Jed, ¿cómo debería pensar en el tiempo que tarda SwiftUI en ejecutar los cuerpos de vistas? Esa es una buena pregunta. Empezaré describiendo cómo funciona el bucle renderizado en las plataformas de Apple. En cada fotograma, la app se activa para gestionar eventos, como los toques o las pulsaciones de teclas. Después, actualiza la interfaz de usuario. Esto incluye ejecutar la propiedad cuerpo de cualquier vista de SwiftUI que haya cambiado. Todo este trabajo debe completarse antes del plazo de cada fotograma. Luego, la app transfiere el trabajo al sistema, que muestra las vistas antes del plazo del siguiente fotograma. La salida renderizada finalmente se vuelve visible en la pantalla justo después de ese plazo. Aquí todo funciona como debería. Las actualizaciones terminan antes del plazo de los fotogramas, lo que permite una renderización fluida y visibilidad en pantalla. Comparemos esto con una app con un problema provocado por el cuerpo de una vista que tarda mucho.

    Como antes, gestionamos los eventos primero. Luego, ejecutamos las actualizaciones de la IU. Pero en el primer fotograma, una de las actualizaciones de la IU tardó demasiado. Esto hacía que la parte de actualización de la IU se ejecutara más allá del plazo del fotograma. Esto significa que la siguiente actualización no puede comenzar hasta un fotograma más tarde. Este fotograma no está listo para enviarle información al renderizador en el plazo.

    El fotograma anterior permanece visible en la pantalla hasta que el sistema termine de renderizar el siguiente. A un fotograma que permanece visible en pantalla durante demasiado tiempo, lo llamamos “problema”. Los bloqueos hacen que las animaciones parezcan menos fluidas. Para saber más sobre los problemas, consulta el artículo “Understanding hitches in your app” y esta charla técnica, sobre el bucle de renderizado y los diferentes tipos de problemas. Steven, ¿ahora sabes por qué el tiempo de ejecución del cuerpo de la vista es importante? Sí, ¡eso fue realmente útil! El riesgo de que las actualizaciones del cuerpo de la vista se ejecuten durante más tiempo del necesario puede hacer que mi app no cumpla con el plazo de fotogramas y genere bloqueos. Entonces, necesito calcular la cadena de distancia para cada punto de referencia y almacenarla en caché antes de mostrar la vista, en lugar de hacerlo mientras el cuerpo está en funcionamiento. Volvamos al código.

    Aquí está la propiedad distancia que se ejecuta cada vez que se actualiza el cuerpo de la vista. En lugar de hacer este trabajo mientras se ejecuta el cuerpo de la vista, lo moveré a algún lugar más centralizado: la clase donde administro las actualizaciones de ubicación.

    La clase LocationFinder es responsable de recibir actualizaciones cada vez que mi ubicación cambia. En vez de calcular la distancia formateada en el cuerpo de la vista, puedo crear estas cadenas antes y almacenarlas en caché para que ya se hayan calculado cuando una de mis vistas necesite mostrar una.

    Comenzaré actualizando el inicializador para crear los formateadores que creé antes en el cuerpo de la vista.

    Agregué esta propiedad llamada formateador para almacenar mi formateador de medición.

    En la parte superior del inicializador creo el formateador de números que estaba creando anteriormente en mi vista.

    Y el formateador de medición, que almaceno en la nueva propiedad que agregué. Puedo reutilizar los formateadores cuando las cadenas de distancia precisen actualizarse y evitar el costo de recrear uno cada vez que se ejecuta el cuerpo de la vista. Necesitaré una forma de mantener las cadenas en caché, para que mis vistas puedan usarlas cuando sea necesario. Agregaré código para gestionar esas actualizaciones.

    Tengo un arreglo para almacenar los puntos de referencia, que usaré para calcular las distancias.

    También tengo un diccionario para almacenar en caché las cadenas de distancia después de calcularlas.

    Esta función llamada updateDistances calculará de nuevo las cadenas cuando cambie mi ubicación.

    Usaré el formateador para crear el texto de distancia.

    Y almaceno el texto en mi caché aquí.

    En un momento, llamaré a esta última función desde mi vista para obtener el texto almacenado en caché.

    Hay una última cosa que necesito hacer aquí. Cuando se actualiza mi ubicación, necesito actualizar el caché de cadenas.

    Haré clic en el menú desplegable de la barra de salto y saltaré a la función didUpdateLocations, que CoreLocation llama cuando cambia mi ubicación.

    Llamaré a la función updateDistances que creé.

    Ahora volveré a mi vista.

    Y la actualizaré para usar el valor en caché.

    Estos cambios deberían solucionar las actualizaciones lentas de cuerpo de la vista.

    Veamos un rastro de Instruments tomado con estas correcciones hechas para verificar que todo esté bien.

    Con la pista View Body Updates seleccionada, el resumen Long View Body Updates en el panel de detalles muestra que las actualizaciones largas de LandmarkListItemView desaparecieron.

    Todavía hay dos actualizaciones largas del cuerpo de la vista en el resumen, pero estas actualizaciones ocurren al comienzo del rastro, mientras la app se prepara para renderizar el primer fotograma. Es habitual que las actualizaciones iniciales de la app sean más lentas mientras el sistema crea la jerarquía. Pero esto no provocará ningún bloqueo. Las actualizaciones LandmarkListItemView largas que podrían haber causado bloqueos durante el desplazamiento están solucionadas y desaparecieron de la lista. Puedo confiar en que no estoy ralentizando SwiftUI, ya que funciona para mostrar todas mis vistas en pantalla.

    Arreglar actualizaciones largas del cuerpo de la vista es una excelente forma de mejorar el rendimiento de una app. Pero algo más a tener en cuenta: muchas actualizaciones innecesarias también pueden traer problemas de desempeño. Veamos por qué.

    Aquí está el diagrama que Jed mostró antes. Pero esta vez, no hay una sola actualización que sea más larga que el resto. En cambio, hay muchas actualizaciones relativamente rápidas que deben realizarse durante este fotograma.

    Todo este trabajo extra hace que la app no cumpla con el plazo para enviar el fotograma. Nuevamente, la próxima actualización se retrasa un fotograma. Como no hay nada que enviar al renderizador, hay un bloqueo, ya que el fotograma anterior es visible por dos fotogramas enteros. Menciono el posible impacto en el rendimiento de las actualizaciones innecesarias de vistas porque estuve trabajando en una nueva función para nuestra app en la que creo que esto importará mucho. Al ver los puntos de referencia, me entusiasma la idea de ir a nuevos lugares, pero es difícil poner prioridades. Entonces se me ocurrió una idea para hacerlo más fácil. Déjame mostrarte. Agregué un nuevo botón de corazón a cada punto de referencia, que uso para agregar y eliminar favoritos.

    Déjame mostrarte el código.

    En LandmarkListItemView, agregué esta superposición que muestra el nuevo botón de corazón.

    La acción del botón llama a la función toggleFavorite en mi clase de datos del modelo para marcar o desmarcar el punto de referencia como favorito.

    El ícono de etiqueta muestra un corazón lleno si el punto de referencia está marcado como favorito, o uno vacío si no.

    Con la tecla Comando presionada, haré clic en toggleFavorite para saltar a esa función.

    Y así es como agrego un favorito.

    El modelo almacena una serie de puntos de referencia favoritos. Y agrego el punto de referencia al arreglo cuando se agrega un favorito.

    Para eliminar un favorito, hago lo contrario.

    Y eso es lo que tengo hasta ahora. Sin duda, mi función necesita más trabajo, pero es bueno perfilarla temprano y a menudo durante el desarrollo. Así que veamos cómo funciona mi nueva función. Presionaré Comando + I para compilar la app y volver a Instruments, y haré clic en grabar.

    Me desplazaré hacia abajo hasta la lista North America y tocaré el corazón para marcar Muir Woods como favorito. ¡Porque no está tan lejos de donde vivo, pero por alguna razón, todavía no lo visité! Ahora me desplazaré hacia arriba nuevamente. Y un lugar favorito lejano, ¿qué tal el Monte Fuji? Ahora esa sí que sería una aventura divertida. Ahora detendré la grabación.

    Quiero asegurarme de que al tocar mi nuevo botón favorito no se produzcan actualizaciones innecesarias. Analiza un rastro con una idea en mente de qué esperar y busca anomalías para identificar posibles problemas.

    Haré clic para ampliar la pista de instrumentos SwiftUI y seleccionaré View Body Updates.

    Dado que toqué dos botones favoritos, Muir Woods y Monte Fuji, espero que esas vistas se hayan actualizado. Toqué los botones en la segunda mitad del rastro, después de desplazarme hacia abajo hasta el final. Así que resaltaré esa parte del rastro, para centrarme solo en lo que me interesa.

    Ahora comprobaré el panel de detalles. Ampliaré la jerarquía para encontrar la lista de actualizaciones de mis vistas.

    Me sorprende ver que LandmarkListItemView en realidad se actualiza bastantes veces. ¿Pero por qué? Al depurar una actualización de vista en una app UIKit, generalmente pondría un punto de interrupción en mi código e inspeccionaría el rastreo para averiguar por qué se actualizó. Pero en mis apps de SwiftUI, como Landmarks, esto no me funcionó bien. Las pilas de llamadas de SwiftUI parecen más difíciles de entender. Jed, ¿por qué este enfoque no funciona con las apps de SwiftUI? Déjame explicarte. Xcode te ayuda a comprender la causa y el efecto del código imperativo, como en las apps de UIKit, te muestra rastreos cuando llegas a un punto de interrupción. UIKit es una estructura imperativa, por lo que los rastreos suelen ser útiles para depurar causa y efecto. Aquí, puedo saber que mi etiqueta se está actualizando porque configuro una propiedad isOn en mi viewDidLoad. Mi app parece haberse bloqueado durante el inicio de la primera escena, según el rastreo del sistema. Comparando apps de SwiftUI, veo actualizaciones recursivas dentro de SwiftUI, separadas por marcos en AttributeGraph. Nada de esto me dice por qué mi vista necesita actualizarse específicamente.

    Debido a que SwiftUI es declarativo, no puedes usar el rastreo para comprender por qué se actualiza tu vista. Entonces, ¿cómo puedo entender qué provoca que mis vistas de SwiftUI se actualicen? Primero, necesitarás comprender cómo funciona SwiftUI.

    Te guiaré a través de una pequeña vista de ejemplo para mostrar cómo AttributeGraph define las dependencias entre las vistas y evita volver a ejecutar tu vista a menos que sea necesario. No voy a cubrir todo, pero esta sección será una base para entender cómo las actualizaciones fluyen en tu app.

    Las vistas declaran conformidad con el protocolo de vista. Luego, implementan una propiedad de cuerpo para definir su aspecto y comportamiento regresando otro valor de Vista.

    OnOffView devuelve una vista de texto de su cuerpo y pasa una etiqueta que cambia según el valor de isOn.

    Al agregarla por primera vez a la jerarquía de vistas, SwiftUI recibe un atributo de su vista principal que almacena la estructura de la vista. Las estructuras de vista se recrean con frecuencia, pero los atributos mantienen su identidad y su estado durante toda la vida útil de la vista. A medida que se actualiza la vista principal, el valor de este atributo cambiará, pero su identidad no. Se le pide a la vista que cree sus propios atributos para almacenar su estado y definir su comportamiento. Primero, crea almacenamiento para la variable de estado isOn y un atributo que rastrea cuándo cambia esa variable. Luego, la vista crea un nuevo atributo para ejecutar su cuerpo, que depende de ambos. Cuando se solicita al atributo del cuerpo de la vista que produzca un nuevo valor, lee el valor actual de tu vista pasada desde la vista principal. El atributo actualiza una copia de esa estructura de vista con el valor actual de su variable de estado. Accede a la propiedad calculada “body” en esa copia temporal de tu vista y guarda el valor que devuelve como el valor actualizado del atributo. Dado que el cuerpo de tu vista devolvió una vista Texto, SwiftUI ajusta los atributos necesarios para mostrar texto.

    La vista de texto crea un atributo que depende del entorno para acceder a los estilos predeterminados actuales para determinar cómo debe verse el texto renderizado. Este atributo agrega una dependencia al cuerpo de tu vista para acceder a la cadena que renderizará desde la estructura de texto devuelta. Texto crea otro atributo que crea una descripción de lo que se debe representar según el texto con estilo.

    Ahora, hablemos de lo que sucede cuando cambias una variable de estado. Cuando hacemos esto, SwiftUI no actualiza inmediatamente las vistas. En su lugar, crea una nueva transacción. Una transacción es un cambio de jerarquía de vista de SwiftUI necesario antes del siguiente fotograma.

    Esta transacción marcará el atributo de señal de tu variable de estado como obsoleto. Luego, cuando SwiftUI se prepara para actualizarse para el siguiente fotograma, ejecuta la transacción y aplica la actualización que se programó. Con un atributo marcado como obsoleto, SwiftUI recorre la cadena de atributos que dependen del atributo obsoleto y marca cada uno de ellos como obsoleto con una bandera. La colocación de bandera se realiza muy rápido y todavía no se realiza ningún trabajo adicional. Tras ejecutar cualquier otra transacción, SwiftUI ahora determina qué dibujar en la pantalla para este fotograma. Pero no puede acceder a esa información porque está marcada como obsoleta.

    SwiftUI debe actualizar las dependencias de esta información para decidir qué dibujar.

    Empezando por los que no tienen dependencias obsoletas, como la señal de Estado. Ahora el atributo del cuerpo de la vista está listo para actualizarse. Se ejecuta nuevamente, produciendo un nuevo valor de estructura de texto con una cadena actualizada. Esto se pasa al atributo Apply styling existente y las actualizaciones continúan hasta que se hayan actualizado todos los atributos necesarios para determinar lo que debe dibujarse. Ahora SwiftUI puede responder la pregunta que se hizo: ¿qué debería dibujar en la pantalla?

    La verdadera pregunta es “¿qué marcó mi cuerpo de vista como obsoleto?”. A menudo puedes controlar cuándo las dependencias, como otras vistas, marcan el cuerpo de tu vista como obsoleto. Pero SwiftUI también realiza un trabajo adicional para mostrar tu vista. Si bien este trabajo es necesario y generalmente inevitable, comprender cuándo sucede puede ser valioso. La nueva herramienta SwiftUI ofrece información sobre las causas y efectos de las actualizaciones de vista. El gráfico de causa y efecto registra todas estas relaciones y las muestra en un gráfico parecido a este.

    Comenzamos con la actualización del cuerpo de la vista que estamos investigando. La actualización aparece como un nodo con un ícono que lo identifica como una actualización del cuerpo de la vista y un título que indica a qué tipo de vista corresponde.

    Hay una flecha que apunta hacia él desde un nodo que representa el cambio de estado. La flecha está etiquetada como “actualizar” porque el cambio de estado provocó que la vista se actualizara. Los bordes etiquetados como “Creación” te indican qué hizo que tu vista apareciera en la jerarquía de vistas.

    El título del nodo de cambio de estado indica el nombre de la variable de estado y el tipo de vista asociada. Cuando seleccionas el cambio de estado, se mostrará un rastreo de dónde se actualizó el valor.

    Aquí se puede ver que el cambio de estado ocurrió debido a un gesto, como tocar un botón.

    Steven, ¿qué muestra el gráfico de causa para la app Landmarks? Veamos el gráfico para saber por qué se produjeron las actualizaciones adicionales en el cuerpo de la vista.

    Esta es la vista del gráfico de causa y efecto. Se selecciona el nodo para LandmarkListItemView.body. Los nodos azules en el gráfico son partes de mi propio código o acciones hechas mientras interactuaba con la app. En el gráfico se muestra la cadena de causas y efectos de izquierda a derecha.

    El nodo "Gesto" representa mis toques del botón favorito.

    Esto provocó que se actualizara el conjunto de puntos de referencia favoritos, lo que provocó que el cuerpo de LandmarkListItemView se actualizara varias veces. Eso es mucho más de lo que esperaba.

    Parece que al tocar un solo botón favorito se pueden actualizar muchas vistas de elementos en la pantalla. Así que vamos a averiguar qué está pasando aquí volviendo al código.

    Volveré a LandmarkListItemView y verifico si un punto de referencia está marcado como favorito llamando a modelData.isFavorite y pasando el punto de referencia. ModelData es mi objeto de modelo de nivel superior, que usa la macro @Observable para permitir que SwiftUI actualice mi vista a medida que cambian sus propiedades. Con la tecla Comando presionada, haré clic en isFavorite para saltar a esa función.

    Accedo al arreglo favoritesCollection.landmarks para verificar si este punto de referencia es un favorito. Esto hace que @Observable establezca una dependencia entre cada vista de elemento y todo el arreglo de favoritos. Cada vez que agrego un favorito al arreglo, se ejecuta el cuerpo de cada vista de elemento, ya que el arreglo cambió. Déjame mostrarte cómo funciona esto.

    Estos son algunos de mis LandmarkListItemViews y esta es mi clase ModelData con favoritesCollection, que da seguimiento de mis puntos de referencia favoritos. Actualmente, mi único favorito es el punto número dos. � La clase ModelData tiene una función isFavorite y cada LandmarkListItemView llama a esta función para determinar si el ícono debe resaltarse o no.

    isFavorite verifica la colección para ver si contiene el punto de referencia y cada vista muestra su propio botón. Debido a que cada vista accedió al arreglo de favoritos, aunque indirectamente, la macro @Observable creó una dependencia para cada vista en todo el arreglo de favoritos.

    ¿Qué sucede cuando quiero agregar un nuevo favorito tocando el botón de favoritos en una de mis otras vistas? La vista llama a toggleFavorite, que agrega un nuevo punto de referencia a mis favoritos. Debido a que todas mis LandmarkListItemViews tienen una dependencia de favoritesCollection, todas las vistas se marcan como obsoletas y sus cuerpos se ejecutan nuevamente.

    Pero eso no es ideal, porque la única vista que realmente cambié fue la vista número tres. Necesito que las dependencias de datos de mi vista sean más granulares, para que cuando cambien los datos de mi app, solo se actualicen los cuerpos de vista necesarios.

    Así que repensemos esto un poco. Una de mis vistas tiene un punto de referencia que tiene su propio estado de favorito; marcado o no.

    Para realizar un seguimiento de ese estado, crearé un modelo de vista Observable para mi vista. La propiedad isFavorite rastrea el estado favorito, y cada vista tendrá su propio modelo de vista.

    Ahora puedo almacenar mis modelos de vista en la clase ModelData. Cada vista puede recuperar su propio modelo y activar y desactivar el favorito según sea necesario. Cada vista solo depende directamente del modelo de vista de su propio punto de referencia. ¡Agreguemos otro favorito! Al tocar el botón, se llama toggleFavorite, que actualiza el modelo de vista para la número uno. Como la vista número uno solo depende de su propio modelo de vista, es la única cuyo cuerpo se ejecuta de nuevo. Descubramos cómo resultaron estos cambios en Landmarks.

    Aquí hay un rastro que registré después de implementar las nuevas mejoras del modelo de vista. Haré clic nuevamente en la subpista View Body Updates. Seleccionaré la misma parte de la línea de tiempo de antes.

    En el panel de detalles, ampliaré el proceso y el módulo Landmarks.

    Por ahora solo hay dos actualizaciones. Como cambié dos favoritos, eso parece correcto, pero revisemos el gráfico. Pasaré el cursor sobre el nombre de la vista, tocaré la flecha y “Show Cause and Effect Graph”.

    Y aquí está el gráfico nuevamente.

    Ahora, la flecha del nodo @Observable al cuerpo de mi vista solo muestra dos actualizaciones, una para cada botón. Al reemplazar la dependencia de la vista de cada elemento en todo el arreglo de favoritos, con un modelo de vista estrechamente acoplado, eliminé una cantidad sustancial de actualizaciones innecesarias del cuerpo de la vista, lo que ayudará a que mi app funcione sin problemas. En este ejemplo, el gráfico era relativamente pequeño, porque las causas de las actualizaciones del cuerpo de mi vista eran muy limitadas. Sin embargo, el gráfico puede ser mucho más grande cuando hay más causas distintas. Una forma en que esto puede suceder es cuando una vista lee desde el Entorno. Jed, ¿puedes mostrarnos un ejemplo? ¡Claro! Los valores del entorno se almacenan en la estructura EnvironmentValues (un tipo de valor, como un diccionario). Cada una de estas vistas tiene una dependencia de toda la estructura EnvironmentValues, porque cada vista accede al entorno utilizando el contenedor de propiedades del entorno. Cuando se actualiza un valor en el entorno, se notifica a cada vista con una dependencia del entorno que es posible que su cuerpo deba ejecutarse. Cada una de estas vistas verifica si el valor que está leyendo cambió. Si el valor cambió, el cuerpo de la vista debe ejecutarse de nuevo. Si no cambió, SwiftUI puede omitir la ejecución del cuerpo de la vista porque la vista ya está actualizada. Exploremos cómo se ven estas actualizaciones en el gráfico de causa y efecto.

    Hay dos tipos principales de nodos en el gráfico que representan actualizaciones del entorno. Las actualizaciones del entorno externo incluyen elementos a nivel de app que se actualizan desde fuera de SwiftUI. Las actualizaciones de EnvironmentWriter son cambios en un valor del entorno que ocurren dentro de SwiftUI. Las actualizaciones que realices en la app mediante el modificador de entorno dot entran en esta categoría. Digamos que el valor del entorno del esquema de color se actualiza porque el dispositivo cambia al modo oscuro. ¿Cómo se vería esto en el gráfico de causa y efecto para estas vistas? El gráfico mostrará un nodo para “External Environment” para View1, ya que este esquema es una actualización del entorno a nivel de sistema. En el gráfico también se mostrará un nodo que indica que el cuerpo de View1 se ejecutó. Como View2 también lee el entorno, tiene como causa una actualización de entorno externo en el gráfico. Pero View2 no lee el valor del esquema de color, por lo que el cuerpo no se ejecuta. En el gráfico, una actualización de vista en la que el cuerpo no se ejecutó se representa con un ícono atenuado. En este caso, estos dos nodos del entorno externo representan la misma actualización. Si pasas el cursor por encima o haces clic en cualquiera de los nodos para la misma actualización, ambos se resaltarán al mismo tiempo para que sea más fácil identificarlas. Para ejecutarse como resultado de una actualización del entorno, sigue existiendo un costo asociado a la búsqueda de actualizaciones en el valor de interés para la vista. El tiempo empleado puede acumularse rápidamente si tu app tiene muchas vistas del entorno. Por eso es importante evitar almacenar valores que se actualizan con mucha frecuencia, como valores de geometría o temporizadores, en el entorno. Y ese es el gráfico de causa y efecto. Es ideal para visualizar el flujo de datos, para garantizar que las vistas se actualizan solo cuando sea necesario. En esta sesión, vimos algunas prácticas recomendadas para lograr un excelente rendimiento en la app de SwiftUI. Es importante mantener la velocidad de los cuerpos de vistas para que SwiftUI tenga tiempo suficiente para mostrar la IU en la pantalla sin demora. Las actualizaciones innecesarias del cuerpo de la vista pueden acumularse. Diseña el flujo de datos para actualizar las vistas solo cuando sea necesario y cuida los cambios frecuentes de dependencias. Recuerda usar Instruments de forma anticipada y frecuente para analizar el rendimiento de tu app en el desarrollo. Sé que abarcamos mucho hoy. Pero lo más importante es esto; asegúrate de que tus cuerpos de vista se actualicen rápidamente y solo cuando sea necesario para lograr un excelente rendimiento de SwiftUI. Usa el instrumento SwiftUI para verificar el rendimiento de tu app a lo largo del proceso.

    En la sesión de hoy, mostramos cómo crear perfiles de apps con el instrumento SwiftUI, pero hay mucho más por explorar. Para conocer las otras funciones del instrumento, consulta los documentos en la descripción del video. También agregamos enlaces a más videos y material sobre el análisis y la mejora del desempeño de la app. ¡Gracias por acompañarnos! Nos entusiasma ver cómo obtienes el mejor rendimiento de tus apps con el nuevo SwiftUI.

    • 8:47 - LandmarkListItemView

      import SwiftUI
      import CoreLocation
      
      /// A view that shows a single landmark in a list.
      struct LandmarkListItemView: View {
          @Environment(ModelData.self) private var modelData
      
          let landmark: Landmark
      
          var body: some View {
              Image(landmark.thumbnailImageName)
                  .resizable()
                  .aspectRatio(contentMode: .fill)
                  .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
                  .overlay { ... }
                  .clipped()
                  .cornerRadius(Constants.cornerRadius)
                  .overlay(alignment: .bottom) {
                      VStack(spacing: 6) {
                          Text(landmark.name)
                              .font(.title3).fontWeight(.semibold)
                              .multilineTextAlignment(.center)
                              .foregroundColor(.white)
      
                          if let distance {
                              Text(distance)
                                  .font(.callout)
                                  .foregroundStyle(.white.opacity(0.9))
                                  .padding(.bottom)
                          }
                      }
                  }
                  .contextMenu { ... }
          }
      
          private var distance: String? {
              guard let currentLocation = modelData.locationFinder.currentLocation else { return nil }
              let distance = currentLocation.distance(from: landmark.clLocation)
      
              let numberFormatter = NumberFormatter()
              numberFormatter.numberStyle = .decimal
              numberFormatter.maximumFractionDigits = 0
      
              let formatter = MeasurementFormatter()
              formatter.locale = Locale.current
              formatter.unitStyle = .medium
              formatter.unitOptions = .naturalScale
              formatter.numberFormatter = numberFormatter
              return formatter.string(from: Measurement(value: distance, unit: UnitLength.meters))
          }
      }
    • 12:13 - LocationFinder Class with Cached Distance Strings

      import CoreLocation
      
      /// A class the app uses to find the current location.
      @Observable
      class LocationFinder: NSObject {
          var currentLocation: CLLocation?
          private let currentLocationManager: CLLocationManager = CLLocationManager()
      
          private let formatter: MeasurementFormatter
      
          override init() {
              // Format the numeric distance
              let numberFormatter = NumberFormatter()
              numberFormatter.numberStyle = .decimal
              numberFormatter.maximumFractionDigits = 0
      
              // Format the measurement based on the current locale
              let formatter = MeasurementFormatter()
              formatter.locale = Locale.current
              formatter.unitStyle = .medium
              formatter.unitOptions = .naturalScale
              formatter.numberFormatter = numberFormatter
              self.formatter = formatter
      
              super.init()
              
              currentLocationManager.desiredAccuracy = kCLLocationAccuracyKilometer
              currentLocationManager.delegate = self
          }
      
          // MARK: - Landmark Distance
      
          var landmarks: [Landmark] = [] {
              didSet {
                  updateDistances()
              }
          }
      
          private var distanceCache: [Landmark.ID: String] = [:]
      
          private func updateDistances() {
              guard let currentLocation else { return }
      
              // Populate the cache with each formatted distance string
              self.distanceCache = landmarks.reduce(into: [:]) { result, landmark in
                  let distance = self.formatter.string(
                      from: Measurement(
                          value: currentLocation.distance(from: landmark.clLocation),
                          unit: UnitLength.meters
                      )
                  )
                  result[landmark.id] = distance
              }
          }
      
          // Call this function from the view to access the cached value
          func distance(from landmark: Landmark) -> String? {
              distanceCache[landmark.id]
          }
      }
      
      extension LocationFinder: CLLocationManagerDelegate {
          func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
              switch currentLocationManager.authorizationStatus {
              case .authorizedWhenInUse, .authorizedAlways:
                  currentLocationManager.requestLocation()
              case .notDetermined:
                  currentLocationManager.requestWhenInUseAuthorization()
              default:
                  currentLocationManager.stopUpdatingLocation()
              }
          }
          
          func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
              print("Found a location.")
              currentLocation = locations.last
              // Update the distance strings when the location changes
              updateDistances() 
          }
          
          func locationManager(_ manager: CLLocationManager, didFailWithError error: any Error) {
              print("Received an error while trying to find a location: \(error.localizedDescription).")
              currentLocationManager.stopUpdatingLocation()
          }
      }
    • 16:51 - LandmarkListItemView with Favorite Button

      import SwiftUI
      import CoreLocation
      
      /// A view that shows a single landmark in a list.
      struct LandmarkListItemView: View {
          @Environment(ModelData.self) private var modelData
      
          let landmark: Landmark
      
          var body: some View {
              Image(landmark.thumbnailImageName)
                  .resizable()
                  .aspectRatio(contentMode: .fill)
                  .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
                  .overlay { ... }
                  .clipped()
                  .cornerRadius(Constants.cornerRadius)
                  .overlay(alignment: .bottom) { ... }
                  .contextMenu { ... }
                  .overlay(alignment: .topTrailing) {
                      let isFavorite = modelData.isFavorite(landmark)
                      Button {
                          modelData.toggleFavorite(landmark)
                      } label: {
                          Label {
                              Text(isFavorite ? "Remove Favorite" : "Add Favorite")
                          } icon: {
                              Image(systemName: "heart")
                                  .symbolVariant(isFavorite ? .fill : .none)
                                  .contentTransition(.symbolEffect)
                                  .font(.title)
                                  .foregroundStyle(.background)
                                  .shadow(color: .primary.opacity(0.25), radius: 2, x: 0, y: 0)
                          }
                      }
                      .labelStyle(.iconOnly)
                      .padding()
                  }
          }
      }
    • 17:20 - ModelData Class

      /// A structure that defines a collection of landmarks.
      @Observable
      class LandmarkCollection: Identifiable {
          // ...
          var landmarks: [Landmark] = []
          // ...
      }
      
      /// A class the app uses to store and manage model data.
      @Observable @MainActor
      class ModelData {
          // ...
          var favoritesCollection: LandmarkCollection!
          // ...
      
          func isFavorite(_ landmark: Landmark) -> Bool {
              var isFavorite: Bool = false
              
              if favoritesCollection.landmarks.firstIndex(of: landmark) != nil {
                  isFavorite = true
              }
              
              return isFavorite
          }
      
          func toggleFavorite(_ landmark: Landmark) {
              if isFavorite(landmark) {
                  removeFavorite(landmark)
              } else {
                  addFavorite(landmark)
              }
          }
      
          func addFavorite(_ landmark: Landmark) {
              favoritesCollection.landmarks.append(landmark)
          }
      
          func removeFavorite(_ landmark: Landmark) {
              if let landmarkIndex = favoritesCollection.landmarks.firstIndex(of: landmark) {
                  favoritesCollection.landmarks.remove(at: landmarkIndex)
              }
          }
          // ...
      }
    • 20:50 - OnOffView

      struct OnOffView: View {
          @State private var isOn = true
          var body: some View {
              Text(isOn ? "On" : "Off")
          }
      }
    • 29:21 - Favorites View Model Class

      @Observable class ViewModel {
          var isFavorite: Bool
          
          init(isFavorite: Bool = false) {
              self.isFavorite = isFavorite
          }
      }
    • 29:21 - ModelData Class with New ViewModel

      @Observable @MainActor
      class ModelData {
          // ...
          var favoritesCollection: LandmarkCollection!
          // ...
      
          @Observable class ViewModel {
              var isFavorite: Bool
              init(isFavorite: Bool = false) {
                  self.isFavorite = isFavorite
              }
          }
      
          // Don't observe this property because we only need to react to changes
          // to each view model individually, rather than the whole dictionary
          @ObservationIgnored private var viewModels: [Landmark.ID: ViewModel] = [:]
      
          private func viewModel(for landmark: Landmark) -> ViewModel {
              // Create a new view model for a landmark on first access
              if viewModels[landmark.id] == nil {
                  viewModels[landmark.id] = ViewModel()
              }
              return viewModels[landmark.id]!
          }
      
          func isFavorite(_ landmark: Landmark) -> Bool {
              // When a SwiftUI view, such as LandmarkListItemView, calls
              // `isFavorite` from its body, accessing `isFavorite` on the 
              // view model here establishes a direct dependency between
              // the view and the view model
              viewModel(for: landmark).isFavorite
          }
      
          func toggleFavorite(_ landmark: Landmark) {
              if isFavorite(landmark) {
                  removeFavorite(landmark)
              } else {
                  addFavorite(landmark)
              }
          }
      
          func addFavorite(_ landmark: Landmark) {
              favoritesCollection.landmarks.append(landmark)
              viewModel(for: landmark).isFavorite = true
          }
      
          func removeFavorite(_ landmark: Landmark) {
              if let landmarkIndex = favoritesCollection.landmarks.firstIndex(of: landmark) {
                  favoritesCollection.landmarks.remove(at: landmarkIndex)
              }
              viewModel(for: landmark).isFavorite = false
          }
          // ...
      }
    • 31:34 - Cause and effect: EnvironmentValues

      struct View1: View {
          @Environment(\.colorScheme)
          private var colorScheme
      
          var body: some View {
              Text(colorScheme == .dark
                      ? "Dark Mode"
                      : "Light Mode")
          }
      }
      
      struct View2: View {
          @Environment(\.counter) private var counter
      
          var body: some View {
              Text("\(counter)")
          }
      }
    • 0:00 - Introducción y agenda
    • Aprende a optimizar el rendimiento de las apps de SwiftUI con el nuevo instrumento y plantilla de SwiftUI en Instruments 26. Puedes usar Instruments para crear perfiles en tus apps e identificar cuellos de botella, como actualizaciones extensas del cuerpo de la vista y actualizaciones innecesarias de SwiftUI, que pueden causar fallas, interrupciones, animaciones y transiciones pausadas, y un desplazamiento lento. La app de ejemplo que se presenta, llamada Landmarks, muestra puntos de referencia de todo el mundo y sus distancias desde la ubicación del usuario. Averigua cómo usar la nueva herramienta de SwiftUI para mejorar la fluidez de desplazamiento de tus apps mediante el diagnóstico y la resolución de problemas de rendimiento en el código SwiftUI.

    • 2:19 - Descubre el instrumento SwiftUI.
    • Instruments 26 presenta un nuevo instrumento y plantilla de SwiftUI para crear perfiles de apps de SwiftUI. Similar a los instrumentos Time Profiler y Hangs and Hitches, ayuda a identificar problemas de rendimiento. La ruta Update Groups muestra el trabajo de SwiftUI. Las otras tres rutas resaltan actualizaciones extensas del cuerpo de la vista, actualizaciones representables extensas y otras actualizaciones de color naranja y rojo según su probabilidad de causar fallas o interrupciones. Para usar la nueva herramienta de SwiftUI, instala Xcode 26 y actualiza el sistema operativo de tu dispositivo para obtener la versión más reciente.

    • 4:20 - Diagnostica y corrige actualizaciones extensas del cuerpo de la vista
    • En el ejemplo se usa Xcode 26 e Instruments 26 para crear perfiles de la app Landmarks, que se escribió en SwiftUI. Primero, abre Instruments y selecciona la plantilla de SwiftUI para registrar el rendimiento de la app. Luego, interactúa con la app en el iPhone y desplázate por una lista de puntos de referencia, que carga vistas adicionales. Una vez que se detiene la grabación, Instruments procesa los datos y luego puedes analizar el seguimiento de SwiftUI. Concéntrate en la ruta Long View Body Updates, donde puedes identificar vistas específicas, como LandmarkListItemView, que causan problemas de rendimiento. Al ampliar el seguimiento de SwiftUI y usar la herramienta Time Profiler, puedes profundizar en el uso del CPU durante las actualizaciones del cuerpo de la vista. Descubrirás que ciertas propiedades calculadas, particularmente los formateadores que se usan para convertir y mostrar datos de distancia, consumen demasiado tiempo. Ten en cuenta la importancia de optimizar el tiempo de ejecución del cuerpo de la vista en SwiftUI, en específico los que se ejecutan en el hilo principal. Cualquier demora puede provocar que la app no cumpla con los plazos de los cuadros, lo que generará fallas. Estas hacen que las animaciones parezcan menos fluidas y pueden afectar negativamente la experiencia general del usuario. Para solucionar estos problemas de rendimiento en el proyecto de ejemplo, calcula y almacena en caché la cadena de distancia con antelación, en lugar de realizar estos cálculos durante la actualización del cuerpo de la vista, para garantizar un rendimiento más fluido y con mayor capacidad de respuesta de la app. En Xcode, hay un proceso de optimización en la clase LocationFinder que gestiona las actualizaciones de ubicación. Anteriormente, el sistema calculaba cadenas de distancia formateadas dentro del cuerpo de la vista de LandmarkListItemView, lo que generaba actualizaciones ineficientes. Para solucionar esto, el código mueve esta lógica a la clase LocationFinder. Aquí, el sistema crea y almacena formateadores en el inicializador para reusarlos, evitando la creación redundante. Un diccionario almacena en caché las cadenas de distancia después del cálculo. La función updateDistances es responsable de volver a calcular estas cadenas cuando hay un cambio de ubicación. Esta usa los formateadores que se crearon antes para generar la cadena de distancia y almacenarla en caché. La estructura CoreLocation invoca al método locationManager(_:didUpdateLocations:) en su objeto CLLocationManagerDelegate cuando cambia la ubicación del dispositivo. Al invocar updateDistances dentro de este método, la memoria caché se mantiene actualizada. Luego, las vistas recuperan las cadenas de distancia almacenadas en caché, lo que elimina la necesidad de realizar nuevos cálculos durante las actualizaciones del cuerpo de la vista. Luego, puedes agregar una nueva funcionalidad: un botón de corazón para marcar puntos de referencia favoritos. Cuando una persona toca el botón, se invoca la función toggleFavorite, que actualiza la clase de datos del modelo para agregar o eliminar lugares de interés de la lista de favoritos. La vista luego refleja este cambio mostrando el ícono de corazón lleno o vacío. Al crear un perfil de la nueva funcionalidad de favoritos de la app en Instruments, es posible que notes que LandmarkListItemView se actualiza con más frecuencia de lo esperado. Este comportamiento inesperado impulsa una investigación sobre la lógica de actualización de la vista, lo que resalta los retos en la depuración de actualizaciones de vista en las apps de SwiftUI en comparación con las apps de UIKit, donde la inspección tradicional basada en puntos de interrupción puede no ser tan sencilla para las estructuras declarativas.

    • 19:54 - Comprende las causas y los efectos de las actualizaciones de SwiftUI
    • En Xcode, la depuración de código imperativo, como en las apps de UIKit, es sencilla mediante el uso de seguimientos inversos. Sin embargo, este enfoque se vuelve menos efectivo con SwiftUI debido a su naturaleza declarativa. El modelo de datos de SwiftUI, AttributeGraph, administra las dependencias entre vistas, optimizando las actualizaciones. Cuando se declara una vista de SwiftUI, se ajusta al protocolo View y se define su apariencia y comportamiento a través de la propiedad body. Esta arroja otro valor View, y SwiftUI gestiona internamente el estado de la vista y las actualizaciones mediante atributos. Los cambios en las variables de estado desencadenan transacciones y marcan los atributos relevantes como obsoletos. Luego, SwiftUI actualiza de manera eficiente la jerarquía de vistas durante el siguiente cuadro, recorriendo la cadena de dependencia para actualizar solo las partes necesarias. Para comprender por qué se actualizó una vista de SwiftUI, puedes usar el nuevo gráfico de causa y efecto del instrumento de SwiftUI. Este gráfico visualiza las relaciones entre las actualizaciones y muestra la cadena de causas, que va desde las interacciones del usuario, como los gestos, hasta los cambios de estado y, en última instancia, las actualizaciones del cuerpo de la vista. Al examinar este gráfico, puedes identificar ineficiencias, como actualizaciones innecesarias, y optimizar su código en consecuencia. En la app Landmarks, la clase ModelData contiene la propiedad favoritesCollection, que almacena los puntos de referencia favoritos en una matriz. Inicialmente, cada LandmarkListItemView verificaba si un punto de referencia era un favorito accediendo a toda la matriz favoritesCollection, creando una dependencia entre cada vista de elemento y toda la matriz. Esto generó un rendimiento ineficiente ya que, cada vez que se agregaba un favorito, se ejecutaba el cuerpo de todas las vistas del elemento. Para abordar este problema, se replanteó el enfoque. Se creó un modelo de datos Observable para cada lugar de interés, almacenando directamente su estado de favorito. Cada LandmarkListItemView ahora tiene su propio modelo de datos, lo que elimina la dependencia de la matriz completa de favoritos. Al implementar este cambio, el sistema solo actualiza el cuerpo de vista necesario cuando el usuario activa o desactiva un favorito. Esta optimización mejora significativamente el rendimiento, como lo demuestra la cantidad reducida de actualizaciones del cuerpo de la vista que se puede observar en el gráfico de causa y efecto. El gráfico también muestra cómo las actualizaciones del entorno, como los cambios en el esquema de colores, pueden afectar las vistas. Incluso si no es necesario ejecutar el cuerpo de una vista debido a una actualización del entorno, aún existe un costo asociado a la verificación de estas actualizaciones, por lo que es importante evitar almacenar valores que cambian con frecuencia en el entorno.

    • 35:01 - Próximos pasos
    • Para el nuevo instrumento de SwiftUI en Instruments 26, hay funcionalidades adicionales, videos y recursos relacionados sobre el análisis y la mejora del rendimiento de las apps disponibles en la documentación para desarrolladores.

Developer Footer

  • Videos
  • WWDC25
  • Optimiza el rendimiento de SwiftUI con instrumentos
  • Open Menu Close Menu
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • Icon Composer
    • SF Symbols
    Open Menu Close Menu
    • Accessibility
    • Accessories
    • App Store
    • Audio & Video
    • Augmented Reality
    • Business
    • Design
    • Distribution
    • Education
    • Fonts
    • Games
    • Health & Fitness
    • In-App Purchase
    • Localization
    • Maps & Location
    • Machine Learning & AI
    • Open Source
    • Security
    • Safari & Web
    Open Menu Close Menu
    • Documentation
    • Sample Code
    • Tutorials
    • Downloads
    • Forums
    • Videos
    Open Menu Close Menu
    • Support Articles
    • Contact Us
    • Bug Reporting
    • System Status
    Open Menu Close Menu
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles
    • Feedback Assistant
    Open Menu Close Menu
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program
    • News Partner Program
    • Video Partner Program
    • Security Bounty Program
    • Security Research Device Program
    Open Menu Close Menu
    • Meet with Apple
    • Apple Developer Centers
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Academies
    • WWDC
    Get the Apple Developer app.
    Copyright © 2025 Apple Inc. All rights reserved.
    Terms of Use Privacy Policy Agreements and Guidelines