大多数浏览器和
Developer App 均支持流媒体播放。
-
使用 SwiftData 构建 App
了解 SwiftData 如何帮助你在 App 中保存数据。当我们将 SwiftData 引入多平台 SwiftUI App 时,与我们一起编写代码。了解如何将现有模型类转换为 SwiftData 模型、设置环境、在 UI 中反映模型层更改以及构建由 SwiftData 存储容量支持的基于文档的 App。为了充分理解本次讲座,你应该先了解 SwiftData。相关介绍请查看 WWDC23 的“认识 SwiftData”。
章节
- 0:00 - Meet the app
- 3:09 - SwiftData models
- 5:30 - Querying models to display in UI
- 9:43 - Creating and updating
- 12:30 - Document-based apps
资源
相关视频
WWDC23
-
下载
Julia:大家好 我叫 Julia 我是一名 SwiftUI 工程师 最近 我们推出了 SwiftData 这是一种在 Swift 中 保存你的模型层的新方法 在今天的讲座中 让我们看看如何 在 SwiftUI App 中 无缝集成 SwiftData 我们将讨论 新的 SwiftUI 功能 以实现与 SwiftData 模型的平滑集成 为了掌握基础知识 如果你尚不了解 请先观看讲座 “了解 SwiftData” 为了解 SwiftData 和 SwiftUI 如何协同工作 我们来构建一个闪卡 App 一直以来 我都想制作一个工具 能帮我记住伟大发明的日期和作者 SwiftData 非常适合 来完成这个任务 它将帮助保存闪卡卡组 这样我可以在有空的时候 打开并点击查看它们 我希望这个 App 能够在 各个平台上运行:Mac、iPhone、 Watch 和 TV 而 SwiftData 能够支持这一点 它在所有平台上都可用 这是一个代码演示 在本次讲座中 我将与你一起构建一个 App 现在请暂停 然后下载 附带的 Xcode 项目 一个包含准备好的 起始点和完成点的存档 打开起始项目 然后转到 ContentView 文件 在本次讲座中 我们将利用 Xcode 新功能 在 Mac 上 嵌入交互式实时预览 在预览部分 有一个带有一些闪卡的网格 点击任何卡片可进入一个视图 我们可以逐个滚动卡片 你还记得是谁发明了编译器吗? 点击卡片 它会翻转并给出答案 App 使用存储在 内存中的示例卡片进行填充 如果我们运行 App 并添加新卡片 关闭 App 后它们将消失 这就是 SwiftData 发挥作用的地方 我们将使用它来保存创建的闪卡 今天 我们将讨论你开始使用 SwiftData 所需的全部内容 并逐一完成我整理的 我们需要的待办事项列表 你刚刚看到了 我们即将构建的 App 接下来 我们将查看起始项目及其模型类 然后我们将逐步对它进行转换和修改 以达到使用 SwiftData 作为其存储容量的目的 我们将学习如何扩展模型类 成为 SwiftData 模型 如何查询数据 并在模型层发生更改时更新视图 创建和保存模型 并方便地给它们绑定 UI 元素 最后 作为额外收获 我们将看到 当 SwiftData 负责存储容量时 创建基于文档的 App 有多么容易 在起始项目中 我定义了一个 Card 模型 这代表一个闪卡 还有一些视图和支持文件 可以节省我们的时间 每张卡片存储了正面和背面的文本 以及创建日期 它是一个相当典型的模型 我们对它进行更新 以便 SwiftData 能为我们存储它 首先 在该文件中导入 SwiftData 接下来 我们需要进行的主要更改是 在定义中添加 @Model 宏 现在 该类完全可以使用 SwiftData 进行保存了 不再需要键入 就是这样 而且 使用 @Model Card 符合 Observable 协议一致性 我们将使用它来替代 ObservableObject 移除 Observable object 以及 @Published 属性包装器的一致性 我们之前使用 ObservedObject 一致性 在 CardEditorView 文件中 直接从 UI 编辑卡片 要在此处使用 Observable 我们将“ObservedObject” 属性包装器替换为“Bindable” 它允许文本字段直接与 卡片的正面和背面文本进行绑定 完成! 新的 Observable 宏和 Bindable 属性包装器 能轻松设置 App 中的数据流 所需的代码比以前还要少 当 View 在其 body 中使用 Observable 类型的属性时 当给定属性更改时 它将自动更新 将模型的可变状态 绑定到 UI 元素从未如此简单! 我推荐你观看 WWDC23 讲座中的 “探索 SwiftUI 中的观察” 你会看到无论是否使用 SwiftData Observable 都能简化数据流代码 这就是你要了解的 关于模型的全部内容 除此以外没有更多内容了 真是棒极了 接下来 要从 SwiftData 查询模型并在 UI 中显示 让我们切换到 ContentView 我们将显示 SwiftData 存储的卡片 而不是 SampleDeck.contents
只需要进行一个更改 将卡片数组绑定到 SwiftData 存储容量 用 @Query 替换 @State 属性包装器 就是这样 正如我们在预览中看到的 没有更多的卡片需要显示 可能因为我们还没有保存任何卡片 使用 @Query 可以在 UI 中 显示由 SwiftData 管理的模型 @Query 是一个新的属性包装器 用于从 SwiftData 中查询模型 它还会在模型更改时触发视图更新 类似于 @State 的工作方式 每个视图可以根据需要 具备多个查询属性 Query 提供了轻量级的语法 用于配置分类、排序、 过滤甚至动画更改 在幕后 它使用 视图模型的上下文作为数据源 如何向 @Query 提供模型上下文? 我们将从模型容器中获取一个 SwiftUI 提供了一个 新的视图和场景修饰符 便于设置视图的模型容器 要使用 SwiftData 任何 App 都必须设置至少一个模型容器 它创建了整个存储堆栈 包括 @Query 将使用的上下文 一个视图只有一个模型容器 但 App 可以为不同的视图层次结构 创建和使用尽可能多的容器 如果 App 没有 设置自己的模型容器 它的窗口和创建的视图无法通过 SwiftData 保存或查询模型 许多 App 只需要一个模型容器 在这种情况下 你可以为整个窗口组场景设置 窗口及其视图将接替容器和 从同一组中创建的任何其他窗口 所有这些视图 将从单个容器中写入和读取 有些 App 需要多个存储堆栈 可以为不同窗口设置多个模型容器 SwiftUI 还允许 在视图级别进行精细的设置 同一窗口中的 不同视图可以拥有单独的容器 一个容器中的 保存操作不会影响其他容器 现在 让我们设置模型容器 来为 Query 提供数据源 我打开 App 定义
为 App 的窗口设置一个模型容器 请注意 子视图只能创建、读取、 更新和删除视图修饰符中 列出的模型类型 设置完成 我还想再进行一步操作 那就是为我的预览提供示例数据 在 App 中 我定义了 一个包含示例卡片的内存容器 让我们打开“PreviewSampleData” 文件并将其包含在目标中 该文件包含带有 示例数据的容器的定义 我将在 ContentView 中 使用它来填充我的预览视图
现在 @Query 有了数据源 预览显示了卡片 这就是使 SwiftData 堆栈 准备就绪并生成预览所需全部设置 接下来 我想确保 SwiftData 跟踪和保存了 我创建的新卡片 以及我对现有卡片所做的更改 为此我将使用视图的模型上下文 为了访问模型上下文 SwiftUI 提供了一个新的环境变量 这类似于模型容器 每个视图只有一个上下文 但是 App 可以根据需要 拥有尽可能多的上下文 在我们的 App 中 上下文已经配置好了 这个环境变量是在我们之前 设置模型容器时自动填充的 让我们切回到 Xcode 我们需要访问模型上下文 来保存和更新卡片
我们将 新创建的卡片插入模型上下文中 使 SwiftData 明白 我们要存储的模型
你可能认为在插入模型后 需要调用“modelContext.save()” 保存上下文 但你不需要这样做 SwiftData 有一个很好的特性 即自动保存模型上下文 自动保存是通过与 UI 相关的 事件和用户输入触发的 我们不需要担心保存 因为 SwiftData 会为我们自动保存 只有在一些特定情况下 当你希望确保所有更改立即保存时 例如在共享 SwiftData 存储容量 或发送存储容量之前 需要显式调用“save()” 现在我们的 App 可以保存和查询卡片了 让我们创建一个吧! 我运行 App 点击加号按钮创建一个卡片 让我们添加之前看到的编译器卡片 现在我们退出 App 重新启动 看看我们的新卡片是否在那里
就是这样 现在你知道如何访问 视图的模型上下文并添加卡片了 完成! 我们来打开一个新窗口 它显示与第一个窗口 相同的卡片堆栈 这是有道理的 因为两个窗口使用 相同的模型容器并访问相同的数据 如果 App 可以在不同窗口中 打开不同的闪卡堆栈 那就更好了 基本上 这意味着我希望 将每个闪卡堆栈视为单独的文档 然后我就可以与朋友分享这些文档 基于文档的 App 是在 macOS、iOS 和 iPadOS 上使用的概念 它描述了一些特定类型的 App 允许用户创建、打开、查看 或编辑不同类型的文档 每个文档都是一个文件 用户可以存储、复制和共享它们 我很高兴地告诉你 SwiftUI 支持 基于 SwiftData 文档的 App 我们来尝试这种方法 我打开 FlashCardApp 文件 基于文档的 App 存在于 iOS 和 macOS 上 在这些平台上 我们将切换到 使用 DocumentGroup 初始化定式
我将传递模型类型 Card.self、 内容类型和视图构建器 让我们稍微偏离一下 更详细地讨论 第二个参数 内容类型 SwiftData 中基于文档的 App 需要声明自定义的内容类型 每个 SwiftData 文档 都是由一组唯一的模型构建的 因此在磁盘上有唯一的表示 在文档的上下文中 你可以将内容类型视为 二进制文件格式 例如 JPEG 另一种类型的文档 即软件包 这是具有固定结构的目录 例如 Xcode 项目 例如 所有的 JPEG 图像 都有相同的二进制结构 否则 照片编辑器将无法读取它们 类似地 所有的 Xcode 项目 都包含一定的目录和文件 当用户打开闪卡堆栈时 我们需要操作系统 将闪卡堆栈格式和文件扩展名 与我们的 App 关联起来 这就是为什么 我们需要声明内容类型 SwiftData 文档是软件包 如果你将 SwiftData 模型的某些属性 标记为“externalStorage”属性 所有外部存储的项 将成为文档软件包的一部分 在 UTType+FlashCards 文件中 我有一个新内容类型的定义 这样我们就可以 在代码中方便地使用它 我们在 Info.plist 中放置相同的定义
我们将在操作系统中 声明一个新的内容类型 我们需要指定文件扩展名来帮助区分 我们的 App 创建的 闪卡堆栈和其他任何文档 对于这个示例 App 我们将使用“sampledeck”作为扩展名
我还将添加一个简短的描述 如“Flash Cards Deck”
标识符应与代码中标识符完全相同
因为 SwiftData 文档是软件包 我们必须确保我们的类型符合 com.apple.package 现在 让我们使用 我们声明的内容类型 我返回到 App 定义 并将内容类型传递给 DocumentGroup 视图构建器看起来是一样的
值得注意的是我们不设置模型容器 文档基础设施 将为每个文档设置一个模型容器 我们来运行 App 看看现在的效果 App 启动时会显示打开面板 这是基于文档的 App 的标准行为 我将创建一个新文档 并在其中添加一个卡片 现在 该文档具有工具栏副标题 指示它有未保存的更改 我按下 Command+S 会出现保存对话框 请注意 闪卡堆栈将以我们之前 放在 Info.plist 中的 相同文件扩展名保存 我保存了新的闪卡堆栈 现在它出现在桌面上 我的第一个闪卡堆栈 我也可以按 Command+N 创建一个新的闪卡堆栈 或按 Command+O 打开一个闪卡堆栈 这些快捷指令以及许多其他功能 都是自动提供给 基于文档的 App 简而言之 今天我们学习了如何 在 SwiftUI App 中使用 SwiftData 存储容量 我们讨论了新的 @Model 宏、 @Query 属性包装器和 模型上下文的新环境变量 并看到了如何轻松将 SwiftData 用作你文档的存储容量 感谢你今天的参与 祝你在 构建 App 的过程中感到愉快! ♪ ♪
-
-
3:33 - Defining a SwiftData model
@Model final class Card { var front: String var back: String var creationDate: Date init(front: String, back: String, creationDate: Date = .now) { self.front = front self.back = back self.creationDate = creationDate } }
-
4:25 - Binding to a SwiftData model
@Bindable var card: Card
-
5:52 - Query models from SwiftData storage
@Query private var cards: [Card]
-
8:27 - Setting up a model container for the window group
WindowGroup { ContentView() } .modelContainer(for: Card.self)
-
9:24 - Providing a preview with sample data
#Preview { ContentView() .frame(minWidth: 500, minHeight: 500) .modelContainer(previewContainer) }
-
10:30 - Accessing the model context of the ContentView
@Environment(\.modelContext) private var modelContext
-
10:51 - Insert a new model in the context
let newCard = Card(front: "Sample Front", back: "Sample Back") modelContext.insert(object: newCard)
-
13:34 - Start document-based application setup
@main struct SwiftDataFlashCardSample: App { var body: some Scene { #if os(iOS) || os(macOS) DocumentGroup(editing: Card.self, contentType: <#UTType#>) { <#code#> } #else WindowGroup { ContentView() .modelContainer(for: Card.self) } #endif } }
-
16:51 - Finish document-based application setup
@main struct SwiftDataFlashCardSample: App { var body: some Scene { #if os(iOS) || os(macOS) DocumentGroup(editing: Card.self, contentType: .flashCards) { ContentView() } #else WindowGroup { ContentView() .modelContainer(for: Card.self) } #endif } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。