CRUD - 업데이트 - Swift SDK
이 페이지의 내용
Realm 객체 업데이트
Realm 객체 업데이트는 쓰기 트랜잭션(write transaction) 내에서 이루어져야 합니다. 쓰기 트랜잭션(write transaction)에 대한 자세한 내용은 트랜잭션을 참조하세요.
이 페이지의 예시 관련 정보
이 페이지의 예시에서는 다음 모델을 사용합니다.
// DogToy.h @interface DogToy : RLMObject @property NSString *name; @end // Dog.h @interface Dog : RLMObject @property NSString *name; @property int age; @property NSString *color; // To-one relationship @property DogToy *favoriteToy; @end // Enable Dog for use in RLMArray RLM_COLLECTION_TYPE(Dog) // Person.h // A person has a primary key ID, a collection of dogs, and can be a member of multiple clubs. @interface Person : RLMObject @property int _id; @property NSString *name; // To-many relationship - a person can have many dogs @property RLMArray<Dog *><Dog> *dogs; // Inverse relationship - a person can be a member of many clubs @property (readonly) RLMLinkingObjects *clubs; @end RLM_COLLECTION_TYPE(Person) // DogClub.h @interface DogClub : RLMObject @property NSString *name; @property RLMArray<Person *><Person> *members; @end // Dog.m @implementation Dog @end // DogToy.m @implementation DogToy @end // Person.m @implementation Person // Define the primary key for the class + (NSString *)primaryKey { return @"_id"; } // Define the inverse relationship to dog clubs + (NSDictionary *)linkingObjectsProperties { return @{ @"clubs": [RLMPropertyDescriptor descriptorWithClass:DogClub.class propertyName:@"members"], }; } @end // DogClub.m @implementation DogClub @end
class Dog: Object { var name = "" var age = 0 var color = "" var currentCity = "" var citiesVisited: MutableSet<String> var companion: AnyRealmValue // Map of city name -> favorite park in that city var favoriteParksByCity: Map<String, String> } class Person: Object { true) var id = 0 (primaryKey: var name = "" // To-many relationship - a person can have many dogs var dogs: List<Dog> // Embed a single object. // Embedded object properties must be marked optional. var address: Address? } class Address: EmbeddedObject { var street: String? var city: String? var country: String? var postalCode: String? }
객체 업데이트
쓰기 트랜잭션(write transaction) 내에서 다른 Swift 또는 오브젝티브-C 객체를 업데이트하는 것과 동일한 방식으로 Realm 객체의 속성을 수정할 수 있습니다.
RLMRealm *realm = [RLMRealm defaultRealm]; // Open a thread-safe transaction. [realm transactionWithBlock:^{ // Get a dog to update. Dog *dog = [[Dog allObjectsInRealm: realm] firstObject]; // Update some properties on the instance. // These changes are saved to the realm. dog.name = @"Wolfie"; dog.age += 1; }];
let realm = try! Realm() // Get a dog to update let dog = realm.objects(Dog.self).first! // Open a thread-safe transaction try! realm.write { // Update some properties on the instance. // These changes are saved to the realm dog.name = "Wolfie" dog.age += 1 }
키-값 코딩으로 속성 업데이트
Object
, Result
및 List
은(는) 모두 키-값 코딩 을 준수합니다. . 이는 런타임에 업데이트 할 속성 을 결정해야 할 때 유용할 수 있습니다.
KVC를 컬렉션에 적용하는 것은 객체를 대량으로 업데이트하는 좋은 방법입니다. 모든 항목에 대한 접근자를 생성하는 동안 컬렉션을 반복하는 오버헤드를 피할 수 있습니다.
let realm = try! Realm() let allDogs = realm.objects(Dog.self) try! realm.write { allDogs.first?.setValue("Sparky", forKey: "name") // Move the dogs to Toronto for vacation allDogs.setValue("Toronto", forKey: "currentCity") }
이 방법으로 내장된 객체 또는 관계에 대한 값을 추가할 수도 있습니다. 다음 예시에서는 객체의 목록 속성에 컬렉션을 추가합니다.
RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^() { // Create a person to take care of some dogs. Person *ali = [[Person alloc] initWithValue:@{@"_id": @1, @"name": @"Ali"}]; [realm addObject:ali]; // Find dogs younger than 2. RLMResults<Dog *> *puppies = [Dog objectsInRealm:realm where:@"age < 2"]; // Batch update: give all puppies to Ali. [ali setValue:puppies forKey:@"dogs"]; }];
let realm = try! Realm() try! realm.write { // Create a person to take care of some dogs. let person = Person(value: ["id": 1, "name": "Ali"]) realm.add(person) let dog = Dog(value: ["name": "Rex", "age": 1]) realm.add(dog) // Find dogs younger than 2. let puppies = realm.objects(Dog.self).filter("age < 2") // Give all puppies to Ali. person.setValue(puppies, forKey: "dogs") }
객체 업서트
업서트는 객체가 이미 존재하는지 여부에 따라 객체를 삽입하거나 업데이트합니다. 업서트를 사용하려면 데이터 모델에 프라이머리 키가 있어야 합니다.
객체 를 업서트 하려면 -[RLMRealm addOrUpdateObject:]를 호출합니다.
RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^{ Person *jones = [[Person alloc] initWithValue:@{@"_id": @1234, @"name": @"Jones"}]; // Add a new person to the realm. Since nobody with ID 1234 // has been added yet, this adds the instance to the realm. [realm addOrUpdateObject:jones]; Person *bowie = [[Person alloc] initWithValue:@{@"_id": @1234, @"name": @"Bowie"}]; // Judging by the ID, it's the same person, just with a different name. // This overwrites the original entry (i.e. Jones -> Bowie). [realm addOrUpdateObject:bowie]; }];
객체 를 업서트 하려면 두 번째 매개변수인 업데이트 정책을 .modified
으로 설정하다 하여 Realm.add(_: 업데이트 :) 를 호출합니다.
let realm = try! Realm() try! realm.write { let person1 = Person(value: ["id": 1234, "name": "Jones"]) // Add a new person to the realm. Since nobody with ID 1234 // has been added yet, this adds the instance to the realm. realm.add(person1, update: .modified) let person2 = Person(value: ["id": 1234, "name": "Bowie"]) // Judging by the ID, it's the same person, just with a // different name. When `update` is: // - .modified: update the fields that have changed. // - .all: replace all of the fields regardless of // whether they've changed. // - .error: throw an exception if a key with the same // primary key already exists. realm.add(person2, update: .modified) }
프라이머리 키와 업데이트할 값의 하위 집합을 전달하여 객체를 부분적으로 업데이트할 수도 있습니다.
RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^{ // Only update the provided values. // Note that the "name" property will remain the same // for the person with primary key "_id" 123. [Person createOrUpdateModifiedInRealm:realm withValue:@{@"_id": @123, @"dogs": @[@[@"Buster", @5]]}]; }];
let realm = try! Realm() try! realm.write { // Use .modified to only update the provided values. // Note that the "name" property will remain the same // for the person with primary key "id" 123. realm.create(Person.self, value: ["id": 123, "dogs": [["Buster", 5]]], update: .modified) }
지도/사전 업데이트
표준 사전 과 마찬가지로 영역 맵 을 업데이트 할 수 있습니다.
let realm = try! Realm() // Find the dog we want to update let wolfie = realm.objects(Dog.self).where { $0.name == "Wolfie" }.first! print("Wolfie's favorite park in New York is: \(wolfie.favoriteParksByCity["New York"])") XCTAssertTrue(wolfie.favoriteParksByCity["New York"] == "Domino Park") // Update values for keys, or add values if the keys do not currently exist try! realm.write { wolfie.favoriteParksByCity["New York"] = "Washington Square Park" wolfie.favoriteParksByCity.updateValue("A Street Park", forKey: "Boston") wolfie.favoriteParksByCity.setValue("Little Long Pond", forKey: "Seal Harbor") } XCTAssertTrue(wolfie.favoriteParksByCity["New York"] == "Washington Square Park")
MutableSet 속성 업데이트
쓰기 트랜잭션(write transaction) 중에 insert
요소를 MutableSet 에 넣어 속성에 추가할 수 있습니다. 여러 세트로 작업하는 경우 한 세트에 포함된 세트 요소를 다른 세트에 삽입하거나 제거할 수도 있습니다. 또는 두 세트의 공통 요소만 포함하도록 세트를 변경할 수 있습니다.
let realm = try! Realm() // Record a dog's name, current city, and store it to the cities visited. let dog = Dog() dog.name = "Maui" dog.currentCity = "New York" try! realm.write { realm.add(dog) dog.citiesVisited.insert(dog.currentCity) } // Update the dog's current city, and add it to the set of cities visited. try! realm.write { dog.currentCity = "Toronto" dog.citiesVisited.insert(dog.currentCity) } XCTAssertEqual(dog.citiesVisited.count, 2) // If you're operating with two sets, you can insert the elements from one set into another set. // The dog2 set contains one element that isn't present in the dog set. try! realm.write { dog.citiesVisited.formUnion(dog2.citiesVisited) } XCTAssertEqual(dog.citiesVisited.count, 3) // Or you can remove elements that are present in the second set. This removes the one element // that we added above from the dog2 set. try! realm.write { dog.citiesVisited.subtract(dog2.citiesVisited) } XCTAssertEqual(dog.citiesVisited.count, 2) // If the sets contain common elements, you can mutate the set to only contain those common elements. // In this case, the two sets contain no common elements, so this set should now contain 0 items. try! realm.write { dog.citiesVisited.formIntersection(dog2.citiesVisited) } XCTAssertEqual(dog.citiesVisited.count, 0)
AnyRealmValue 속성 업데이트
할당을 통해 AnyRealmValue 속성을 업데이트할 수 있지만 값을 할당할 때 값의 유형을 지정해야 합니다. Realm Swift SDK는 AnyRealmValue가 저장할 수 있는 모든 유형을 반복하는 AnyRealmValue 열거형을 제공합니다.
let realm = try! Realm() // Get a dog to update let rex = realm.objects(Dog.self).where { $0.name == "Rex" }.first! try! realm.write { // As with creating an object with an AnyRealmValue, you must specify the // type of the value when you update the property. rex.companion = .object(Dog(value: ["name": "Regina"])) }
내장된 객체 속성 업데이트
내장된 객체의 속성을 업데이트하려면 쓰기 트랜잭션(write transaction)에서 속성을 수정합니다. 내장된 객체가 null이면 내장된 객체 속성을 업데이트해도 아무런 효과가 없습니다.
RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock: ^{ Contact *contact = [Contact objectInRealm:realm forPrimaryKey:[[RLMObjectId alloc] initWithString:@"5f481c21f634a1f4eeaa7268" error:nil]]; contact.address.street = @"Hollywood Upstairs Medical College"; contact.address.city = @"Los Angeles"; contact.address.postalCode = @"90210"; NSLog(@"Updated contact: %@", contact); }];
// Open the default realm let realm = try! Realm() let idOfPersonToUpdate = 123 // Find the person to update by ID guard let person = realm.object(ofType: Person.self, forPrimaryKey: idOfPersonToUpdate) else { print("Person \(idOfPersonToUpdate) not found") return } try! realm.write { // Update the embedded object directly through the person // If the embedded object is null, updating these properties has no effect person.address?.street = "789 Any Street" person.address?.city = "Anytown" person.address?.postalCode = "12345" print("Updated person: \(person)") }
내장된 객체 덮어쓰기
내장된 객체를 덮어쓰려면 쓰기 트랜잭션(write transaction)에서 당사자의 내장된 객체 속성을 새 인스턴스에 다시 할당합니다.
RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock: ^{ Contact *contact = [Contact objectInRealm:realm forPrimaryKey:[[RLMObjectId alloc] initWithString:@"5f481c21f634a1f4eeaa7268" error:nil]]; Address *newAddress = [[Address alloc] init]; newAddress.street = @"Hollywood Upstairs Medical College"; newAddress.city = @"Los Angeles"; newAddress.country = @"USA"; newAddress.postalCode = @"90210"; contact.address = newAddress; NSLog(@"Updated contact: %@", contact); }];
// Open the default realm let realm = try! Realm() let idOfPersonToUpdate = 123 // Find the person to update by ID guard let person = realm.object(ofType: Person.self, forPrimaryKey: idOfPersonToUpdate) else { print("Person \(idOfPersonToUpdate) not found") return } try! realm.write { let newAddress = Address() newAddress.street = "789 Any Street" newAddress.city = "Anytown" newAddress.country = "USA" newAddress.postalCode = "12345" // Overwrite the embedded object person.address = newAddress print("Updated person: \(person)") }
비동기적 객체 업데이트
Swift 동시성 기능을 통해 행위자 분리 영역을 사용하여 객체를 비동기적으로 업데이트할 수 있습니다.
행위자와 Realm 사용 페이지에 정의된 RealmActor
예제의 함수는 행위자 격리 영역에서 객체를 업데이트하는 방법을 보여줍니다.
func updateTodo(_id: ObjectId, name: String, owner: String, status: String) async throws { try await realm.asyncWrite { realm.create(Todo.self, value: [ "_id": _id, "name": name, "owner": owner, "status": status ], update: .modified) } }
그리고 Swift의 비동기 구문을 사용하여 이 업데이트를 수행할 수도 있습니다.
let actor = try await RealmActor() // Read objects in functions isolated to the actor and pass primitive values to the caller func getObjectId(in actor: isolated RealmActor, forTodoNamed name: String) async -> ObjectId { let todo = actor.realm.objects(Todo.self).where { $0.name == name }.first! return todo._id } let objectId = await getObjectId(in: actor, forTodoNamed: "Keep it safe") try await actor.updateTodo(_id: objectId, name: "Keep it safe", owner: "Frodo", status: "Completed")
이 작업은 호출 스레드에서 I/O를 차단하거나 수행하지 않습니다. Swift 동시성 기능을 사용하여 Realm에 쓰는 방법에 대해 자세히 알아보려면 Actors와 함께 Realm 사용 - Swift SDK를 참조하세요.
클래스 프로젝션으로 속성 업데이트
클래스 프로젝션 속성 변경
쓰기 트랜잭션(write transaction)에서 클래스 프로젝션의 속성을 변경할 수 있습니다.
// Retrieve all class projections of the given type `PersonProjection` // and filter for the first class projection where the `firstName` property // value is "Jason" let person = realm.objects(PersonProjection.self).first(where: { $0.firstName == "Jason" })! // Update class projection property in a write transaction try! realm.write { person.firstName = "David" }