Docs Menu

데이터 쓰기 - SwiftUI

트랜잭션 블록 내에서 쓰기를 수행하는 것 외에도, Realm Swift SDK는 쓰기 트랜잭션(write transaction)을 명시적으로 수행하지 않고도 빠른 쓰기를 가능하게 하는 편리한 기능을 제공합니다.

@ObservedRealmObject 또는 @ObservedResults 속성 래퍼(wrapper)를 사용하는 경우 쓰기 트랜잭션(write transaction)을 암시적으로 열 수 있습니다. 상태 객체에 대한 양방향 바인딩을 생성하려면 $ 연산자를 사용합니다. 그런 다음 Realm 객체나 컬렉션을 변경하면 암시적 쓰기가 시작됩니다.

Realm SwiftUI 속성 래퍼(wrapper)는 고정된 데이터와 함께 작동하여 스레드 안전성을 제공합니다. $을(를) 사용하여 양방향 바인딩을 만들면 Realm Swift SDK가 동결된 객체를 동결 해제하여 해당 객체에 쓸 수 있도록 관리합니다.

이 예시에서는 상태 객체의 속성 중 하나를 사용하여 양방향 바인딩을 생성합니다. $dog.favoriteToy은(는) 모델 개 객체의 favoriteToy 속성에 대한 바인딩을 생성합니다.

이 예제에서 앱 사용자가 해당 필드를 업데이트하면 Realm은 암시적 쓰기 트랜잭션(write transaction)을 열고 새 값을 데이터베이스에 저장합니다.

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) (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 속성 래퍼(wrapper)는 SwiftUI 보기에서 사용하기 위한 것입니다. View 모델에서 결과를 관찰하려면 변경 리스너를 등록하십시오.

목록 속성이 있는 @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")
}
}
}
}
}

새 객체를 생성하고 그 속성 중 하나를 영역에 이미 존재하는 객체로 설정하는 경우가 있을 수 있습니다. 그런 다음 새 객체를 영역에 추가하려고 하면 다음과 유사한 오류가 표시됩니다.

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

이 경우 .create 메서드를 사용하여 객체를 초기화하고 modified: .update 를 사용하여 해당 속성을 기존 객체로 설정합니다.

예시

favoriteToy 속성이 단순한 String이(가) 아니라 선택적 DogToy 객체인 DoggoDB Dog 모델 버전을 생각해 보세요.

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

앱이 새 Dog 객체를 생성하려고 할 때 DogToy 객체가 이미 영역에 존재하는지 확인한 다음 favoriteToy 속성을 기존 강아지 장난감으로 설정할 수 있습니다.

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 객체를 확인하여 현재 관리 중인 영역을 찾아도 아무것도 발견하지 못합니다.

$ 표기법을 사용하여 Dog 객체를 Person 객체에 추가하는 빠른 쓰기를 수행할 때 이 쓰기는 보기에서 액세스할 수 있는 영역을 사용합니다. 이는 @ObservedRealmObject 또는 @ObservedResults 속성 래퍼(wrapper)에 의해 암시적으로 열리는 영역 인스턴스입니다. 그러나 기존 DogToy 객체는 다른 영역 인스턴스에서 관리할 수 있습니다.

이 오류를 해결하려면 .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)
}

경우에 따라 빠른 쓰기를 수행하기 위해 암시적인 $을(를) 사용하는 대신 명시적으로 쓰기 트랜잭션(write transaction)을 수행해야할 수 있습니다. 다음과 같은 경우에 이 작업을 수행할 수 있습니다.

  • 쓰기를 수행하려면 추가 객체를 조회해야 합니다.

  • 보기에서 액세스할 수 없는 객체에 쓰기를 수행해야 합니다.

@ObservedRealmObject 또는 @ObservedResults(으)로 관찰 중인 객체를 객체를 수정하는 명시적 쓰기 트랜잭션(write transaction)을 수행하는 함수에 전달하는 경우 먼저 해당 객체를 고정 해제해야 합니다.

let thawedCompany = company.thaw()!

객체 또는 컬렉션에서 .realm을(를) 호출하여 영역 객체를 관리하는 영역에 액세스할 수 있습니다.

let realm = company.realm!.thaw()

SwiftUI 속성 래퍼(wrapper)는 고정된 객체를 사용하기 때문에 영역 객체에 쓰기 전에 영역을 재개해야 합니다.

예시

Company 객체에 Employee 객체 목록이 있는 DoggoDB 앱 버전을 생각해 보세요. 각 Employee에는 Dog 객체 목록이 있습니다. 그러나 비즈니스상의 이유로 Employee에 연결되지 않고 Company 객체에서 직접 사용할 수 있는 Dog 객체 목록이 필요할 수도 있습니다. 모델은 다음과 같습니다.

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)
}
}