Todos los objetos de Realm son activos, lo que significa que se actualizan automáticamente al modificarse. Realm emite un evento de notificación cada vez que cambia una propiedad. Puedes registrar un controlador de notificaciones para que detecte estos eventos y actualice tu interfaz de usuario con los datos más recientes.
Esta página muestra cómo registrar manualmente los oyentes de notificaciones en Swift. El SDK de dispositivos Atlas para Swift ofrece contenedores de propiedades SwiftUI para facilitar la actualización automática de la interfaz de usuario cuando cambian los datos. Para obtener más información sobre cómo usar los contenedores de propiedades SwiftUI para reaccionar a los cambios, consulte Observar un objeto.
Registrar un oyente de cambio de reino
Puedes registrar un controlador de notificaciones en todo un dominio. El dominio llama al controlador de notificaciones cada vez que se confirma una transacción de escritura que lo involucra. El controlador no recibe información sobre el cambio.
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()
Registrar un oyente de cambios de colección
Puede registrar un controlador de notificaciones en una colección dentro de un reino.
Realm notifica a su controlador:
Después de recuperar la colección por primera vez.
Siempre que una transacción de escritura agrega, cambia o elimina objetos en la colección.
Las notificaciones describen los cambios desde la notificación anterior con tres listas de índices: los índices de los objetos que se eliminaron, insertaron y modificaron.
Importante
El orden importa
En los controladores de notificaciones de colecciones, aplique siempre los cambios en el siguiente orden: eliminaciones, inserciones y modificaciones. Gestionar las inserciones antes de las eliminaciones puede provocar un comportamiento inesperado.
Las notificaciones de cobro proporcionan una change parámetro que informa qué objetos se eliminan, agregan o modifican durante la transacción de escritura. Este
RealmCollectionChange se resuelve en una matriz de rutas de índice que puede pasar a los UITableView métodos de actualización por lotes de un.
Importante
Actualizaciones de alta frecuencia
Este ejemplo de detector de cambios de colección no admite actualizaciones frecuentes. Bajo una carga de trabajo intensa, este detector de cambios de colección puede provocar que la aplicación genere una excepción.
@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)") } } } }
Registrar un detector de cambios de objetos
Puedes registrar un controlador de notificaciones en un objeto específico dentro de un dominio. El dominio notifica a tu controlador:
Cuando el objeto se borra.
Cuando cambia cualquiera de las propiedades del objeto.
El controlador recibe información sobre qué campos cambiaron y si el objeto fue eliminado.
@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" } }
Registrar un detector de cambios de ruta de clave
Nuevo en la versión 10.12.0.
Además de registrar un controlador de notificaciones en un objeto o colección, puede pasar un keyPaths parámetro de cadena opcional para especificar la ruta de clave o las rutas de clave a observar.
Ejemplo
// 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" } }
Nuevo en la versión 10.14.0.
Puede observar un PartialKeyPath parcialmente borrado de tipoen objetos o colecciones de reinos.
objectNotificationToken = dog.observe(keyPaths: [\Dog.favoriteToy, \Dog.age], { change in
Al keyPaths especificar, solo los cambios en esos keyPaths activan bloques de notificación. Los demás cambios no activan bloques de notificación.
Ejemplo
Considere un objeto Dog donde una de sus propiedades es una lista de siblings:
class Dog: Object { var name = "" var siblings: List<Dog> var age: Int? }
Si pasa siblings como keyPath para observar, cualquier inserción, eliminación o modificación en la lista siblings activará una notificación. Sin embargo, un cambio en someSibling.name no activará una notificación, a menos que observe explícitamente ["siblings.name"].
Nota
Varios tokens de notificación en el mismo objeto que filtran por rutas de clave independientes no filtran de forma exclusiva. Si se cumple un cambio de ruta de clave para un token de notificación, se ejecutarán todos los bloques de tokens de notificación de ese objeto.
Colecciones de reinos
Cuando observe las rutas clave en los distintos tipos de colecciones, espere estos comportamientos:
ObjetosDeEnlace:: Al observar una propiedad del ObjetoDeEnlace, se activa una notificación para cualquier cambio en dicha propiedad, pero no para cualquier cambio en sus otras propiedades. Las inserciones o eliminaciones en la lista o en el objeto que la contiene activan una notificación.
Listas: La observación de una propiedad del objeto de la lista activará una notificación por un cambio en esa propiedad, pero no activará notificaciones por cambios en sus otras propiedades. Las inserciones o eliminaciones en la lista o en el objeto donde se encuentra la lista activan una notificación.
Mapa: Observar una propiedad del objeto del mapa activa una notificación para cualquier cambio en dicha propiedad, pero no para cualquier cambio en las demás propiedades. Las inserciones o eliminaciones en el mapa o en el objeto donde se encuentra activan una notificación. El
changeparámetro indica, en forma de claves dentro del mapa, qué pares clave-valor se añaden, eliminan o modifican durante cada transacción de escritura.MutableSet: Observar una propiedad de un objeto de un MutableSet activa una notificación para un cambio en dicha propiedad, pero no para cambios en sus otras propiedades. Las inserciones o eliminaciones en el MutableSet o en el objeto en el que se encuentra activan una notificación.
Resultados: Al observar una propiedad del Resultado, se activa una notificación para cualquier cambio en dicha propiedad, pero no para cualquier cambio en sus otras propiedades. Las inserciones o eliminaciones en el Resultado sí activan una notificación.
Escribe en silencio
Puede escribir en un reino sin enviar una notificación a un observador específico pasando el token de notificación del observador en una matriz a realm.write(withoutNotifying:_:):
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()
Deja de esperar los cambios
La observación se detiene cuando el token devuelto por una llamada observe deja de ser válido. Puedes invalidar un token explícitamente llamando a su método invalidate().
Importante
Conserve los tokens durante el tiempo que desee observar
Las notificaciones se detienen si el token está en una variable local que sale del ámbito.
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()
Observación de clave-valor
Cumplimiento de la observación de valores clave
Los objetos de reino cumplen con la observación de clave-valor (KVO) para la mayoría de las propiedades:
Casi todas las propiedades administradas (no ignoradas) en
ObjectsubclasesLa propiedad
invalidatedenObjectyList
No se pueden observar propiedades LinkingObjects a través de la observación de clave-valor.
Importante
No se puede agregar un objeto a un reino (con realm.add(obj) o métodos similares) mientras tenga observadores registrados.
Consideraciones sobre KVO administrado y no administrado
La observación de las propiedades de instancias no administradas de subclases Object funciona como cualquier otra propiedad dinámica.
La observación de las propiedades de los objetos administrados funciona de forma diferente. Con los objetos administrados por dominio, el valor de una propiedad puede cambiar cuando:
Tú le asignas
El realm se actualiza, ya sea manualmente con
realm.refresh()o automáticamente en un hilo de runloop.Comienza una transacción de escritura después de los cambios en otro hilo
Realm aplica los cambios realizados en las transacciones de escritura en otros subprocesos simultáneamente. Los observadores ven las notificaciones de observación de clave-valor inmediatamente. Los pasos intermedios no activan notificaciones de KVO.
Ejemplo
Supongamos que tu aplicación realiza una transacción de escritura que incrementa una propiedad de 1 a 10. En el hilo principal, recibes una única notificación de un cambio directamente de 1 a 10. No recibirás notificaciones por cada cambio incremental entre 1 y 10.
Evitar modificar objetos Realm gestionados desde observeValueForKeyPath(_:ofObject:change:context:). Los valores de propiedad pueden cambiar cuando no se está en una transacción de escritura, o como parte de comenzar una transacción de escritura.
Observando listas de reinos
Observar los cambios realizados en las propiedades de Realm List es más sencillo que en las propiedades NSMutableArray:
No es necesario marcar
Listpropiedades como dinámicas para observarlas.Puedes llamar directamente a los métodos de modificación en
List. Cualquiera que observe la propiedad que lo almacena recibe una notificación.
No necesitas usar mutableArrayValueForKey(_:), aunque Realm sí permite esto para la compatibilidad de código.
Tip
Ejemplos de uso de Realm con ReactiveCocoa de Objective-C y ReactKit de Swift.
React a los cambios en un actor diferente
Puedes observar las notificaciones de un actor diferente. Al llamar a await object.observe(on: Actor) o await collection.observe(on: Actor), se registra un bloque que se llamará cada vez que cambie el objeto o la colección.
// 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() }
Para obtener más información sobre las notificaciones de cambio en otro actor, consulte Observar notificaciones en un actor diferente.
Reaccionar a los cambios en una proyección de clase
Al igual que otros objetos de dominio, puedes reaccionar a los cambios en una proyección de clase. Al registrar un detector de cambios de proyección de clase, verás notificaciones de los cambios realizados directamente a través del objeto de proyección de clase. También verás notificaciones de los cambios en las propiedades del objeto subyacente que se proyectan a través del objeto de proyección de clase.
Las propiedades del objeto subyacente que no son @Projected en la proyección de clase no generan notificaciones.
Este bloque de notificación se activa cuando se producen cambios en:
Person.firstNamepropiedad del objetoPersonsubyacente de la proyección de clase, pero no cambia aPerson.lastNameoPerson.friends.PersonProjection.firstNamepropiedad, pero no otra proyección de clase que use la misma propiedad del objeto subyacente.
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" }
Entrega de notificaciones
La entrega de notificaciones puede variar dependiendo de:
Si la notificación ocurre o no dentro de una transacción de escritura
Los hilos relativos de la guardar y la observación
Cuando su aplicación depende del tiempo de entrega de notificaciones, como cuando usa notificaciones para actualizar un UITableView, es importante comprender los comportamientos específicos para el contexto del código de su aplicación.
Realizar escrituras solo en un hilo diferente al hilo de observación
La lectura de una colección o un objeto observado desde dentro de una notificación de cambio siempre le indica con precisión qué ha cambiado en la colección pasada a la devolución de llamada desde la última vez que se invocó la devolución de llamada.
La lectura de colecciones u objetos fuera de las notificaciones de cambio siempre le brinda exactamente los mismos valores que vio en la notificación de cambio más reciente para ese objeto.
Al leer objetos distintos del observado dentro de una notificación de cambio, se podría ver un valor distinto antes de que se hubiera entregado la notificación de ese cambio. Realm refresh trae todo el realm desde la 'antigua versión' a la 'última versión' en una sola operación. Sin embargo, puede que se hayan disparado varias notificaciones de cambio entre la 'versión antigua' y la 'versión más reciente'. Dentro de una función de retorno, puedes ver cambios que tengan notificaciones pendientes.
Las escrituras en diferentes hilos se hacen visibles en el hilo observador. Llamar explícitamente a refresh() bloquea el proceso hasta que las escrituras realizadas en otros hilos sean visibles y se hayan enviado las notificaciones correspondientes. Si se llama a refresh() dentro de una devolución de llamada de notificación, la operación no es válida.
Realizar escrituras en el hilo de observación, fuera de las notificaciones
Al inicio de la transacción de escritura, todos los comportamientos anteriores se aplican a este contexto. Además, siempre puedes esperar ver la versión más reciente de los datos.
Dentro de una transacción de escritura, los únicos cambios que verá son aquellos que haya realizado hasta el momento dentro de la transacción de escritura.
Entre la confirmación de una transacción de escritura y el envío del siguiente conjunto de notificaciones de cambio, puede ver los cambios realizados en la transacción de escritura, pero ningún otro cambio. Las escrituras realizadas en diferentes subprocesos no son visibles hasta que recibe el siguiente conjunto de notificaciones. Al realizar otra escritura en el mismo subproceso, se envían primero las notificaciones de la escritura anterior.
Realizar escrituras dentro de las notificaciones
Cuando realiza escrituras dentro de las notificaciones, verá muchos de los mismos comportamientos mencionados anteriormente, con algunas excepciones.
Las devoluciones de llamada invocadas antes de la que realizó la escritura se comportan con normalidad. Si bien Realm invoca las devoluciones de llamada de cambio en un orden estable, este no es estrictamente el orden en el que se agregaron las observaciones.
Si al iniciar la escritura se actualiza el dominio, lo cual puede ocurrir si otro hilo está escribiendo, se activan notificaciones recursivas. Estas notificaciones anidadas informan de los cambios realizados desde la última llamada a la devolución de llamada. Para las devoluciones de llamada anteriores a la que realiza la escritura, esto significa que la notificación interna solo informa de los cambios realizados después de los ya informados en la notificación externa. Si la devolución de llamada que realiza la escritura intenta escribir de nuevo en la notificación interna, Realm lanza una excepción. Las devoluciones de llamada posteriores a la que realiza la escritura reciben una sola notificación para ambos conjuntos de cambios.
Una vez que la devolución de llamada completa la escritura y retorna, Realm no invoca ninguna de las devoluciones de llamada posteriores, ya que ya no tienen cambios que reportar. Realm proporciona una notificación posterior de la escritura, como si esta se hubiera producido fuera de ella.
Si al iniciar la escritura no se actualiza el dominio, esta se realiza de forma normal. Sin embargo, Realm invoca las devoluciones de llamada subsiguientes de forma inconsistente. Estas siguen reportando la información del cambio original, pero el objeto/colección observado ahora incluye los cambios de la escritura realizada en la devolución de llamada anterior.
Si intenta realizar comprobaciones y gestión de escritura manuales para obtener notificaciones más detalladas dentro de una transacción de escritura, puede obtener notificaciones anidadas a más de dos niveles de profundidad. Un ejemplo de gestión de escritura manual es comprobar realm.isInWriteTransaction y, en caso afirmativo, realizar cambios, llamando a realm.commitWrite() y luego a realm.beginWrite(). Las notificaciones anidadas y la posibilidad de errores hacen que esta manipulación manual sea propensa a errores y difícil de depurar.
Puedes usar la API writeAsync para evitar la complejidad si no necesitas información detallada sobre los cambios dentro de tu bloque de escritura. Observar una escritura asíncrona similar a esta proporciona notificaciones incluso si la notificación se entrega dentro de una transacción de escritura:
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 } }
Sin embargo, dado que la escritura es asíncrona, es posible que el dominio haya cambiado entre la notificación y el momento de la escritura. En este caso, es posible que la información de cambio enviada a la notificación ya no sea aplicable.
Actualización de una UITableView según notificaciones
Si solo actualiza un UITableView mediante notificaciones, entre una transacción de escritura y la siguiente notificación, el estado de TableView no está sincronizado con los datos. TableView podría tener una actualización pendiente programada, lo que puede causar actualizaciones retrasadas o inconsistentes.
Puedes abordar estos comportamientos de varias maneras.
Los siguientes ejemplos utilizan este UITableViewController muy básico.
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]) } } }
Actualice UITableView directamente sin una notificación
Actualizar UITableView directamente sin esperar una notificación proporciona la interfaz de usuario más ágil. Este código actualiza TableView inmediatamente en lugar de requerir saltos entre subprocesos, lo que añade un pequeño retraso a cada actualización. La desventaja es que requiere actualizaciones manuales frecuentes de la vista.
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) }
Forzar una actualización después de una escritura
Forzar un refresh() después de una escritura proporciona las notificaciones de la escritura inmediatamente, en lugar de en una ejecución posterior del bucle de ejecución. No hay ventana para que TableView lea valores desincronizados.
La desventaja es que esto significa que las tareas que recomendamos realizar en segundo plano, como escribir, volver a ejecutar la consulta y reordenar los resultados, se realizan en el hilo principal. Cuando estas operaciones requieren un alto consumo computacional, pueden causar retrasos en el hilo principal.
func delete(at index: Int) throws { try realm.write { realm.delete(results[index]) } realm.refresh() }
Realizar la escritura en un hilo en segundo plano
Realizar una escritura en un hilo en segundo plano bloquea el hilo principal durante el menor tiempo posible. Sin embargo, el código para realizar una escritura en segundo plano requiere mayor familiaridad con el modelo de subprocesos de Realm y el uso de Swift DispatchQueue. Dado que la escritura no ocurre en el hilo principal, este nunca la ve antes de que lleguen las notificaciones.
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) } } } } }
Límites de notificación de cambios
Los cambios en documentos anidados a más de cuatro niveles de profundidad no activan notificaciones de cambio.
Si tiene una estructura de datos en la que necesita escuchar cambios cinco niveles más abajo o más profundos, las soluciones alternativas incluyen:
Refactorice el esquema para reducir la anidación.
Agregue algo como "push-to-refresh" para permitir que los usuarios actualicen los datos manualmente.
En el SDK de Swift, también se puede usar el filtrado de rutas de claves para solucionar esta limitación. Esta función no está disponible en los demás SDK.