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

Open Menu Close Menu
  • Collections
  • All Videos
  • About

Back to WWDC26

  • About
  • Summary
  • Transcript
  • Code
  • What’s new in SwiftData

    Discover the latest enhancements to SwiftData. We'll show you how to persist custom and third-party types using Codable, and group fetched data into sections in your SwiftUI app. We'll also explore how to observe data store changes anywhere else using ResultsObserver and HistoryObserver, giving you the flexibility to drive powerful state objects and react precisely to model updates.

    Chapters

    • 0:00 - Introduction
    • 0:53 - Sectioning your fetches
    • 2:56 - Using custom types
    • 6:26 - Observing data stores with ResultsObserver
    • 9:41 - Observing history with HistoryObserver
    • 12:20 - Next steps

    Resources

    • SwiftData
    • Adopting SwiftData for a Core Data app
      • HD Video
      • SD Video

    Related Videos

    WWDC26

    • Code-along: Add persistence with SwiftData

    WWDC24

    • Track model changes with SwiftData history
  • Search this video…

    Hi I'm Thomas, an Engineer on the SwiftData team. In this session, I'm going to show you some of the new SwiftData features in Apple's 2027 releases.

    If you're just starting on your SwiftData journey, make sure to watch the code-along "Add persistence with SwiftData" and learn how to adopt SwiftData in a new app.

    Apple's 2027 releases bring exciting new features to SwiftData. First, I'm going to show how to use Query to fetch your data in sections in SwiftUI views. Next, I'm going to cover enhancements on how you can store custom types in your model.

    And finally, I'll cover some great new APIs for observing model and history changes in your SwiftData store. Let's have a look at sectioning.

    Over the last years, we've been working on an App "SampleTrips" to let me track all the adventures I have planned.

    SampleTrips uses SwiftData for Persistence and SwiftUI for the User Interface. SwiftUI and SwiftData work great together.

    Reading from SwiftData in SwiftUI is as easy as adding a Query to the view. Here Query fetches all trips from SwiftData, sorted by the start date.

    The results of this query can then be used in the view body to create a list of trips.

    In SampleTrips I want to add the option to group trips by destination. So when I have several trips planned to the same place, I can see them next to each other.

    In Apple's 2027 releases, Query adds support for sectioning.

    To create a sectioned query, I can pass a KeyPath that starts from the root of the Trip model and leads to a string to the new sectionBy parameter of Query. Here I'm using a KeyPath to the Trips destination property to create a section for each destination.

    The wrapped value of Query is still an array of Trip. So right now the list looks like before. To section the list, I'll wrap the current ForEach in a section and add a second ForEach.

    To get to the sections, I access the query from the property wrapper, using the underscore-prefixed name "_trips". The query has a sections property that returns a list of sections. Then I can use SwiftUI's ForEach to iterate through all sections.

    Each section has an ID property. The ID is the value of my model from the KeyPath I passed to the SectionBy parameter of Query.

    So here in my TripListView the ID will be the destination of all trips in the section.

    I use ID as the Section's header label.

    The section itself is a collection of trips - so I change the inner ForEach to iterate though them and create a TripListItem for each trip in the section.

    Now, that I can see all my trips grouped by their destination, let's move to the next section.

    And have a closer look at some enhancements on storing custom types in your model.

    At the center of the SampleTrips app is the Trip model. It stores information like name, destination, and the start and end dates. Right now the destination is stored as a String in the Trip Model. I want add the ability to find locations from MapKit. I've already built a picker in the App that lets me search for destinations using MapKits search API.

    For each location, the MapKit API provides a lot of metadata, such as the name, geographic coordinates and a MKMapItem.Identifier. This MKMapItem.Identifier uniquely identifies the location in MapKit and can be used to look up more metadata about the location and open the location in the Maps app.

    I want to store the coordinates and the MKMapItem.Identifier in the trip model. So I'm adding them to the Trip class.

    But now, when I launch the app, SwiftData raises a Fatal Error - "Class property within Persisted Struct/Enum is not supported" - pointing at MKMapItem.Identifier as the cause. When SwiftData loads, it automatically generates a schema for the models. The schema defines a mapping between the Model classes and the entities and properties a DataStore can persist. For most types this works automatically. But classes that are not annotated with the Model macro can't be automatically inspected and SwiftData fails to generate a schema.

    Since MKMapItem.Identifier comes from MapKit, I don't have access to its implementation. I can't modify it and mark it as @Model, and SwiftData can't inspect it because it's a class. But it does conform to Codable. Codable types know how to serialize themselves into data and instantiate themselves back from it.

    In Apple's 2027 releases, you can mark a model attribute as .codable to tell SwiftData to delegate serialization to the type and store the serialized representation.

    Let's mark the mapItemIdentifier attribute as codable. Now, SampleTrips launches without any error. Great.

    The codable attribute tells SwiftData to persist the encoded representation instead of inferring a schema for the type. Like transformable attributes, codable comes with a few things to keep in mind. The contents of codable attributes are opaque to SwiftData. This means they can't be used in Predicates to filter results or for sorting - using Sort Descriptors. Also, if the shape of the codable type changes, like adding or removing properties, they will not trigger a migration. The Codable implementation of the type must be able to encode and decode in a forward- and backward-compatible way.

    Using Codable attributes can be thought of as an "escape hatch" to persist types that SwiftData does not support natively.

    You should avoid using codable for Types that you define. Modeling them as SwiftData models or supported values types, allows you to harness the full power of SwiftData, like Sorting, Filtering, and Indexing. But for types that you don't own, codable lets you store them alongside your other attributes. Now I can pick locations from MapKit in SampleTrips and persist the destination's MKMapItem.Identifier alongside the other properties in the trip model. Now, let's look at how you can monitor your store and be notified when data changes.

    Earlier we used the query macro in the TripListView to make trips from the SwiftData store available to SwiftUI.

    @Query is a powerful tool. When the view appears, it fetches models from the SwiftData store so they can be used in the view body.

    Then, it continuously monitors the store for changes that would affect its results. For example, when a trip is removed from the store, Query will notice the change and the view re-renders to reflect the new results of the query.

    Query is great and should be your first choice in a SwiftUI view. But what about parts of your app that are not SwiftUI? Maybe you have a state object that derives values from your SwiftData store and needs to recompute when the data changes. Or your app doesn't use SwiftUI at all like a game written in SceneKit? In Apple's 2027 releases, we're introducing ResultsObserver.

    Like Query in SwiftUI views, ResultsObserver fetches data from your SwiftData store and then observes your store for changes. But it works anywhere in your app - independent of SwiftUI views - using Swift Observation.

    It supports the same query primitives that you already know - filtering, sorting, and the sectioning we covered earlier.

    Here's the data flow diagram I shared earlier, but with the Query replaced with ResultsObserver and the View replaced with any code you like. The ResultsObserver fetches data and observes your store for changes. Your code can use Swift Observation to react to these changes.

    Earlier I've added location information in the SampleTrips app. I want to add a map to see all the places I have trips planned to. I am using SwiftUIs MapKit integration. But I want to customize what area of the map is shown. To do this, I've added a new MapCameraController, which calculates a fitting MapCameraBounds for the map. It uses ResultsObserver to know when to recalculate the MapCameraBounds.

    To do this, I create a ResultsObserver for trips. For the map, I always want to show all trips, so I'm not going to pass in a predicate to filter or a sectioning key path.

    Then, I'm using withContinuousObservation with the didSet option to get a callback every time a trip changes.

    And when the results are changing, I'm re-calculating the bounds of the map.

    withContinuousObservation returns an ObservationTracking token. This token defines the lifetime of the observation. I'll store this token on my class to receive updates for the entire lifetime of my MapCameraController.

    Here it is in action. When I launch SampleTrips, MapCameraController picked the map camera bounds that fit all my trips into the view.

    Unfortunately, I won't be able to visit Toronto later this month, so I'm going to delete this trip.

    The ResultsObserver will notice the change and the MapCameraController will recalculate the camera bounds of the map.

    ResultsObserver is a powerful tool if your code needs to react to changes in your data store - anywhere in your app.

    But we didn't stop there. In Apple's 2027 releases, we're also supporting observing history.

    First, a quick history lesson. When data in your store changes, SwiftData keeps a record of everything that changed. You can use this history to build features like syncing with external servers, or reacting to changes made outside of your app, like in an app extension.

    Every time your data store is saved, SwiftData records a history transaction. The history transaction contains information about what changed, and where the change was coming from - and a token that uniquely identifies the transaction in the history. This token can be used with the ModelContext.fetchHistory API to fetch newer transactions. To learn more about persistent history, watch "Track Model Changes with SwiftData History" from WWDC 2024.

    New in Apple's 2027 releases is HistoryObserver. HistoryObserver makes it easy to react to any changes in your store. Similar to ResultsObserver observing fetch results, HistoryObserver can observe the persistent history and let your code react when new transactions are added.

    And if you only need to know about certain kinds of changes, HistoryObserver lets you filter by model type and transaction author. Observing history is useful when your app needs to keep parts of the data store in sync with other systems, like an external server.

    HistoryObserver has a single observable property - eventCounter. When new transactions are available in the persistent history, the eventCounter increments.

    Your code can observe the eventCounter and when it increments, use ModelContext.fetchHistory API to fetch the latest changes.

    Here's an example of how you could use HistoryObserver to synchronize changes, made in the app with an external server.

    First, I set up a HistoryObserver for my modelContainer.

    In this case, I'm only interested in changes made by the app so I'm passing "App" as authors. So that I'm not replaying changes that came from the server back to the server.

    Next, I'm using withContinuousObservation. Like the MapCameraController, I store the observation tracking token on the class so that tracking remains active for the lifetime of my class. Within the observation closure I need to access eventCounter so that Swift Observation knows what to track.

    And finally, I call my processChanges function. In processChanges I can use the ModelContext.fetchHistory API to fetch history and upload changes to my server.

    I hope you are as excited as I am about the new features in SwiftData in our 2027 releases.

    Tailor SwiftData fetches with sections. Use codable attributes when storing types from other frameworks. Use ResultsObserver to react to changes of query results outside of SwiftUI views. And finally, use HistoryObserver to react to changes in SwiftData's persistent history.

    Thanks for watching.

    • 0:01 - Sectioned fetching

      // Sectioned fetching
      
      struct TripListView: View {   
          @Query(sort: \Trip.startDate,
                 sectionBy: \.destination)
          var trips: [Trip]
      
          var body: some View: {
              List(selection: $selection) {
                  ForEach(_trips.sections) {section in
                      Section(section.id) {
                          ForEach(trips) {trip in
                              TripListItem(trip: trip)
                          }
                     }
                  }
              }
          }
      }
    • 4:59 - Using Codable

      // Using Codable
      
      import SwiftData
      
      @Model class Trip {
      
          struct Location: Codable {
              var latitude: Double
              var longitude: Double
          }
      
          var name: String
          var destination: String
      
          var startDate: Date
          var endDate: Date
      
          var location: Location?
          @Attribute(.codable) var mapItemIdentifier: MKMapItem.Identifier?
      }
    • 8:20 - // Use observation to update map bounds

      // Use observation to update map bounds
      
      @Observable @MainActor final class MapCameraController {
          private let resultsObserver: ResultsObserver<Trip, Never>
          var bounds: MapCameraBounds?
          private var token: ObservationTracking.Token?
      
          init(modelContext: ModelContext) throws {
              resultsObserver = try ResultsObserver<Trip, Never>(modelContext: modelContext)
      
              token = withContinuousObservation(options: [.didSet]) {[weak self], event in
                  self?.bounds = self?.calculateBounds(trips: resultsObserver.results)
             }
          }
      
          private func calculateBounds(trips: [Trip]) -> MapCameraBounds? { / * */ }
      }
    • 8:21 - // Using HistoryObserver to sync with a server

      // Using HistoryObserver to sync with a server
      
      @SyncActor final class ServerSync {
          private let observer: HistoryObserver
          private var token: ObservationTracking.Token?
      
          func start() throws {
              self.observer = try HistoryObserver(authors: ["App"], modelContainer: modelContainer)
              token = withContinuousObservation(options: .didSet) {[weak self] _ in
                  _ = self?.observer.eventCounter
                  self?.processChanges()
              }
          }
      
          private func processChanges() {
              // Fetch and process history transactions
          }
      }
    • 0:00 - Introduction
    • What's new in SwiftData for Apple's 2027 releases, with a preview of the agenda: sectioning fetches, using custom types, and observing data stores.

    • 0:53 - Sectioning your fetches
    • Use @Query's new sectionBy: parameter to group results by a key path. Access the underlying query via the underscore-prefixed name to iterate sections, each with an id and a collection of models — demonstrated by grouping trips by destination in SampleTrips.

    • 2:56 - Using custom types
    • Store types SwiftData can't model natively — like MKMapItem.Identifier — by marking properties with @Attribute(.codable). Treat codable as an escape hatch for external types, not types you own; modeling your own types as @Model or supported value types unlocks filtering, sorting, and migration.

    • 6:26 - Observing data stores with ResultsObserver
    • The new ResultsObserver brings Query-style fetching to non-SwiftUI code. Combine it with withContinuousObservation(didSet:) to react to model changes anywhere in your app — shown updating map camera bounds as trips change.

    • 9:41 - Observing history with HistoryObserver
    • HistoryObserver exposes a single observable eventCounter that ticks when new persistent-history transactions arrive. Pair it with ModelContext.fetchHistory() to filter by model type or transaction author — ideal for syncing changes to an external server.

    • 12:20 - Next steps
    • Recap: section your fetches, adopt codable types, react to data changes with ResultsObserver, and observe history with HistoryObserver.

Developer Footer

  • Videos
  • WWDC26
  • What’s new in SwiftData
  • 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