変更に対応する - React Swift SDK
項目一覧
すべての Realm オブジェクトはライブ オブジェクトであるため、変更されるたびに自動的に更新されます。 Realm は、プロパティが変更されるたびに通知イベントを発行します。 通知ハンドラーを登録してこれらの通知イベントをリッスンし、最新データで UI を更新できます。
このページでは、Swift で通知リスナーを手動で登録する方法を説明します。 Atlas Device SDK for Swift は、データが変更されたときに UI を自動的に更新できるようにする SwiftUI プロパティ ラッパーを提供します。 SwiftUIプロパティ ラッパーを使用して変更にReactする方法の詳細については、「 オブジェクトの監視 」を参照してください。
Realm 変更リスナーの登録
通知ハンドラーはRealm全体で登録できます。 Realm は、Realm に関連する書込みトランザクションがコミットされるたびに、通知ハンドラを呼び出します。 ハンドラーは変更に関する情報を受け取りません。
RLMRealm *realm = [RLMRealm defaultRealm]; // Observe realm notifications. Keep a strong reference to the notification token // or the observation will stop. RLMNotificationToken *token = [realm addNotificationBlock:^(RLMNotification _Nonnull notification, RLMRealm * _Nonnull realm) { // `notification` is an enum specifying what kind of notification was emitted. // ... update UI ... }]; // ... // Later, explicitly stop observing. [token invalidate];
let realm = try! Realm() // Observe realm notifications. Keep a strong reference to the notification token // or the observation will stop. let token = realm.observe { notification, realm in // `notification` is an enum specifying what kind of notification was emitted viewController.updateUI() } // ... // Later, explicitly stop observing. token.invalidate()
コレクション変更リスナーの登録
Realm 内のコレクションに通知ハンドラーを登録できます。
Realm はハンドラーに次のように通知します。
最初に コレクションを取得した後、
書込みトランザクションによってコレクション内のオブジェクトが追加、変更、または削除されるたび。
通知は、削除、挿入、および変更されたオブジェクトのインデックスの 3 つのインデックス リストを使用して、前回の通知以降の変更を説明します。
重要
順序は重要
コレクション通知ハンドラーでは、削除、挿入、変更の順に常に変更を適用します。 削除前に挿入を処理すると、予期しない動作が発生する可能性があります。
コレクション通知では、書込みトランザクション中にどのオブジェクトが削除、追加、または変更されたかを報告する change
パラメーターが提供されます。 この RealmCollectionchangeは、 UITableView
のバッチ更新メソッドに渡すことができるインデックス パスの配列に解決されます。
重要
高頻度更新
このコレクション変更リスナーの例では、高頻度の更新をサポートしていません。 集中的なワークロードでは、このコレクション変更リスナーによってアプリが例外をスローする可能性があります。
@interface CollectionNotificationExampleViewController : UITableViewController @end @implementation CollectionNotificationExampleViewController { RLMNotificationToken *_notificationToken; } - (void)viewDidLoad { [super viewDidLoad]; // Observe RLMResults Notifications __weak typeof(self) weakSelf = self; _notificationToken = [[Dog objectsWhere:@"age > 5"] addNotificationBlock:^(RLMResults<Dog *> *results, RLMCollectionChange *changes, NSError *error) { if (error) { NSLog(@"Failed to open realm on background worker: %@", error); return; } UITableView *tableView = weakSelf.tableView; // Initial run of the query will pass nil for the change information if (!changes) { [tableView reloadData]; return; } // Query results have changed, so apply them to the UITableView [tableView performBatchUpdates:^{ // Always apply updates in the following order: deletions, insertions, then modifications. // Handling insertions before deletions may result in unexpected behavior. [tableView deleteRowsAtIndexPaths:[changes deletionsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic]; [tableView insertRowsAtIndexPaths:[changes insertionsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic]; [tableView reloadRowsAtIndexPaths:[changes modificationsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic]; } completion:^(BOOL finished) { // ... }]; }]; } @end
class CollectionNotificationExampleViewController: UITableViewController { var notificationToken: NotificationToken? override func viewDidLoad() { super.viewDidLoad() let realm = try! Realm() let results = realm.objects(Dog.self) // Observe collection notifications. Keep a strong // reference to the notification token or the // observation will stop. notificationToken = results.observe { [weak self] (changes: RealmCollectionChange) in guard let tableView = self?.tableView else { return } switch changes { case .initial: // Results are now populated and can be accessed without blocking the UI tableView.reloadData() case .update(_, let deletions, let insertions, let modifications): // Query results have changed, so apply them to the UITableView tableView.performBatchUpdates({ // Always apply updates in the following order: deletions, insertions, then modifications. // Handling insertions before deletions may result in unexpected behavior. tableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: 0)}), with: .automatic) tableView.insertRows(at: insertions.map({ IndexPath(row: $0, section: 0) }), with: .automatic) tableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: 0) }), with: .automatic) }, completion: { finished in // ... }) case .error(let error): // An error occurred while opening the Realm file on the background worker thread fatalError("\(error)") } } } }
オブジェクト変更リスナーの登録
Realm 内の特定のオブジェクトに通知ハンドラーを登録できます。 Realm はハンドラーに次のように通知します。
オブジェクトが削除された場合。
オブジェクトのプロパティのいずれかが変更された場合。
ハンドラーは、変更されたフィールドとオブジェクトが削除されたかどうかに関する情報を受け取ります。
@interface Dog : RLMObject @property NSString *name; @property int age; @end @implementation Dog @end RLMNotificationToken *objectNotificationToken = nil; void objectNotificationExample() { Dog *dog = [[Dog alloc] init]; dog.name = @"Max"; dog.age = 3; // Open the default realm RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^{ [realm addObject:dog]; }]; // Observe object notifications. Keep a strong reference to the notification token // or the observation will stop. Invalidate the token when done observing. objectNotificationToken = [dog addNotificationBlock:^(BOOL deleted, NSArray<RLMPropertyChange *> * _Nullable changes, NSError * _Nullable error) { if (error != nil) { NSLog(@"An error occurred: %@", [error localizedDescription]); return; } if (deleted) { NSLog(@"The object was deleted."); return; } NSLog(@"Property %@ changed to '%@'", changes[0].name, changes[0].value); }]; // Now update to trigger the notification [realm transactionWithBlock:^{ dog.name = @"Wolfie"; }]; }
// Define the dog class. class Dog: Object { var name = "" } var objectNotificationToken: NotificationToken? func objectNotificationExample() { let dog = Dog() dog.name = "Max" // Open the default realm. let realm = try! Realm() try! realm.write { realm.add(dog) } // Observe object notifications. Keep a strong reference to the notification token // or the observation will stop. Invalidate the token when done observing. objectNotificationToken = dog.observe { change in switch change { case .change(let object, let properties): for property in properties { print("Property '\(property.name)' of object \(object) changed to '\(property.newValue!)'") } case .error(let error): print("An error occurred: \(error)") case .deleted: print("The object was deleted.") } } // Now update to trigger the notification try! realm.write { dog.name = "Wolfie" } }
キー パス変更リスナーの登録
バージョン10.12.0の新機能。
オブジェクトまたはコレクションに通知ハンドラーを登録するだけでなく、任意の string keyPaths
パラメーターを渡して、監視するキー パスを指定できます。
例
// Define the dog class. class Dog: Object { var name = "" var favoriteToy = "" var age: Int? } var objectNotificationToken: NotificationToken? func objectNotificationExample() { let dog = Dog() dog.name = "Max" dog.favoriteToy = "Ball" dog.age = 2 // Open the default realm. let realm = try! Realm() try! realm.write { realm.add(dog) } // Observe notifications on some of the object's key paths. Keep a strong // reference to the notification token or the observation will stop. // Invalidate the token when done observing. objectNotificationToken = dog.observe(keyPaths: ["favoriteToy", "age"], { change in switch change { case .change(let object, let properties): for property in properties { print("Property '\(property.name)' of object \(object) changed to '\(property.newValue!)'") } case .error(let error): print("An error occurred: \(error)") case .deleted: print("The object was deleted.") } }) // Now update to trigger the notification try! realm.write { dog.favoriteToy = "Frisbee" } // When you specify one or more key paths, changes to other properties // do not trigger notifications. In this example, changing the "name" // property does not trigger a notification. try! realm.write { dog.name = "Maxamillion" } }
バージョン 10.14.0 の新機能。
部分的に型消去された PartialKeyPath{ オブジェクト または RealmCollections の
objectNotificationToken = dog.observe(keyPaths: [\Dog.favoriteToy, \Dog.age], { change in
keyPaths
を指定すると、 はそれらのkeyPaths
trigger 通知ブロック のみ を変更します。その他の変更では通知ブロックはtriggerされません。
例
プロパティの 1 つがsiblings
のリストであるDog
オブジェクトを考えてみましょう。
class Dog: Object { var name = "" var siblings: List<Dog> var age: Int? }
siblings
を keyPath
として渡して観察すると、siblings
リストへの挿入、削除、または変更によって通知がtriggerされます。 ただし、["siblings.name"]
を明示的に観察しない限り、someSibling.name
への変更によって通知がtriggerされることはありません。
注意
個別のキー パスでフィルタリングされる同じオブジェクト上の複数の通知トークンは、排他的にフィルタリングされません。 1 つの通知トークンに対して 1 つのキーパスの変更が満たされた場合、そのオブジェクトのすべての通知トークン ブロックが実行されます。
Realm コレクション
さまざまなコレクション タイプでキー パスを確認すると、次のような動作が予想されます。
LinkingObjects: : LinkingObject のプロパティを観察すると、そのプロパティへの変更の通知がトリガーされますが、その他のプロパティに対する変更の通知はtriggerされません。 リストまたはリストが作成するオブジェクトに挿入または削除があると、通知がtriggerされます。
リスト: リストのオブジェクトのプロパティを観察すると、そのプロパティへの変更の通知がトリガーされますが、その他のプロパティに対する変更の通知はtriggerされません。 リストまたはリストが作成するオブジェクトに挿入または削除があると、通知がtriggerされます。
マップ: マップのオブジェクトのプロパティを観察すると、そのプロパティへの変更の通知がトリガーされますが、その他のプロパティに対する変更の通知はtriggerされません。 マップまたはマップが存在するオブジェクトへの挿入または削除により通知がtriggerされます。
change
パラメーターは、各書込みトランザクション中に追加、削除、または変更されたキーと値のペアを、マップ内のキーの形式で報告します。MutableSet : MutableSet のオブジェクトのプロパティを観察すると、そのプロパティへの変更の通知がトリガーされますが、その他のプロパティに対する変更についての通知はtriggerされません。 MutableSet または MutableSet がオンになっているオブジェクトへの挿入または削除によって通知がtriggerされます。
結果: 結果のプロパティを観察すると、そのプロパティへの変更の通知がトリガーされますが、その他のプロパティに対する変更の通知はtriggerされません。 結果 への挿入または削除により通知triggerされます。
書き込みサイレント
オブジェクトの通知トークンを配列で Realm.write(WithNotification:_:) に渡すことで、特定のオブジェクトに通知を送信 せず に Realm に書き込むことができます。
RLMRealm *realm = [RLMRealm defaultRealm]; // Observe realm notifications RLMNotificationToken *token = [realm addNotificationBlock:^(RLMNotification _Nonnull notification, RLMRealm * _Nonnull realm) { // ... handle update }]; // Later, pass the token in an array to the realm's `-transactionWithoutNotifying:block:` method. // Realm will _not_ notify the handler after this write. [realm transactionWithoutNotifying:@[token] block:^{ // ... write to realm }]; // Finally [token invalidate];
let realm = try! Realm() // Observe realm notifications let token = realm.observe { notification, realm in // ... handle update } // Later, pass the token in an array to the realm.write(withoutNotifying:) // method to write without sending a notification to that observer. try! realm.write(withoutNotifying: [token]) { // ... write to realm } // Finally token.invalidate()
変更の監視を停止する
observe
呼び出しによって返されたトークンが無効になると、観察は停止します。 トークンは、 invalidate()
メソッドを呼び出して明示的に無効化できます。
重要
観察する必要がある限りトークンを保持する
トークンがスコープ外のローカル変数にある場合、通知は停止します。
RLMRealm *realm = [RLMRealm defaultRealm]; // Observe and obtain token RLMNotificationToken *token = [realm addNotificationBlock:^(RLMNotification _Nonnull notification, RLMRealm * _Nonnull realm) { /* ... */ }]; // Stop observing [token invalidate];
let realm = try! Realm() // Observe and obtain token let token = realm.observe { notification, realm in /* ... */ } // Stop observing token.invalidate()
キー値の観察
キー値監視コンプライアンス
Realm オブジェクトは キー値監視(KVE)に準拠しています ほとんどのプロパティでは、次のようになります。
Object
サブクラスのほぼすべての管理対象(無視されていない)プロパティObject
とList
のinvalidated
プロパティ
キー値の監視ではLinkingObjects
プロパティの監視はできません。
重要
オブジェクトを登録している間は、Realm( realm.add(obj)
または同様のメソッドを使用)にオブジェクトを追加することはできません。
管理対象と非管理の KVM に関する考慮事項
Object
サブクラスの非管理インスタンスのプロパティを監視する方法は、他の動的プロパティと同様です。
管理対象オブジェクトのプロパティの観察は異なります。 Realm 管理オブジェクトでは、次の場合にプロパティの値が変更されることがあります。
に割り当てる
Realm は、
realm.refresh()
を使用して手動で、またはランループ スレッドで自動的に更新されます別のスレッドで変更した後に書込みトランザクションを開始する
Realm は、書込みトランザクション (write transaction) で行われた変更を他のスレッドに一度に適用します。 オブジェクトは、キー値の監視通知を一度に表示します。 中間手順では KVE 通知はtriggerされません。
例
アプリが、プロパティを 1 から 10 に増加させる書込みトランザクションを実行するとします。 メイン スレッドでは、1 から 10 への変更の通知が 1 回直接行われます。 1 から 10 までの増分変更ごとに通知は届きません。
observeValueForKeyPath(_:ofObject:change:context:)
内から管理対象の Realm オブジェクトを変更しないでください。 プロパティ値は、書込みトランザクションではない場合、または書込みトランザクションの開始の一環として変更される可能性があります。
Realm リストの監視
Realm List
プロパティに加えられた変更を監視する方法は、 NSMutableArray
プロパティよりも簡単です。
これらを観察するには、
List
プロパティを動的としてマークする必要はありません。List
の変更メソッドを直接呼び出せます。 これを保存するプロパティを観察しているユーザーは、通知を受け取ります。
Realm はコードの互換性のためにこれをサポートしていますが、 mutableArrayValueForKey(_:)
を使用する必要はありません。
別のReactの変更に対応する
別のアクター上の通知を観察できます。 await object.observe(on: Actor)
またはawait collection.observe(on: Actor)
を呼び出すと、オブジェクトまたはコレクションが変更されるたびに呼び出されるブロックが登録されます。
// Create a simple actor actor BackgroundActor { public func deleteTodo(tsrToTodo tsr: ThreadSafeReference<Todo>) throws { let realm = try! Realm() try realm.write { // Resolve the thread safe reference on the Actor where you want to use it. // Then, do something with the object. let todoOnActor = realm.resolve(tsr) realm.delete(todoOnActor!) } } } // Execute some code on a different actor - in this case, the MainActor func mainThreadFunction() async throws { let backgroundActor = BackgroundActor() let realm = try! await Realm() // Create a todo item so there is something to observe try await realm.asyncWrite { realm.create(Todo.self, value: [ "_id": ObjectId.generate(), "name": "Arrive safely in Bree", "owner": "Merry", "status": "In Progress" ]) } // Get the collection of todos on the current actor let todoCollection = realm.objects(Todo.self) // Register a notification token, providing the actor where you want to observe changes. // This is only required if you want to observe on a different actor. let token = await todoCollection.observe(on: backgroundActor, { actor, changes in print("A change occurred on actor: \(actor)") switch changes { case .initial: print("The initial value of the changed object was: \(changes)") case .update(_, let deletions, let insertions, let modifications): if !deletions.isEmpty { print("An object was deleted: \(changes)") } else if !insertions.isEmpty { print("An object was inserted: \(changes)") } else if !modifications.isEmpty { print("An object was modified: \(changes)") } case .error(let error): print("An error occurred: \(error.localizedDescription)") } }) // Update an object to trigger the notification. // This example triggers a notification that the object is deleted. // We can pass a thread-safe reference to an object to update it on a different actor. let todo = todoCollection.where { $0.name == "Arrive safely in Bree" }.first! let threadSafeReferenceToTodo = ThreadSafeReference(to: todo) try await backgroundActor.deleteTodo(tsrToTodo: threadSafeReferenceToTodo) // Invalidate the token when done observing token.invalidate() }
別のアクターに対する変更通知の詳細については、「 別のアクターに対する通知の監視 」を参照してください。
クラスプロジェクションの変更に対応する
他の Realm オブジェクトと同様に、クラス プロジェクションのReact に対応できます。クラスプロジェクションの変更リスナーを登録すると、クラスプロジェクションオブジェクトから直接行われた変更の通知が表示されます。 また、クラス プロジェクション オブジェクトを通じてプロジェクションされる、基礎となるオブジェクトのプロパティに対する変更に関する通知も表示されます。
クラス プロジェクションで@Projected
ではない基礎オブジェクトのプロパティは通知を生成しません。
この通知ブロックは、次の変更に対して起動します。
Person.firstName
クラス プロジェクションの基礎となるPerson
オブジェクトの プロパティが、Person.lastName
またはPerson.friends
には変更されません。PersonProjection.firstName
プロパティを使用しますが、同じ基礎オブジェクトのプロパティを使用する別のクラス プロジェクションは使用できません。
let realm = try! Realm() let projectedPerson = realm.objects(PersonProjection.self).first(where: { $0.firstName == "Jason" })! let token = projectedPerson.observe(keyPaths: ["firstName"], { change in switch change { case .change(let object, let properties): for property in properties { print("Property '\(property.name)' of object \(object) changed to '\(property.newValue!)'") } case .error(let error): print("An error occurred: \(error)") case .deleted: print("The object was deleted.") } }) // Now update to trigger the notification try! realm.write { projectedPerson.firstName = "David" }
通知配信
通知の配信は次の場合に応じて異なる場合があります。
書込みトランザクション内で通知が行われるかどうか
書込み (write) と監視の相対的なスレッド
アプリケーションが通知の配信タイミングに依存する場合(通知を使用してUITableView
を更新する場合など)は、アプリケーション コードのコンテキストの具体的な動作を理解することが重要です。
監視スレッドとは異なるスレッドでのみ書込みを実行
変更通知内から監視されたコレクションまたはオブジェクトを読み取ると、コールバックが最後に呼び出されて以降にコールバックに渡されたコレクションの変更が常に正確に示されます。
変更通知の外部でコレクションまたはオブジェクトを読み取ると、常にそのオブジェクトの最新の変更通知で確認された値と完全に一致する値が提供されます。
変更通知内で観察されたもの以外のオブジェクトを読み取ると、その変更の通知が配信される前に異なる 値が表示される場合があります。 Realm refresh
は、1 回の操作でRealm全体を「古いバージョン」から「最新バージョン」にします。 ただし、「古いバージョン」と「最新バージョン」の間に複数の変更通知が起動された場合があります。 コールバック内では、通知が保留になっている変更が表示される場合があります。
異なるスレッドに対する書込み (write) は、最終的に監視スレッドに表示されるようになります。 refresh()
を明示的に呼び出すと、他のスレッドで行われた書込みが表示され、適切な通知が送信されるまでブロックされます。 通知コールバック内でrefresh()
を呼び出す場合、処理はありません。
通知以外で、監視スレッドで書込み (write) を実行
書込みトランザクションの開始時に、上記のすべての動作がこのコンテキストに適用されます。 さらに、常に最新バージョンのデータが表示されることが期待できます。
書込みトランザクション (write transaction) 内では、書込みトランザクション (write transaction) 内でそれまでに行った変更のみが表示されます。
書込みトランザクションをコミットしてから次の変更通知のセットが送信されるまでの間、書込みトランザクションで行った変更は確認できますが、その他の変更はありません。 異なるスレッドで行われた書込みは、次の通知セットを受信するまで表示されません。 同じスレッドで別の書込みを実行すると、前の書込みの通知が最初に送信されます。
通知内での書込みの実行
通知内で書込みを実行すると、いくつかの例外を除いて、上記と同じ動作の多くが表示されます。
書込みを実行したコールバックより前に呼び出されたコールバックは正常に動作します。 Realm は安定した順序で変更コールバックを呼び出しますが、これは正確には観察を追加した順序ではありません。
書込みを開始するとRealmが更新されます。これは別のスレッドが書込みを行っている場合に発生する可能性があり、これにより再帰通知がトリガーされます。 これらのネストされた通知は、コールバックへの最後の呼び出し以降に行われた変更を報告します。 が書込み (write) を行う前のコールバックの場合、内部通知は外部通知ですでに報告された変更の後に行われた変更のみを報告します。 書込み (write) を行っているコールバックが内部通知で再度書込み (write) を試みると、Realm は例外をスローします。 書込み (write) を行った後のコールバックは、両方の変更セットに対して単一の通知を受け取ります。
コールバックが書込み (write) を完了して戻ってくると、Realm は報告する変更がなくなるため、後続のコールバックを呼び出しません。 Realm は、書込み (write) が通知外で発生したかのように、書込み (write) について後で通知を提供します。
書き込みを開始しても Realm が更新されない場合は、書き込みは通常どおり行われます。 ただし、Realm は一貫性のない状態で後続のコールバックを呼び出します。 元の変更情報は引き続き報告されますが、監視されたオブジェクトやコレクションには、前回のコールバックで行われた書込み (write) からの変更が含まれるようになりました。
書込みトランザクション内からより詳細な通知を取得するために手動チェックと書込み処理を実行しようとすると、通知が 2 レベル以上ネストされる場合があります。 手動書込み処理の例としては、 realm.isInWriteTransaction
をチェックし、変更を行う場合は、 realm.commitWrite()
を呼び出してからrealm.beginWrite()
を呼び出します。 ネストされた通知とエラーの可能性によって、この手動操作はエラーが発生しやすくなり、デバッグが困難になります。
書込みブロック内からの詳細な変更情報が必要ない場合は、 writeAsync APIを使用して複雑さを回避できます。 これと同様の非同期書込みを監視すると、書込みトランザクション内で通知が配信される場合でも、通知が提供されます。
let token = dog.observe(keyPaths: [\Dog.age]) { change in guard case let .change(dog, _) = change else { return } dog.realm!.writeAsync { dog.isPuppy = dog.age < 2 } }
ただし、書込みは非同期であるため、通知と書込みが発生するまでの間にRealmが変更される可能性があります。 この場合、通知に渡された変更情報が適用されなくなる可能性があります。
通知に基づいて UITableView を更新
通知経由でUITableView
のみを更新する場合、書き込みトランザクションが発生してから次の通知が受信されるまでの間に、TableView の状態はデータと同期されなくなります。 TableView では保留中の更新がスケジュールされている可能性があり、これにより更新が遅延したり一貫性がなくなったりする可能性があります。
これらの動作には、いくつかの方法で対処できます。
次の例では、この非常に基本的なUITableViewController
を使用します。
class TableViewController: UITableViewController { let realm = try! Realm() let results = try! Realm().objects(DemoObject.self).sorted(byKeyPath: "date") var notificationToken: NotificationToken! override func viewDidLoad() { super.viewDidLoad() tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") notificationToken = results.observe { (changes: RealmCollectionChange) in switch changes { case .initial: self.tableView.reloadData() case .update(_, let deletions, let insertions, let modifications): // Always apply updates in the following order: deletions, insertions, then modifications. // Handling insertions before deletions may result in unexpected behavior. self.tableView.beginUpdates() self.tableView.deleteRows(at: deletions.map { IndexPath(row: $0, section: 0) }, with: .automatic) self.tableView.insertRows(at: insertions.map { IndexPath(row: $0, section: 0) }, with: .automatic) self.tableView.reloadRows(at: modifications.map { IndexPath(row: $0, section: 0) }, with: .automatic) self.tableView.endUpdates() case .error(let err): fatalError("\(err)") } } } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return results.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let object = results[indexPath.row] let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) cell.textLabel?.text = object.title return cell } func delete(at index: Int) throws { try realm.write { realm.delete(results[index]) } } }
通知なしで UITableView を直接更新
通知を待たずにUITableView
を直接更新すると、UI の応答性が最も高くなります。 このコードでは、スレッド間のホストが必要な代わりに、TableView をすぐに更新します。これにより、各更新にわずかな遅延が発生します。 ダウン量は、ビューを頻繁に手動で更新する必要があることです。
func delete(at index: Int) throws { try realm.write(withoutNotifying: [notificationToken]) { realm.delete(results[index]) } tableView.deleteRows(at: [IndexPath(row: index, section: 0)], with: .automatic) }
書込み後に更新を強制する
書き込み後にrefresh()
を強制すると、実行ループのその後の実行ではなく、書き込みからすぐに通知が行われます。 テーブルビューが同期されていない値を読み取るためのウィンドウはありません。
ダウングレードは、書き込み、クエリの再実行、結果の再ソートなど、バックグラウンドで実行することをお勧めする操作がメイン スレッドで実行されることを意味します。 これらの操作の計算コストが高い場合、メイン スレッドで遅延が発生する可能性があります。
func delete(at index: Int) throws { try realm.write { realm.delete(results[index]) } realm.refresh() }
バックグラウンド スレッドでの書込み (write) の実行
バックグラウンド スレッドで書き込みを実行すると、メイン スレッドがブロックされる時間は最小限に抑えられます。 ただし、バックグラウンドで書込み (write) を実行するコードには、Realm のスレッド モデルと Swift DispatchQueue の使用方法に関するより知識が必要です。 メインスレッドでは書込み (write) が行われないため、通知が到達する前にメインスレッドは書込み (write) を認識することはありません。
func delete(at index: Int) throws { func delete(at index: Int) throws { var object = results[index] DispatchQueue.global().async { guard let object = object else { return } let realm = object.realm! try! realm.write { if !object.isInvalidated { realm.delete(object) } } } } }
通知制限の変更
4 レベルよりも深いネストされたドキュメントの変更では、変更通知はtriggerされません。
5 レベル以上の変更をリッスンする必要があるデータ構造がある場合は、次の回避策があります。
スキーマをリファクタリングしてネストを減らします。
ユーザーがデータを手動で更新できるようにするには、「push-to-refresh」のようなものを追加します。
Swift SDK では、キー パス フィルタリングを使用してこの制限を回避することもできます。 この機能は、他の SDK では使用できません。