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
  • SwiftData: Analiza a detalle la herencia y migración de esquemas

    Descubre cómo usar la herencia de clases para modelar tus datos. Aprende a optimizar las consultas y migrar sin problemas los datos de tu app para usar la herencia. Explora la subclasificación para crear gráficos de modelos, crear búsquedas y consultas eficientes e implementar migraciones de esquemas sólidas. Comprende cómo usar el historial observable y persistente para un seguimiento eficiente de los cambios.

    Capítulos

    • 0:00 - Introducción
    • 2:11 - Aprovecha la herencia de clases
    • 7:39 - Evolución de datos con la migración
    • 11:27 - Adaptación de los datos obtenidos
    • 13:54 - Observación de cambios en los datos
    • 18:28 - Próximos pasos

    Recursos

    • Building rich SwiftUI text experiences
    • SwiftData
    • Adopting SwiftData for a Core Data app
      • Video HD
      • Video SD

    Videos relacionados

    WWDC25

    • Novedades de SwiftUI

    WWDC24

    • Track model changes with SwiftData history
  • Buscar este video…
    • 1:07 - Import SwiftData and add @Model

      // Trip Models decorated with @Model
      import Foundation
      import SwiftData
      
      @Model
      class Trip {
        var name: String
        var destination: String
        var startDate: Date
        var endDate: Date
        
        var bucketList: [BucketListItem] = [BucketListItem]()
        var livingAccommodation: LivingAccommodation?
      }
      
      @Model
      class BucketListItem { ... }
      
      @Model
      class LivingAccommodation { ... }
    • 1:18 - Add modelContainer modifier

      // SampleTrip App using modelContainer Scene modifier
      
      import SwiftUI
      import SwiftData
      
      @main
      struct TripsApp: App {
        var body: some Scene {
          WindowGroup {
            ContentView()
          }
          .modelContainer(for: Trip.self)
        }
      }
    • 1:30 - Adopt @Query

      // Trip App using @Query
      import SwiftUI
      import SwiftData
      
      struct ContentView: View {
        @Query
        var trips: [Trip]
      
        var body: some View {
          NavigationSplitView {
            List(selection: $selection) {
              ForEach(trips) { trip in
                TripListItem(trip: trip)
              }
            }
          }
        }
      }
    • 3:28 - Add subclasses to Trip

      // Trip Model extended with two new subclasses
      
      @Model
      class Trip { 
        var name: String
        var destination: String
        var startDate: Date
        var endDate: Date
        
        var bucketList: [BucketListItem] = [BucketListItem]()
        var livingAccommodation: LivingAccommodation?
      }
      
      @available(iOS 26, *)
      @Model
      class BusinessTrip: Trip {
        var perdiem: Double = 0.0
      }
      
      @available(iOS 26, *)
      @Model
      class PersonalTrip: Trip {
        enum Reason: String, CaseIterable, Codable {
          case family
          case reunion
          case wellness
        }
        
        var reason: Reason
      }
    • 4:03 - Update modelContainer modifier

      // SampleTrip App using modelContainer Scene modifier
      
      import SwiftUI
      import SwiftData
      
      @main
      struct TripsApp: App {
        var body: some Scene {
          WindowGroup {
            ContentView()
          }
          .modelContainer(for: [Trip.self, BusinessTrip.self, PersonalTrip.self])
        }
      }
    • 7:06 - Add segmented control to drive a predicate to filter by Type

      // Trip App add segmented control
      import SwiftUI
      import SwiftData
      
      struct ContentView: View {
        @Query
        var trips: [Trip]
        
        enum Segment: String, CaseIterable {
          case all = "All"
          case personal = "Personal"
          case business = "Business"
        }
        
        init() {
          let classPredicate: Predicate<Trip>? = {
            switch segment.wrappedValue {
            case .personal:
              return #Predicate { $0 is PersonalTrip }
            case .business:
              return #Predicate { $0 is BusinessTrip }
            default:
              return nil
            }
          }
          _trips = Query(filter: classPredicate, sort: \.startDate, order: .forward)
        }
        
        var body: some View { ... }
      }
    • 8:26 - SampleTrips Versioned Schema 2.0

      enum SampleTripsSchemaV2: VersionedSchema {
        static var versionIdentifier: Schema.Version { Schema.Version(2, 0, 0) }
        static var models: [any PersistentModel.Type] {
          [SampleTripsSchemaV2.Trip.self, BucketListItem.self, LivingAccommodation.self]
        }
      
        @Model
        class Trip {
          @Attribute(.unique) var name: String
          var destination: String
      
          @Attribute(originalName: "start_date") var startDate: Date
          @Attribute(originalName: "end_date") var endDate: Date
          
          var bucketList: [BucketListItem]? = []
          var livingAccommodation: LivingAccommodation?
          
          ...
        }
      }
    • 8:41 - SampleTrips Custom Migration Stage from Version 1.0 to 2.0

      static let migrateV1toV2 = MigrationStage.custom(
         fromVersion: SampleTripsSchemaV1.self,
         toVersion: SampleTripsSchemaV2.self,
         willMigrate: { context in
            let fetchDesc =  FetchDescriptor<SampleTripsSchemaV1.Trip>()
            let trips = try? context.fetch(fetchDesc)
        
            // De-duplicate Trip instances here...
      
            try? context.save()
          }, 
          didMigrate: nil
      )
    • 9:09 - SampleTrips Versioned Schema 3.0

      enum SampleTripsSchemaV3: VersionedSchema {
        static var versionIdentifier: Schema.Version { Schema.Version(3, 0, 0) }
        static var models: [any PersistentModel.Type] {
          [SampleTripsSchemaV3.Trip.self, BucketListItem.self, LivingAccommodation.self]
        }
      
        @Model
        class Trip {
          #Unique<Trip>([\.name, \.startDate, \.endDate])
          #Index<Trip>([\.name], [\.startDate], [\.endDate], [\.name, \.startDate, \.endDate])
      
          @Attribute(.preserveValueOnDeletion)
          var name: String
          
          @Attribute(hashModifier:@"v3")
          var destination: String
      
          @Attribute(.preserveValueOnDeletion, originalName: "start_date")
          var startDate: Date
      
          @Attribute(.preserveValueOnDeletion, originalName: "end_date")
          var endDate: Date
        }
      }
    • 9:33 - SampleTrips Custom Migration Stage from Version 2.0 to 3.0

      static let migrateV2toV3 = MigrationStage.custom(
        fromVersion: SampleTripsSchemaV2.self,
        toVersion: SampleTripsSchemaV3.self,
        willMigrate: { context in
          let trips = try? context.fetch(FetchDescriptor<SampleTripsSchemaV2.Trip>())
      
          // De-duplicate Trip instances here...
      
          try? context.save()
        }, 
        didMigrate: nil
      )
    • 9:50 - SampleTrips Versioned Schema 4.0

      @available(iOS 26, *)
      enum SampleTripsSchemaV4: VersionedSchema {
        static var versionIdentifier: Schema.Version { Schema.Version(4, 0, 0) }
      
        static var models: [any PersistentModel.Type] {
          [Trip.self, 
           BusinessTrip.self, 
           PersonalTrip.self, 
           BucketListItem.self,
           LivingAccommodation.self]
        }
      }
    • 10:03 - SampleTrips Lightweight Migration Stage from Version 3.0 to 4.0

      @available(iOS 26, *)
      static let migrateV3toV4 = MigrationStage.lightweight(
        fromVersion: SampleTripsSchemaV3.self,
        toVersion: SampleTripsSchemaV4.self
      )
    • 10:24 - SampleTrips Schema Migration Plan

      enum SampleTripsMigrationPlan: SchemaMigrationPlan {
        static var schemas: [any VersionedSchema.Type] {
          var currentSchemas: [any VersionedSchema.Type] =
            [SampleTripsSchemaV1.self, SampleTripsSchemaV2.self, SampleTripsSchemaV3.self]
          if #available(iOS 26, *) {
            currentSchemas.append(SampleTripsSchemaV4.self)
          }
          return currentSchemas
        }
      
        static var stages: [MigrationStage] {
          var currentStages = [migrateV1toV2, migrateV2toV3]
          if #available(iOS 26, *) {
            currentStages.append(migrateV3toV4)
          }
          return currentStages
        }
      }
    • 10:51 - Use Schema Migration Plan with ModelContainer

      // SampleTrip App update modelContainer Scene modifier for migrated container
      
      @main
      struct TripsApp: App {
      
        let container: ModelContainer = {
          do {
            let schema = Schema(versionedSchema: SampleTripsSchemaV4.self)
            container = try ModelContainer(
              for: schema, migrationPlan: SampleTripsMigrationPlan.self)
          } catch { ... }
          return container
        }()
        var body: some Scene {
          WindowGroup {
            ContentView()
          }
          .modelContainer(container)
        }
      }
    • 11:48 - Add search predicate to Query

      // Trip App add search text to predicate
      struct ContentView: View {
        @Query
        var trips: [Trip]
      
        init( ... ) {
          let classPredicate: Predicate<Trip>? = {
            switch segment.wrappedValue {
            case .personal:
              return #Predicate { $0 is PersonalTrip }
            case .business:
              return #Predicate { $0 is BusinessTrip }
            default:
              return nil
            }
          }
          
          let searchPredicate = #Predicate<Trip> {
            searchText.isEmpty ? true : 
              $0.name.localizedStandardContains(searchText) ||              
              $0.destination.localizedStandardContains(searchText)
          }
          
          let fullPredicate: Predicate<Trip>
          if let classPredicate {
            fullPredicate = #Predicate { classPredicate.evaluate($0) &&
                                         searchPredicate.evaluate($0)}
          } else { 
            fullPredicate = searchPredicate
          }
          _trips = Query(filter: fullPredicate, sort: \.startDate, order: .forward)
        }
        var body: some View { ... }
      }
    • 12:31 - Tailor SwiftData Fetch in Custom Migration Stage

      static let migrateV1toV2 = MigrationStage.custom(
         fromVersion: SampleTripsSchemaV1.self,
         toVersion: SampleTripsSchemaV2.self,
         willMigrate: { context in
            var fetchDesc =  FetchDescriptor<SampleTripsSchemaV1.Trip>()
            fetchDesc.propertiesToFetch = [\.name]
      
            let trips = try? context.fetch(fetchDesc)
        
            // De-duplicate Trip instances here...
      
            try? context.save()
          }, 
          didMigrate: nil
      )
    • 13:11 - Add relationshipsToPrefetch in Custom Migration Stage

      static let migrateV1toV2 = MigrationStage.custom(
         fromVersion: SampleTripsSchemaV1.self,
         toVersion: SampleTripsSchemaV2.self,
         willMigrate: { context in
            var fetchDesc =  FetchDescriptor<SampleTripsSchemaV1.Trip>()
            fetchDesc.propertiesToFetch = [\.name]
            fetchDesc.relationshipKeyPathsForPrefetching = [\.livingAccommodation]
      
            let trips = try? context.fetch(fetchDesc)
        
            // De-duplicate Trip instances here...
      
            try? context.save()
          }, 
          didMigrate: nil
      )
    • 13:28 - Update Widget to harness fetchLimit

      // Widget code to get new Timeline Entry
      
      func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
        let currentDate = Date.now
        var fetchDesc = FetchDescriptor(sortBy: [SortDescriptor(\Trip.startDate, order: .forward)])
        fetchDesc.predicate = #Predicate { $0.endDate >= currentDate }
      
        fetchDesc.fetchLimit = 1
        
        let modelContext = ModelContext(DataModel.shared.modelContainer)
        if let upcomingTrips = try? modelContext.fetch(fetchDesc) {
          if let trip = upcomingTrips.first { ... }
          
        }
      }
    • 16:24 - Fetch the last transaction efficiently

      // Fetch history with sortBy and fetchlimit to get the last token
      
      var historyDesc = HistoryDescriptor<DefaultHistoryTransaction>()
      historyDesc.sortBy = [.init(\.transactionIdentifier, order: .reverse)]
      historyDesc.fetchLimit = 1
      
      let transactions = try context.fetchHistory(historyDesc)
      if let transaction = transactions.last {
        historyToken = transaction.token
      }
    • 17:29 - Fetch History after the given token and only for the entities of concern

      // Changes AFTER the last known token
      let tokenPredicate = #Predicate<DefaultHistoryTransaction> { $0.token > historyToken }
      
      // Changes for ONLY entities of concern
      let entityNames = [LivingAccommodation.self, Trip.self]
      let changesPredicate = #Predicate<DefaultHistoryTransaction> {
                               $0.changes.contains { change in
                                 entityNames.contains(change.changedPersistentIdentifier.entityName)
                               }
                             }
      
      
      let fullPredicate = #Predicate<DefaultHistoryTransaction> {
                            tokenPredicate.evaluate($0)
                            &&
                            changesPredicate.evaluate($0)
                          }
      
      let historyDesc = HistoryDescriptor<DefaultHistoryTransaction>(predicate: fullPredicate)
      let transactions = try context.fetchHistory(historyDesc)
    • 0:00 - Introducción
    • SwiftData permite modelar y conservar datos de apps en todas las plataformas de Apple. Esta estructura simplifica la persistencia de datos, el modelado y la migración de esquemas, la administración de gráficos y la sincronización de CloudKit. La herencia de clases, una nueva funcionalidad disponible a partir de iOS 26, permite crear gráficos de modelos con la herencia.

    • 2:11 - Aprovecha la herencia de clases
    • La herencia de clases es una herramienta avanzada y particularmente útil cuando los modelos forman una jerarquía natural y comparten características comunes. La herencia permite la creación de subclases que heredan propiedades y comportamientos de una clase principal, lo que promueve la reutilización del código y mantiene una organización estructurada. En la app SampleTrips se aplica la herencia para modelar diferentes tipos de viajes, tanto personales como de negocios. Cada subclase hereda propiedades esenciales del modelo Trip y agrega atributos específicos relevantes a su subdominio. Este enfoque permite una representación de datos más personalizada y eficiente. Usa la herencia con prudencia. La herencia es apropiada cuando los modelos establecen una relación “is-a” y cuando las consultas involucran tanto a la clase principal como a sus subclases. Si los modelos solo comparten propiedades comunes sin una jerarquía natural, la conformidad con el protocolo es un enfoque más adecuado. La elección entre herencia y conformidad con el protocolo también depende de lo detalladas que sean las búsquedas en los datos.

    • 7:39 - Evolución de datos con la migración
    • El proceso de migración de datos de la app SampleTrips en diferentes versiones de iOS es un ejemplo de cómo garantizar la preservación de los datos del usuario durante las actualizaciones. El esquema de la app evolucionó a lo largo de varias versiones. En iOS 17 se introdujo SwiftData y la versión 2.0 del esquema, que hace que los nombres de los viajes sean únicos y se renombren las propiedades. En iOS 18 se agregó la versión 3.0, que utiliza índices y macros únicas, y conserva las propiedades al momento de la eliminación. Se usaron MigrationStages personalizados para la eliminación de duplicados. En iOS 26 se presenta la versión 4.0, que incluye subclases. Se necesita un MigrationStage ligero de la versión 3.0 a la 4.0. Un SchemaMigrationPlan se construye encapsulando los VersionedSchemas y MigrationStages en el orden correcto. Luego, se aplica SchemaMigrationPlan al crear ModelContainer para SampleTrips, lo que permite una migración sin problemas a través de todas las iteraciones anteriores y, al mismo tiempo, se preservan los datos del usuario.

    • 11:27 - Adaptación de los datos obtenidos
    • Para explorar la optimización de consultas y búsquedas, la app SampleTrips reintroduce la funcionalidad de la barra de búsqueda. Construye un predicado basado en la búsqueda del cliente y luego esta se combina con el predicado de clase para filtrar viajes. Estas técnicas mejoran el rendimiento de la búsqueda. Durante la migración, solo se obtienen las propiedades necesarias mediante propertiesToFetch. relationshipsToPrefetch se usa para optimizar el recorrido de relaciones. fetchLimit se configura en el código del widget para recuperar solo el viaje más reciente, lo que mejora la eficiencia.

    • 13:54 - Observación de cambios en los datos
    • La funcionalidad Observable de SwiftData te ayuda a reaccionar a los cambios locales realizados en PersistentModels. Pero no todos los cambios son observables. Los cambios de otros procesos, acciones externas o diferentes contextos de modelos dentro de la app requieren una nueva búsqueda, lo que puede resultar costoso. Para optimizar esto, puedes usar la funcionalidad de historial persistente de SwiftData. Al obtener el último token del historial y usarlo como marcador, puedes crear predicados para obtener únicamente las entradas del historial que se registraron después del último token y para entidades específicas de interés. Este enfoque permite que la app determine si es necesaria una nueva búsqueda, lo que evita la recuperación innecesaria de datos y mejora el rendimiento.

    • 18:28 - Próximos pasos
    • Al crear un gráfico de modelo, considera las implicaciones de herencia y migración. Mejora los buscadores de datos y las consultas para mejorar el rendimiento. Usa la observación y el historial persistente para llevar un registro de los cambios en los datos.

Developer Footer

  • Videos
  • WWDC25
  • SwiftData: Analiza a detalle la herencia y migración de esquemas
  • 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