Docs 菜单
Docs 主页
/ /
Atlas Device SDKs
/ /

写入数据 - SwiftUI

在此页面上

  • 执行快速写入
  • 更新对象的属性
  • 在 ObservedResults 集合中添加或删除对象
  • 将对象附加到列表
  • 使用“创建”将对象复制到 Realm 中
  • 执行显式写入

除了在事务区块内执行写入之外,Realm Swift SDK 还提供了一项便利功能,无需显式执行写入事务即可实现快速写入。

使用 @ObservedRealmObject@ObservedResults 属性包装器时,可以隐式启动写事务。使用 $ 操作符可创建与状态对象的双向绑定。然后,当您对绑定对象或集合进行更改时,您将启动隐式写入。

Realm SwiftUI 属性包装器可处理冻结的数据,以确保线程安全。当您使用 $ 创建双向绑定时,Realm Swift SDK 会管理已冻结对象的解冻操作,以便您可以写入它们。

在本例中,我们使用状态对象的属性之一创建双向绑定。$dog.favoriteToy 用于创建与模型 Dog 对象的 favoriteToy 属性的绑定

当应用程序用户更新此示例中的该字段时,Realm 将打开隐式写入事务,并将新值保存到数据库中。

struct EditDogDetails: View {
@ObservedRealmObject var dog: Dog
var body: some View {
VStack {
Text(dog.name)
.font(.title2)
TextField("Favorite toy", text: $dog.favoriteToy)
}
}
}

常规Realm结果集合是不可变的,而 ObservedResults是可变集合,允许您使用双向绑定执行写入。 当您更新绑定集合时, Realm会打开一个隐式写事务(write transaction),并将更改保存到集合中。

在此示例中,我们将使用 onDelete 中的 $dogs.remove 以从结果集中删除一个元素。此处使用 $dogs 会创建一个针对 BoundCollection 的双向绑定,而它可用于修改 @ObservedResults dogs 集合。

我们使用 addDogButton 中的 $dogs.append 将项目添加到结果中。

这些动作会直接写入@ObservedResults 集合。

struct DogsListView: View {
@ObservedResults(Dog.self) var dogs
var body: some View {
NavigationView {
VStack {
// The list shows the dogs in the realm.
List {
ForEach(dogs) { dog in
DogRow(dog: dog)
// Because `$dogs` here accesses an ObservedResults
// collection, we can remove the specific dog from the collection.
// Regular Realm Results are immutable, but you can write directly
// to an `@ObservedResults` collection.
}.onDelete(perform: $dogs.remove)
}.listStyle(GroupedListStyle())
.navigationBarTitle("Dogs", displayMode: .large)
.navigationBarBackButtonHidden(true)
// Action bar at bottom contains Add button.
HStack {
Spacer()
Button(action: {
// The bound collection automatically
// handles write transactions, so we can
// append directly to it. This example assumes
// we have some values to populate the Dog object.
$dogs.append(Dog(value: ["name":"Bandido"]))
}) { Image(systemName: "plus") }
.accessibilityIdentifier("addDogButton")
}.padding()
}
}
}
}

注意

@ObservedResults 属性包装器用于“SwiftUI 视图”。如果您想观察视图模型中的结果,请注册更改监听器

与具有列表属性的 @ObservedRealmObject 进行双向绑定时,可将新对象添加到列表中。

在本例中,Person 对象具有与一只或多只狗形成对多关系的列表属性。

class Person: Object, ObjectKeyIdentifiable {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var firstName = ""
@Persisted var lastName = ""
...
@Persisted var dogs: List<Dog>
}

当用户按下 Save 按钮时,会出现以下情况:

  • 创建一个 Dog 对象,其中包含用户输入的详细信息

  • Dog 对象添加到 Person 对象的 dogs 列表中

struct AddDogToPersonView: View {
@ObservedRealmObject var person: Person
@Binding var isInAddDogView: Bool
@State var name = ""
@State var breed = ""
@State var weight = 0
@State var favoriteToy = ""
@State var profileImageUrl: URL?
var body: some View {
Form {
TextField("Dog's name", text: $name)
TextField("Dog's breed", text: $breed)
TextField("Dog's weight", value: $weight, format: .number)
TextField("Dog's favorite toy", text: $favoriteToy)
TextField("Image link", value: $profileImageUrl, format: .url)
.keyboardType(.URL)
.textInputAutocapitalization(.never)
.disableAutocorrection(true)
Section {
Button(action: {
let dog = createDog(name: name, breed: breed, weight: weight, favoriteToy: favoriteToy, profileImageUrl: profileImageUrl)
$person.dogs.append(dog)
isInAddDogView.toggle()
}) {
Text("Save")
}
Button(action: {
isInAddDogView.toggle()
}) {
Text("Cancel")
}
}
}
}
}

有时,您可能会创建一个新对象,并将它的一个属性设为 Realm 中已存在的对象。然后,当您将新对象添加到 Realm 时,您就会看到类似以下内容的错误消息:

Object is already managed by another Realm. Use create instead to copy it into this Realm.

发生这种情况时,您可以使用.create 方法初始化该对象,并使用modified: .update将其属性设置为现有对象。

例子

以 DoggoDB Dog 模型的一个版本为例。其中,favoriteToy 属性不只是一个 String,而且是一个可选的 DogToy 对象:

class Dog: Object, ObjectKeyIdentifiable {
@Persisted(primaryKey: true) var _id: UUID
@Persisted var name = ""
...
@Persisted var favoriteToy: DogToy?
...
}

当您的应用程序要创建新的 Dog 对象时,它可能会检查Realm 中是否已存在 DogToy, 然后将 favoriteToy 属性设置为现有的 Dog Toy。

当您将新的 Dog 附加到 Person 对象时,您可能会看到类似以下内容的错误消息:

Object is already managed by another Realm. Use create instead to copy it into this Realm.

在将 Dog 对象附加到 Person 对象的 dogs 属性之前,该对象将将保持非托管状态。当 Realm Swift SDK 检查 Dog 对象以查找当前托管它的Realm 时,并未得到任何结果。

当您使用 $ 表示法将 Dog 对象附加到 Person 对象中以执行快速写入时,写入操作会利用该视图可访问的 Realm。这是由 @ObservedRealmObject@ObservedResults 属性包装器隐式打开的 Realm 实例。不过,现有 DogToy 对象可能由不同 Realm 实例托管。

要解决此错误,请使用.create 方法(当您初始化Dog对象时),并使用modified: .update设立其favoriteToy值设置为现有对象:

// When working with an `@ObservedRealmObject` `Person`, this is a frozen object.
// Thaw the object and get its realm to perform the write to append the new dog.
let thawedPersonRealm = frozenPerson.thaw()!.realm!
try! thawedPersonRealm.write {
// Use the .create method with `update: .modified` to copy the
// existing object into the realm
let dog = thawedPersonRealm.create(Dog.self, value:
["name": "Maui",
"favoriteToy": wubba],
update: .modified)
person.dogs.append(dog)
}

在某些情况下,您可能希望或需要显式执行写事务,而不是使用隐式 $ 执行快速写入操作。在以下情况下,您可能希望执行此操作:

  • 您需要查找其他对象才能执行写入

  • 需要对视图中您没有访问权限的对象执行写入操作

如果您使用 @ObservedRealmObject@ObservedResults 将观察中的 Realm 对象传递到一个函数中,并在该函数中执行显式写事务以修改对象时,您需要先将对象解冻。

let thawedCompany = company.thaw()!

您可以对对象或集合调用 .realm,来访问管理对象的 Realm:

let realm = company.realm!.thaw()

由于 SwiftUI 属性包装器使用冻结对象,因此您必须先解冻 Realm,然后才能对其执行写入操作。

例子

考虑 DoggoDB 应用程序的一个版本,其中Company 对象具有 Employee 对象列表。每个 Employee 都有一个 Dog 对象列表。但出于业务原因,您还希望在 Company 对象上有一个可以直接使用的 Dog 对象列表,而不需要与 Employee 关联。该模型可能如下所示:

class Company: Object, ObjectKeyIdentifiable {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var companyName = ""
@Persisted var employees: List<Employee>
@Persisted var dogs: List<Dog>
}

以某一视图为例,您可以访问 Company 对象,但想执行显式写入以将现有犬添加到现有员工。您的函数可能如下所示:

// The `frozenCompany` here represents an `@ObservedRealmObject var company: Company`
performAnExplicitWrite(company: frozenCompany, employeeName: "Dachary", dogName: "Maui")
func performAnExplicitWrite(company: Company, employeeName: String, dogName: String) {
// Get the realm that is managing the `Company` object you passed in.
// Thaw the realm so you can write to it.
let realm = company.realm!.thaw()
// Thawing the `Company` object that you passed in also thaws the objects in its List properties.
// This lets you append the `Dog` to the `Employee` without individually thawing both of them.
let thawedCompany = company.thaw()!
let thisEmployee = thawedCompany.employees.where { $0.name == employeeName }.first!
let thisDog = thawedCompany.dogs.where { $0.name == dogName }.first!
try! realm.write {
thisEmployee.dogs.append(thisDog)
}
}

后退

在各视图之间传递 Realm 数据