문서 메뉴
문서 홈
/ /
Atlas Device SDK
/ /

CRUD - 업데이트 - Swift SDK

이 페이지의 내용

  • Realm 객체 업데이트
  • 이 페이지의 예시 관련 정보
  • 객체 업데이트
  • 키-값 코딩으로 속성 업데이트
  • 객체 업서트
  • 지도/사전 업데이트
  • MutableSet 속성 업데이트
  • AnyRealmValue 속성 업데이트
  • 내장된 객체 속성 업데이트
  • 포함된 객체 덮어쓰기
  • 비동기적 객체 업데이트
  • 클래스 프로젝션으로 속성 업데이트
  • 클래스 프로젝션 속성 변경

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 {
@Persisted var name = ""
@Persisted var age = 0
@Persisted var color = ""
@Persisted var currentCity = ""
@Persisted var citiesVisited: MutableSet<String>
@Persisted var companion: AnyRealmValue
// Map of city name -> favorite park in that city
@Persisted var favoriteParksByCity: Map<String, String>
}
class Person: Object {
@Persisted(primaryKey: true) var id = 0
@Persisted var name = ""
// To-many relationship - a person can have many dogs
@Persisted var dogs: List<Dog>
// Embed a single object.
// Embedded object properties must be marked optional.
@Persisted var address: Address?
}
class Address: EmbeddedObject {
@Persisted var street: String?
@Persisted var city: String?
@Persisted var country: String?
@Persisted var postalCode: String?
}

쓰기 트랜잭션 내에서 다른 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(_:update:) 를 호출합니다.

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

표준 사전 과 마찬가지로 Realm 맵 을 업데이트할 수 있습니다.

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

쓰기 트랜잭션(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 속성을 업데이트할 수 있지만 값을 할당할 때 값의 유형을 지정해야 합니다. 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"]))
}

포함된 객체의 속성을 업데이트하려면 쓰기 트랜잭션에서 속성을 수정합니다. 포함된 객체가 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)")
}

포함된 객체를 덮어쓰려면 쓰기 트랜잭션에서 당사자의 포함된 객체 속성을 새 인스턴스에 다시 할당합니다.

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 예제의 함수는 행위자 격리 Realm에서 객체를 업데이트하는 방법을 보여줍니다.

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를 참조하세요.

쓰기 트랜잭션에서 클래스 프로젝션의 속성을 변경할 수 있습니다.

// 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"
}

돌아가기

읽기

다음

삭제