React para alterações - Swift SDK
Nesta página
- Registrar um ouvinte de alteração de Realm
- Registrar um ouvinte de alteração de coleção
- Registrar um ouvinte de alteração de objeto
- Registrar um ouvinte de alteração de caminho principal
- Realm Collections
- Escreva silenciosamente
- Parar de observar as alterações
- Observação de valor-chave
- Conformidade de observação de valor-chave
- Considerações sobre KVO gerenciado versus não gerenciado
- Observando listas Realm
- React para alterações em um ator diferente
- React para alterações em uma projeção de classe
- Entrega de notificações
- Executar gravações apenas em um thread diferente da thread de observação
- Execute escritas no tópico de observação, fora das notificações
- Executar gravações dentro de notificações
- Atualizar um UITableView com base em notificações
- Alterar limites de notificação
Todos os objetos de Realm são objetos ativos, o que significa que são atualizados automaticamente sempre que são modificados. O Realm emite um evento de notificação sempre que alguma propriedade é alterada. Você pode registrar um manipulador de notificações para ouvir esses eventos de notificação e atualizar sua interface do usuário com os dados mais recentes.
Esta página mostra como registrar manualmente os ouvintes de notificações no Swift. O Atlas Device SDK for Swift oferece wrappers de propriedade SwiftUI para facilitar a atualização automática da interface do usuário quando os dados mudam. Para saber mais sobre como usar os wrappers de propriedade SwiftUI para reagir a alterações, consulte Observar um objeto.
Registrar um ouvinte de alteração de Realm
Você pode registrar um manipulador de notificações em um domínio inteiro. O domínio chama o manipulador de notificações sempre que qualquer transação de escrita envolvendo esse domínio é confirmada. O manipulador não recebe informações sobre a mudança.
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 um ouvinte de alteração de coleção
Você pode registrar um manipulador de notificações em uma coleção dentro de um realm.
O realm notifica seu manipulador:
Depois de primeiro recuperar a coleção.
Sempre que uma transação escrita adicionar, alterar ou remover objetos na coleção.
As notificações descrevem as alterações desde a notificação anterior com três listas de índices: os índices dos objetos que foram excluídos, inseridos e modificados.
Importante
Questões importantes
Em manipuladores de notificações de collection, sempre execute alterações na seguinte ordem: exclusões, inserções e modificações. Manipular inserções antes de exclusões pode resultar em comportamento inesperado.
As notificações de coleção fornecem um parâmetro change
que informa quais objetos foram excluídos, adicionados ou modificados durante a transação de gravação. Esta alteração de coleção derealm soluciona uma array de caminhos de índice que você pode transmitir para os métodos de atualização em lote do UITableView
.
Importante
Atualizações de alta frequência
Este exemplo de ouvinte de alteração de coleção não oferece suporte a atualizações de alta frequência. Sob uma carga de trabalho intensa, esse ouvinte de alteração de coleção pode fazer com que o aplicativo lance uma exceção.
@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 um ouvinte de alteração de objeto
Você pode registrar um manipulador de notificações em um objeto específico dentro de um domínio. O realm notifica seu manipulador:
Quando o objeto é excluído.
Quando qualquer uma das propriedades do objeto é alterada.
O manipulador recebe informações sobre quais campos foram alterados e se o objeto foi excluído.
@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 um ouvinte de alteração de caminho principal
Novidades na versão 10,12,0.
Além de registrar um manipulador de notificações em um objeto ou coleção, você pode passar um parâmetro de string keyPaths
opcional para especificar o caminho da chave ou caminhos da chave para assistir.
Exemplo
// 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" } }
Novidades na versão 10,14,0.
Você pode observar um PartialKeyPath parcialmente apagado em Objetos ou RealmCollections.
objectNotificationToken = dog.observe(keyPaths: [\Dog.favoriteToy, \Dog.age], { change in
Quando você especifica keyPaths
, apenas as alterações nesses keyPaths
bloqueios de notificação de gatilhos. Quaisquer outras alterações não bloqueia notificações de gatilhos.
Exemplo
Considere um objeto Dog
onde uma de suas propriedades é uma lista de siblings
:
class Dog: Object { var name = "" var siblings: List<Dog> var age: Int? }
Se você passar siblings
como um keyPath
para observar, qualquer inserção, exclusão ou modificação na lista siblings
acionará uma notificação. No entanto, uma alteração em someSibling.name
não acionaria uma notificação, a menos que você tenha explicitamente observado ["siblings.name"]
.
Observação
Vários tokens de notificação no mesmo objeto que filtram exclusivamente caminhos de chave separados não filtram. Se uma alteração de caminho-chavefor satisfeita para um token de notificação, todos os bloqueios de token de notificação para esse objeto serão executados.
Realm Collections
Ao observar caminhos-chave nos vários tipos de coleção, espere estes comportamentos:
LinkingObjects:: Observar uma propriedade do LinkingObject dispara uma notificação para uma alteração nessa propriedade, mas não dispara notificações para alterações em suas outras propriedades. Inserções ou exclusões na lista ou no objeto em que a lista está aciona uma notificação.
Listas: A observação de uma propriedade do objeto da lista acionará uma notificação para uma alteração nessa propriedade, mas não acionará notificações para alterações em suas outras propriedades. Inserções ou exclusões na lista ou no objeto em que a lista está no trigger de uma notificação.
Mapa: Observando uma propriedade do objeto do mapa Atlas Triggers uma notificação para uma alteração nessa propriedade, mas não trigger notificações para alterações em suas outras propriedades. Inserções ou exclusões no Mapa ou no objeto em que o mapa está trigger uma notificação. O parâmetro
change
relata, na forma de chaves dentro do mapa, quais pares de chave-valor foram adicionados, removidos ou modificados durante cada transação de escrita.MutableSet: A observação de uma propriedade do objeto de um MutableSet aciona uma notificação para uma alteração nessa propriedade, mas não aciona notificações para alterações em suas outras propriedades. Inserções ou exclusões no MutableSet ou no objeto em que o MutableSet está ativa uma notificação.
Resultados: Observar uma propriedade do Resultado aciona uma notificação para uma alteração nessa propriedade, mas não aciona notificações para alterações em suas outras propriedades. Inserções ou exclusões no resultado aciona uma notificação.
Escreva silenciosamente
Você pode gravar em um domínio sem enviar uma notificação para um observador específico passando o token de notificação do observador em uma array para 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()
Parar de observar as alterações
A observação para quando o token retornado por uma chamada do observe
se torna inválido. Você pode invalidar explicitamente um token ligando para seu método invalidate()
.
Importante
Retenha os tokens pelo tempo que desejar observar
As notificações param se o token estiver em uma variável local fora de escopo.
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()
Observação de valor-chave
Conformidade de observação de valor-chave
Objetos de Realm são compatíveis com observação de valor-chave (KVO) para a maioria das propriedades:
Quase todas as propriedades gerenciadas (não assinadas) em
Object
subclassesA propriedade
invalidated
emObject
eList
Não é possível observar propriedades LinkingObjects
por meio da observação de valor-chave.
Importante
Não é possível adicionar um objeto a um Realm (com realm.add(obj)
ou métodos semelhantes) enquanto ele tiver observadores registrados.
Considerações sobre KVO gerenciado versus não gerenciado
Observar as propriedades de instâncias não gerenciadas de Object
subclasses funciona como qualquer outra propriedade dinâmica.
Observar as propriedades dos objetos gerenciados funciona de forma diferente. Com objetos gerenciados pelo real, o valor de uma propriedade pode mudar quando:
Você atribui a ele
O realm é atualizado manualmente com
realm.refresh()
ou automaticamente em uma thread runloopVocê inicia uma transação de escrita após alterações em outro thread
O realm aplica alterações feitas na(s) transação(ões) escrita(s) em outras threads de uma só vez. Os observadores veem notificações de observação de valor-chave de uma só vez. Etapas intermediárias não acionam notificações KVO.
Exemplo
Digamos que sua aplicação execute uma transação de escrita que incrementa uma propriedade de 1 a 10. No thread principal, você recebe uma única notificação de uma alteração diretamente de 1 para 10. Você não receberá notificações para cada alteração incremental entre 1 e 10.
Evite modificar objetos de real gerenciados a partir de observeValueForKeyPath(_:ofObject:change:context:)
. Os valores da propriedade podem mudar quando não estão numa transação escrita ou como parte do início de uma transação escrita.
Observando listas Realm
Observar as alterações feitas nas propriedades do realm List
é mais simples do que propriedades NSMutableArray
:
Você não precisa marcar
List
propriedades como dinâmicas para observá-las.Você pode chamar métodos de modificação diretamente em
List
. Qualquer pessoa que observe a propriedade que a armazena recebe uma notificação.
Você não precisa usar o mutableArrayValueForKey(_:)
, embora o realm seja compatível com isso para compatibilidade de código.
React para alterações em um ator diferente
Você pode observar notificações sobre um ator diferente. Chamar await object.observe(on: Actor)
ou await collection.observe(on: Actor)
registra um bloqueio a ser chamado sempre que o objeto ou coleção for alterado.
// 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 obter mais informações sobre notificações de alteração em outro ator, consulte Observar notificações em um ator diferente.
React para alterações em uma projeção de classe
Como outros objetos de domínio, você pode reagir às alterações em uma projeção de classe. Ao registrar um ouvinte de alteração de projeção de classe, você verá notificações de alterações feitas diretamente por meio do objeto de projeção de classe. Você também verá notificações de alterações nas propriedades do objeto subjacente que são projetadas por meio do objeto de projeção de classe.
Propriedades no objeto subjacente que não são @Projected
na projeção da classe não geram notificações.
Este bloqueio de notificação é disparado para alterações em:
Person.firstName
propriedade do objetoPerson
subjacente da projeção de classe, mas não alterações emPerson.lastName
ouPerson.friends
.PersonProjection.firstName
, mas não outra projeção de classe que usa a mesma propriedade do objeto subjacente.
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 notificações
A entrega de notificações pode variar dependendo de:
Se a notificação ocorre ou não em uma transação de escrita
Os threads relativos da escrita e da observação
Quando seu aplicativo depende do tempo de entrega da notificação, como quando você usa notificações para atualizar um UITableView
, é importante entender os comportamentos específicos para o contexto do código do aplicativo.
Executar gravações apenas em um thread diferente da thread de observação
A leitura de uma coleção ou objeto observado de dentro de uma notificação de alteração sempre informa com precisão o que foi alterado na coleção passada para a chamada de resposta desde a última vez que a chamada de resposta foi invocada.
A leitura de coleção ou objects fora das change notifications sempre gives exatamente os mesmos valores que you saw na most recent change notification para that object.
A leitura de objetos diferentes do observado dentro de uma notificação de alteração pode ver um valor diferente antes de a notificação dessa alteração ser entregue. O realm refresh
traz todo o realm da “versão antiga” para a “versão mais recente” em uma operação. No entanto, pode ter havido várias notificações de alteração entre a “versão antiga” e a “versão mais recente”. Dentro de uma chamada de resposta, você pode ver alterações com notificações pendentes.
As escritas em threads diferentes por fim se tornam visíveis no thread de observação. Chamando explicitamente refresh()
blocos até que as escritas feitas em outros threads sejam visíveis e as notificações apropriadas tenham sido enviadas. Se você chamar refresh()
em um chamada de resposta de notificação, não haverá operação.
Execute escritas no tópico de observação, fora das notificações
No início da transação escrita, todos os comportamentos acima se aplicam a este contexto. Além disso, você pode esperar sempre ver a versão mais recente dos dados.
Dentro de uma transação de escrita, as únicas alterações que você vê são aquelas que você fez até agora na transação de escrita.
Entre a confirmação de uma transação de gravação e o próximo conjunto de notificações de alteração que está sendo enviado, você pode ver as alterações feitas na transação de gravação, mas nenhuma outra alteração. As gravações feitas em tópicos diferentes não ficam visíveis enquanto você não receber o próximo conjunto de notificações. Executar outra gravação no mesmo thread envia primeiro as notificações da gravação anterior.
Executar gravações dentro de notificações
Quando você executa gravações em notificações, você vê muitos dos mesmos comportamentos acima, com algumas exceções.
Os retornos de chamada invocados antes daquele que executou uma gravação se comportam normalmente. Embora o realm invoque chamada de resposta de alteração em uma ordem estável, essa não é estritamente a ordem em que você adicionou as observações.
Se o início da gravação atualizar o realm, o que pode acontecer se outro thread estiver fazendo gravações, isso aciona notificações recursivas. Essas notificações aninhadas relatam as alterações feitas desde a última chamada à chamada de resposta. Para chamadas de resposta antes de fazer a gravação, isso significa que a notificação interna relata apenas as alterações feitas após as já relatadas na notificação externa. Se a chamada de resposta que faz a gravação tentar gravar novamente na notificação interna, o realm lançará uma exceção. As chamas de resposta após aquela que fez o registro recebem uma única chamada de resposta para ambos os conjuntos de alterações.
Depois que a chamada de resposta concluir a gravação e retornar, o realm não invoca nenhuma das chamas de resposta subsequentes, pois eles não têm mais alterações a serem relatadas. O realm fornece uma notificação mais tarde para a escrita como se a escrita tivesse acontecido fora de uma notificação.
Se o início da escrita não atualizar o Realm, a escrita ocorrerá normalmente. No entanto, o Realm invoca as chamada de resposta subsequentes em um estado inconsistente. Elas continuam a relatar as informações de alteração originais, mas o objeto/collection observado agora inclui as alterações da escrita feitas na chamada de resposta anterior.
Se você tentar realizar verificações manuais e tratamento de gravação para obter notificações mais detalhadas de dentro de uma transação de escrita, poderá receber notificações aninhadas com mais de dois níveis de profundidade. Um exemplo de tratamento manual de gravação é verificar realm.isInWriteTransaction
e, em caso afirmativo, fazer alterações, chamar realm.commitWrite()
e depois realm.beginWrite()
. As notificações aninhadas e o potencial de erro tornam esta manipulação manual propensa a erros e difícil de depurar.
Você pode usar a API writeAsync para contornar a complexidade se não precisar de informações de alteração refinadas de dentro do bloco de gravação. Observar uma gravação assíncrona semelhante a essa fornece notificações mesmo se a notificação for entregue dentro de uma transação de escrita:
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 } }
No entanto, como a escrita é assíncrona, o Realm pode ter mudado entre a notificação e quando a escrita acontece. Nesse caso, as informações de alteração passadas para a notificação podem não ser mais aplicáveis.
Atualizar um UITableView com base em notificações
Se você só atualizar um UITableView
por meio de notificações, no intervalo de tempo entre uma transação de escrita e a chegada da próxima notificação, o estado do TableView estará fora de sincronia com os dados. O TableView pode ter uma atualização pendente agendada, o que pode parecer causar atualizações atrasadas ou inconsistentes.
Você pode abordar esses comportamentos de algumas maneiras.
Os exemplos a seguir usam esse UITableViewController
muito 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]) } } }
Atualize o UITableView diretamente sem uma notificação
Atualizar o UITableView
diretamente sem aguardar uma notificação fornece a UI mais responsiva. Este código atualiza o TableView imediatamente em vez de exigir saltos entre threads, o que causa um pequeno atraso a cada atualização. A desvantagem é que isso exige atualizações manuais frequentes da visualização.
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) }
Forçar uma atualização após uma gravação
Forçar um refresh()
após uma gravação fornece as notificações da gravação imediatamente, e não em uma execução futura do loop de execução. Não há janela para o TableView ler valores fora de sincronização.
A desvantagem é que isso significa que as coisas que recomendamos fazer em segundo plano, como escrever, reexecutar a query e reordenar os resultados, acontecem no thread principal. Quando essas operações são computacionalmente caras, isso pode causar atrasos na thread principal.
func delete(at index: Int) throws { try realm.write { realm.delete(results[index]) } realm.refresh() }
Execute a gravação em uma thread em segundo plano
Executar uma gravação em uma thread em segundo plano bloquear a thread principal pelo menor período de tempo. No entanto, o código para executar uma gravação em segundo plano requer mais familiaridade com o modelo de threading do Realm e com o uso do Swift DispatchQueue. Como a gravação não ocorre na thread principal, a thread principal nunca vê a gravação antes da chegada das notificações.
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) } } } } }
Alterar limites de notificação
Alterações em documentos aninhados com mais de quatro níveis abaixo não acionam notificações de alterações.
Se você tiver uma estrutura de dados em que precise escutar as alterações em cinco níveis para baixo ou mais profundamente, as soluções alternativas incluem:
Refatore o esquema para reduzir o aninhamento.
Adicione algo como "pressione para atualizar" para permitir que os usuários atualizem os dados manualmente.
No Swift SDK, você também pode usar a filtragem de caminho principal para contornar essa limitação. Este recurso não está disponível em outros SDKs.