View in English

  • Apple 开发者
    • 入门汇总

    探索“入门汇总”

    • 概览
    • 学习
    • Apple Developer Program

    及时了解最新动态

    • 最新动态
    • 开发者你好
    • 平台

    探索“平台”

    • Apple 平台
    • iOS
    • iPadOS
    • macOS
    • Apple tvOS
    • visionOS
    • watchOS
    • App Store

    精选

    • 设计
    • 分发
    • 游戏
    • 配件
    • 网页
    • Home
    • CarPlay 车载
    • 技术

    探索“技术”

    • 概览
    • Xcode
    • Swift
    • SwiftUI

    精选

    • 辅助功能
    • App Intents
    • Apple 智能
    • 游戏
    • 机器学习与 AI
    • 安全性
    • Xcode Cloud
    • 社区

    探索“社区”

    • 概览
    • “与 Apple 会面交流”活动
    • 社区主导的活动
    • 开发者论坛
    • 开源

    精选

    • WWDC
    • Swift Student Challenge
    • 开发者故事
    • App Store 大奖
    • Apple 设计大奖
    • Apple Developer Centers
    • 文档

    探索“文档”

    • 文档库
    • 技术概述
    • 示例代码
    • 《人机界面指南》
    • 视频

    发布说明

    • 精选更新
    • iOS
    • iPadOS
    • macOS
    • watchOS
    • visionOS
    • Apple tvOS
    • Xcode
    • 下载

    探索“下载”

    • 所有下载
    • 操作系统
    • 应用程序
    • 设计资源

    精选

    • Xcode
    • TestFlight
    • 字体
    • SF Symbols
    • Icon Composer
    • 支持

    探索“支持”

    • 概览
    • 帮助指南
    • 开发者论坛
    • “反馈助理”
    • 联系我们

    精选

    • 《开发者账户帮助》
    • 《App 审核指南》
    • 《App Store Connect 帮助》
    • 即将实行的要求
    • 协议和准则
    • 系统状态
  • 快速链接

    • 活动
    • 新闻
    • 论坛
    • 示例代码
    • 视频
 

视频

打开菜单 关闭菜单
  • 专题
  • 所有视频
  • 关于

更多视频

  • 简介
  • 概要
  • 转写文稿
  • 代码
  • SwiftData 的新功能

    了解 SwiftData 的最新增强功能。我们将介绍如何使用 Codable 持久保留自定类型和第三方类型的数据,并将获取的数据分组到 SwiftUI App 的各个分区。我们还将探索如何使用 ModelResultsObserver 和 HistoryObserver 在其他各处观察数据存储的变化,以便灵活地驱动强大的状态对象、与基于委托的架构实现整合,并精准地响应模型更新。

    章节

    • 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

    资源

    • SwiftData
    • Adopting SwiftData for a Core Data app
      • 高清视频
      • 标清视频

    相关视频

    WWDC26

    • 跟随编程:使用 SwiftData 添加持久化功能

    WWDC24

    • 使用 SwiftData 历史记录 API 跟踪模型更改
  • 搜索此视频…

    大家好,我是Thomas, SwiftData团队的工程师。 在本次会话中,我将向大家展示 一些新的SwiftData功能, 这些功能将在Apple 2027年版本中推出。 如果你刚刚开始SwiftData之旅, 务必观看代码演练 《使用SwiftData添加持久化》, 了解如何在新应用中采用SwiftData。 Apple 2027年版本为SwiftData 带来了令人兴奋的新功能。 首先,我将展示如何使用Query 在SwiftUI视图中 按分区获取数据。 接下来,我将介绍 关于如何在模型中存储 自定义类型的增强功能。 最后,我将介绍 一些出色的新API, 用于观察SwiftData存储中 模型和历史记录的变化。 让我们来看看分区功能。 过去几年,我们一直在开发 "SampleTrips"应用,让我能够追踪

    所有计划中的旅行冒险。 SampleTrips使用SwiftData实现持久化, 使用SwiftUI构建用户界面。 SwiftUI和SwiftData 配合使用效果出色。 在SwiftUI中读取SwiftData数据, 就像在视图中添加一个Query一样简单。 这里的Query从SwiftData获取所有行程, 并按开始日期排序。 此查询的结果随后可以在 视图主体中用于创建行程列表。 在SampleTrips中,我想添加 按目的地对行程分组的功能。 这样,当我有多个计划前往 同一地点的行程时, 可以将它们并排显示。 在Apple 2027年版本中, Query新增了对分区的支持。 要创建分区查询, 可以传入一个KeyPath, 从Trip模型的根节点出发, 指向一个字符串, 作为Query新参数sectionBy的值。 这里我使用指向Trip的 destination属性的KeyPath来创建分区, 为每个目的地创建一个分区。 Query的包装值 仍然是一个Trip数组。 所以现在列表看起来和之前一样。 要对列表进行分区,我将 当前的ForEach包裹在一个Section中, 并添加第二个ForEach。 要访问各个分区,我通过属性包装器 访问Query, 使用带下划线前缀的名称"_trips"。 Query有一个sections属性, 可以返回分区列表。 然后,我可以使用SwiftUI的ForEach 遍历所有分区。 每个分区都有一个ID属性。 ID是通过传给Query的 sectionBy参数的KeyPath 所对应的模型值。 因此,在我的TripListView中,ID 将是该分区中所有行程 的目的地。

    我将ID用作分区的标题标签。 分区本身是一个行程集合—— 所以我修改内层ForEach, 让它遍历这些行程,并为 分区中的每个行程创建TripListItem。 现在,我可以看到所有行程 按目的地分组显示了, 让我们继续下一部分。 来深入了解 在模型中存储 自定义类型的一些增强功能。 SampleTrips应用的核心 是Trip模型。 它存储了名称、目的地 以及开始和结束日期等信息。 目前,目的地在Trip模型中 以String类型存储。 我想添加从MapKit 查找位置的功能。 我已经在应用中构建了一个选择器, 可以使用MapKit的搜索API 搜索目的地。 MapKit API为每个位置提供 大量元数据,例如名称、 地理坐标 以及MKMapItem.Identifier。 MKMapItem.Identifier可以 在MapKit中唯一标识该位置, 可用于查找该位置的 更多元数据, 以及在地图应用中打开该位置。 我想存储坐标 和MKMapItem.Identifier 到行程模型中。 所以我将它们添加到Trip类中。 但是,当我启动应用时, SwiftData报出了一个Fatal Error—— "持久化Struct/Enum中的 类属性不受支持"—— 并指出MKMapItem.Identifier 是问题所在。

    SwiftData加载时,会自动 为模型生成schema。 schema定义了模型类与 DataStore可以持久化的 实体和属性之间的映射关系。 对于大多数类型,这一过程是自动完成的。 但没有使用Model宏标注的类 无法被自动检查, SwiftData因此无法生成schema。 由于MKMapItem.Identifier 来自MapKit, 我无法访问其实现。 我无法修改它并标注@Model, SwiftData也无法检查它, 因为它是一个类。 但它确实遵循了Codable协议。 Codable类型知道如何将自身 序列化为数据, 也知道如何从数据中还原自身。 在Apple 2027年版本中,你可以 将模型属性标记为.codable, 以告知SwiftData将 序列化工作委托给该类型, 并存储序列化后的表示。 让我们将mapItemIdentifier属性 标记为codable。 现在,SampleTrips启动时 不再报任何错误。 太好了。 codable属性告知SwiftData 持久化编码后的表示, 而不是为该类型 推断schema。 与transformable属性类似, 使用codable有几点需要注意。 codable属性的内容 对SwiftData是不透明的。 这意味着它们无法在 Predicates中用于过滤结果, 也无法用于排序——即使用Sort Descriptors。 此外,如果codable类型的 结构发生变化, 例如添加或删除属性, 不会触发迁移。 该类型的Codable实现必须能够 以向前和向后兼容的方式 进行编码和解码。

    使用Codable属性可以视为 持久化SwiftData原生不支持的类型的 "后备方案"。 对于你自己定义的类型, 应避免使用codable。 将它们建模为SwiftData模型 或支持的值类型,使你能够 充分发挥SwiftData的强大功能, 例如排序、过滤和索引。 但对于你不拥有的类型, codable让你能够将它们 与其他属性一起存储。 现在,我可以在SampleTrips中 从MapKit选择位置,并持久化 目的地的MKMapItem.Identifier, 与行程模型中的 其他属性一起存储。 现在,让我们来看看 如何监控你的存储, 并在数据发生变化时收到通知。 之前我们在TripListView中 使用了Query宏, 将SwiftData存储中的行程 提供给SwiftUI使用。 @Query是一个强大的工具。 当视图显示时,它从SwiftData存储中 获取模型, 从SwiftData存储中获取, 以便在视图主体中使用。 然后,它持续监控存储中 会影响查询结果的变化。 例如,当一个行程 从存储中删除时, Query会注意到该变化, 视图会重新渲染以反映 查询的最新结果。 Query非常强大,应该是 你在SwiftUI视图中的首选。 但如果应用中有不使用SwiftUI的部分怎么办? 也许你有一个状态对象, 需要从SwiftData存储中派生值, 并在数据变化时重新计算。 或者你的应用完全不使用SwiftUI, 比如用SceneKit编写的游戏?

    在Apple 2027年版本中, 我们推出了ResultsObserver。 与SwiftUI视图中的Query类似, ResultsObserver从你的 SwiftData存储中获取数据, 并监控存储中的变化。 但它可以在应用的任何地方运行—— 独立于SwiftUI视图—— 使用Swift Observation。 它支持你已熟悉的 相同查询原语—— 过滤、排序,以及我们 之前介绍的分区功能。 这是我之前分享的 数据流示意图, 但将Query 替换为ResultsObserver, 将视图替换为 任意你喜欢的代码。 ResultsObserver获取数据 并监控存储中的变化。 你的代码可以使用Swift Observation 对这些变化作出响应。 之前我在SampleTrips应用中 添加了位置信息。 我想添加一个地图, 查看所有计划行程的目的地。 我正在使用SwiftUI的MapKit集成。 但我想自定义 地图显示的区域。 为此,我添加了一个新的 MapCameraController, 用于计算适合地图的 MapCameraBounds。 它使用ResultsObserver来判断 何时重新计算MapCameraBounds。 为此,我为行程 创建一个ResultsObserver。 对于地图, 我始终希望显示所有行程, 所以我不会传入用于过滤的 predicate或分区键路径。 然后,我使用 withContinuousObservation 和didSet选项, 在每次行程变化时 获得回调。 当结果发生变化时, 我会重新计算地图的边界。 withContinuousObservation 返回一个ObservationTracking token。 这个token定义了 观察的生命周期。 我将这个token存储在我的类上, 以便接收更新, 覆盖MapCameraController 整个生命周期。 来看看它的实际效果。 当我启动SampleTrips时, MapCameraController选择了 能将所有行程纳入视图的 地图相机边界。 遗憾的是,我本月晚些时候 无法前往多伦多了, 所以我要删除这次行程。

    ResultsObserver会注意到这一变化, MapCameraController 会重新计算地图的 相机边界。 ResultsObserver是一个强大的工具, 适用于需要对数据存储变化作出响应的代码—— 在应用的任何位置。 但我们没有止步于此。 在Apple 2027年版本中, 我们还支持观察历史记录。 首先,快速了解一下历史记录。 当存储中的数据发生变化时,SwiftData 会记录所有发生的变更。 你可以使用这些历史记录 来构建功能, 例如与外部服务器同步, 或响应在应用外部 发生的变化, 比如在应用扩展中。 每次保存数据存储时, SwiftData会记录一条历史事务。 历史事务包含了变更内容的信息, 以及变更的来源—— 以及一个在历史记录中 唯一标识该事务的token。 这个token可以与 ModelContext.fetchHistory API 一起使用,以获取更新的事务。 要了解更多关于 持久化历史记录的内容,请观看 WWDC 2024的 《使用SwiftData History追踪模型变更》。 Apple 2027年版本的新功能 是HistoryObserver。 HistoryObserver让响应存储中 的任何变化变得简单。 与ResultsObserver观察查询结果类似, HistoryObserver可以观察 持久化历史记录,并在 新事务加入时通知你的代码。

    如果你只需要了解 特定类型的变更, HistoryObserver支持按 模型类型和事务作者进行过滤。 观察历史记录非常有用, 当你的应用需要保持 数据存储的各部分 与其他系统同步时, 例如外部服务器。 HistoryObserver只有一个 可观察属性——eventCounter。 当持久化历史记录中 有新事务可用时, eventCounter会递增。 你的代码可以观察eventCounter, 当它递增时, 使用ModelContext.fetchHistory API 获取最新变更。

    这里是一个示例,展示如何使用 HistoryObserver同步变更, 将应用中的变更 同步到外部服务器。 首先,我为我的modelContainer 设置一个HistoryObserver。 在这个案例中,我只关心 应用本身产生的变更, 所以我传入"App"作为authors参数。 这样可以避免将 来自服务器的变更再次推送回服务器。 接下来,我使用withContinuousObservation。 与MapCameraController类似, 我将observation tracking token 存储在类上, 使得追踪在我的类的整个 生命周期内保持活跃。 在observation闭包内, 我需要访问eventCounter, 以便Swift Observation 知道要追踪什么。 最后,我调用 processChanges函数。 在processChanges中,我可以使用 ModelContext.fetchHistory API 获取历史记录,并将 变更上传到服务器。 希望你和我一样期待, 期待SwiftData在 2027年版本中的这些新功能。 使用分区来定制SwiftData的查询。 在存储其他框架的类型时, 使用codable属性。 使用ResultsObserver在SwiftUI视图 之外响应查询结果的变化。 最后, 使用HistoryObserver响应 SwiftData持久化历史记录中的变化。 感谢观看。

    • 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

  • 视频
  • WWDC26
  • SwiftData 的新功能
  • 打开菜单 关闭菜单
    • iOS
    • iPadOS
    • macOS
    • Apple tvOS
    • visionOS
    • watchOS
    打开菜单 关闭菜单
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    打开菜单 关闭菜单
    • 辅助功能
    • 配件
    • Apple 智能
    • App 扩展
    • App Store
    • 音频与视频 (英文)
    • 增强现实
    • 设计
    • 分发
    • 教育
    • 字体 (英文)
    • 游戏
    • 健康与健身
    • App 内购买项目
    • 本地化
    • 地图与位置
    • 机器学习与 AI
    • 开源资源 (英文)
    • 安全性
    • Safari 浏览器与网页 (英文)
    打开菜单 关闭菜单
    • 完整文档 (英文)
    • 部分主题文档 (简体中文)
    • 教程
    • 下载
    • 论坛 (英文)
    • 视频
    打开菜单 关闭菜单
    • 支持文档
    • 联系我们
    • 错误报告
    • 系统状态 (英文)
    打开菜单 关闭菜单
    • Apple 开发者
    • App Store Connect
    • 证书、标识符和描述文件 (英文)
    • 反馈助理
    打开菜单 关闭菜单
    • 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 Research Device Program (英文)
    打开菜单 关闭菜单
    • 与 Apple 会面交流
    • Apple Developer Center
    • App Store 大奖 (英文)
    • Apple 设计大奖
    • Apple Developer Academies (英文)
    • WWDC
    阅读最近新闻。
    获取 Apple Developer App。
    版权所有 © 2026 Apple Inc. 保留所有权利。
    使用条款 隐私政策 协议和准则