[WWDC2023] SwiftData基本用例以6个步骤
#ios #swift #xcode #swiftdata

SwiftData

它已经发生了!期待已久的CoreData的继任者已发行了SwiftData!尽管SwiftData保留了CoreData的某些特性(使用相同的存储架构),但它确实使我们能够使用简单的声明代码持续存在数据。对于Swift开发人员来说,这似乎可能是改变游戏规则的人,因此我立即必须检查一下。

当我探讨了SwiftData的基本用例时,我注意到SwiftData的使用降至6个步骤左右。在本文中,我将指导您使用SwiftData构建一个简单的待办事项应用程序,从而阐明这些基本步骤。本文将不涵盖应用程序的UI实现,因为重点是SwiftData

这是我们将要介绍的六个步骤:

  1. import SwiftData.
  2. 创建模型:为数据定义类。
  3. 将您的模型设置为modelContainer:这使SwiftData知道它将使用哪些数据。
  4. 在您的观点中声明@Environment(\.modelContext):这确立了您的数据存在并受到操纵的上下文。
  5. 前缀@Query到一个数组变量:访问您存储的数据的包装器。
  6. 与您的CRUD操作的上下文互动。

让我们从更详细地研究这些步骤中开始。

1.导入SwiftData

第一步是到import SwiftData。是的,我知道这很明显,但有时我们都忘记了。

import SwiftData

2.创建模型

过去,当使用CoreData时,您将打开一个.xcdatamodel文件并通过添加“ add entity”来实现实体。在SwiftData中,我们只需使用@Model与普通的Swift类型建模数据。在这里,我将@Model包装器放在数据类ToDoItem上方。该模型将代表我们的待办事项列表中的各个任务。每个任务都将具有标题,内容和添加的日期。

@Model
class ToDoItem {
    let title: String
    let content: String
    let dateAdded: Date

    init(title: String, content: String, dateAdded: Date = .init()) {
        self.title = title
        self.content = content
        self.dateAdded = dateAdded
    }
}

3.在App中设置.modelContainer()

SwiftData中,.modelContainer()用于在应用程序中为您的数据模型建立存储区域或“容器”。将其视为指定应用程序中的特定空间以存储ToDoItem模型的所有数据实例。

在主应用程序结构中设置了容器(在这种情况下,SwiftDataToDoExampleApp,因为这可以确保在整个应用程序中访问数据。

@main
struct SwiftDataToDoExampleApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: ToDoItem.self)
    }
}

在此代码段中,.modelContainer(for: ToDoItem.self)添加到WindowGroup中。这告诉应用程序创建专门针对ToDoItem实例的存储区域。现在,无论我们在应用程序中的何处,我们都可以操纵我们的待办事项。

4.在您看来声明@Environment(\.modelContext)

接下来,在您的视图中声明@environment(.modelcontext)。 @Environment(\.modelContext)允许我们从容器中阅读。这听起来有些复杂,但这本质上是从步骤3中建立您的视图与存储在模型范围内的数据之间的连接的一种方法。换句话说,它的作用就像门户一样,我们的视图可以用来与ModelContainer进行交互。

struct ContentView: View {
    @Environment(\.modelContext) var context
    // ...
}

5.前缀@Query到数组变量

SwiftUI视图中,您可以使用包装器@Query获取数据。 SwiftDataSwiftUI一起工作,当基础数据更改时,视图将自动更新,并使用最新数据进行更新。

@Query var items: [ToDoItem]

就像在CoreData中一样,您可以将SortDescriptorPredicate添加到获取中。在下面的情况下,ToDoItem将以添加时的相反顺序显示。

@Query(FetchDescriptor(sortBy: [SortDescriptor(\.dateAdded, order: .reverse)]),animation: .snappy) private var items: [ToDoItem]

6.与您的上下文互动以进行CRUD操作

现在,我们已经完成了模型,容器,上下文以及与视图进行通信的方式的设置,现在该是时候与我们的上下文进行交互了。与上下文的第一个交互是将数据(ToDoItem)插入上下文。将ToDoItem插入上下文之后,请不要忘记保存它。这是一个两步的过程,对上下文所做的更改将通过保存来最终确定。

在下面的摘要中,我创建了两个函数来插入该项目,然后将其保存到上下文中。

func insert(_ item: ToDoItem) {
    context.insert(item)
    save()
}

func save() {
    do {
        try context.save()
    } catch {
        print(error.localizedDescription)
    }
}

删除也是如此。从上下文中删除,然后保存到上下文以确保已更新。

func delete(_ item: ToDoItem) {
    context.delete(item)
    save()
} 

以及上述6个步骤,我们已将SwiftData实施到我们的待办事项应用中。我将在下面包含所有UI代码,以便您可以看到我如何整理整个项目。可以随意将其复制并粘贴到Xcode中。

最后,请记住,SwiftData仍在Beta中,它可能会随机崩溃,尤其是在保存后。我想,随着Xcode 15来自Beta,这些问题将被更新。

应用屏幕截图

App Preview Gif

代码

contentvievice.swoft

import SwiftUI
import SwiftData

struct ContentView: View {
    @Environment(\.modelContext) var context
    @Query var items: [ToDoItem]

    @State private var showingSheet: Bool = false
    @State private var title: String = ""
    @State private var content: String = ""

    var body: some View {
        NavigationStack {
            List {
                ForEach(items)  { item in
                    VStack(alignment: .leading) {
                        HStack(alignment: .top) {

                            Text(item.title)
                                .bold()

                            Spacer()

                            Text(item.dateAdded.formatted(date: .numeric, time: .shortened))
                                .font(.caption)
                                .foregroundStyle(.gray)

                        }

                        Text(item.content)
                            .font(.subheadline)
                    }
                    .swipeActions(content: {
                        Button(action: {
                            self.delete(item)
                        }, label: {
                            Label("Delete", systemImage: "xmark.bin.fill")
                        })
                        .tint(Color.red)
                    })
                }
            }
            .navigationTitle("To Do List")
            .toolbar {
                ToolbarItem(placement: .topBarTrailing) {
                    Button {
                        showingSheet.toggle()
                    } label: {
                        Image(systemName: "plus")
                    }

                }
            }
            .sheet(isPresented: $showingSheet, content: {
                Form {
                    Section {
                        TextField("Add the title...", text: $title)
                    } header: {
                        Text("Title")
                    }

                    Section {
                        TextEditor(text: $content)
                    } header: {
                        Text("Content / Notes")
                    }

                    Section {
                        Button("Add Item") {
                            let item = ToDoItem(title: title, content: content)
                            insert(item)

                            showingSheet = false
                            title = ""
                            content = ""
                        }
                        .disabled(title.isEmpty || content.isEmpty)
                    }
                }
            })
        }
    }

    func insert(_ item: ToDoItem) {
        context.insert(item)
        save()
    }

    func delete(_ item: ToDoItem) {
        context.delete(item)
        save()
    }

    func save() {
        do {
            try context.save()
        } catch {
            print(error.localizedDescription)
        }
    }
}

todoitem.swift

import SwiftUI
import SwiftData

@Model
class ToDoItem {
    let title: String
    let content: String
    let dateAdded: Date

    init(title: String, content: String, dateAdded: Date = .init()) {
        self.title = title
        self.content = content
        self.dateAdded = dateAdded
    }
}

swiftdatatodoexampleapp.swift

import SwiftUI

@main
struct SwiftDataToDoExampleApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: ToDoItem.self)
    }
}

感谢您查看帖子。
最好!

Dean Thompson

跟着我!
LinkedIn
Twitter
Instagram

参考

Apple's Press Release

使用KavSoft的SwiftData

的很好示例

亚历山大·洛根(Alexander Logan)的精彩文章。还深入介绍了与关系使用SwiftData:Alexander Logan's Blog