View in English

  • Apple Developer
    • Get Started

    Explore Get Started

    • Overview
    • Learn
    • Apple Developer Program

    Stay Updated

    • Latest News
    • Hello Developer
    • Platforms

    Explore Platforms

    • Apple Platforms
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    • App Store

    Featured

    • Design
    • Distribution
    • Games
    • Accessories
    • Web
    • Home
    • CarPlay
    • Technologies

    Explore Technologies

    • Overview
    • Xcode
    • Swift
    • SwiftUI

    Featured

    • Accessibility
    • App Intents
    • Apple Intelligence
    • Games
    • Machine Learning & AI
    • Security
    • Xcode Cloud
    • Community

    Explore Community

    • Overview
    • Meet with Apple events
    • Community-driven events
    • Developer Forums
    • Open Source

    Featured

    • WWDC
    • Swift Student Challenge
    • Developer Stories
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Centers
    • Documentation

    Explore Documentation

    • Documentation Library
    • Technology Overviews
    • Sample Code
    • Human Interface Guidelines
    • Videos

    Release Notes

    • Featured Updates
    • iOS
    • iPadOS
    • macOS
    • watchOS
    • visionOS
    • tvOS
    • Xcode
    • Downloads

    Explore Downloads

    • All Downloads
    • Operating Systems
    • Applications
    • Design Resources

    Featured

    • Xcode
    • TestFlight
    • Fonts
    • SF Symbols
    • Icon Composer
    • Support

    Explore Support

    • Overview
    • Help Guides
    • Developer Forums
    • Feedback Assistant
    • Contact Us

    Featured

    • Account Help
    • App Review Guidelines
    • App Store Connect Help
    • Upcoming Requirements
    • Agreements and Guidelines
    • System Status
  • Quick Links

    • Events
    • News
    • Forums
    • Sample Code
    • Videos
 

Videos

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

Más videos

  • Información
  • Resumen
  • 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

    • Performance and metrics
    • Measuring your app’s power use with Power Profiler
    • Understanding and improving SwiftUI performance
    • Analyzing the performance of your visionOS app
    • Improving app responsiveness
      • 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…
    • 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
    • App Store
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • Icon Composer
    • SF Symbols
    Open Menu Close Menu
    • Accessibility
    • Accessories
    • Apple Intelligence
    • Audio & Video
    • Augmented Reality
    • Business
    • Design
    • Distribution
    • Education
    • Games
    • Health & Fitness
    • In-App Purchase
    • Localization
    • Maps & Location
    • Machine Learning & AI
    • Security
    • Safari & Web
    Open Menu Close Menu
    • Documentation
    • Downloads
    • Sample Code
    • Videos
    Open Menu Close Menu
    • Help Guides & Articles
    • Contact Us
    • Forums
    • Feedback & 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
    • Mini Apps Partner 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
    Read the latest news.
    Get the Apple Developer app.
    Copyright © 2026 Apple Inc. All rights reserved.
    Terms of Use Privacy Policy Agreements and Guidelines