Menu Docs
Página inicial do Docs
/ /
Atlas Device SDKs
/

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.

Você pode registrar um manipulador de notificações em um realm inteiro. O realm chama o manipulador de notificações sempre que qualquer transação de escrita envolvendo esse realm é 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()

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 escrita. 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)")
}
}
}
}

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 {
@Persisted 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"
}
}

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 {
@Persisted var name = ""
@Persisted var favoriteToy = ""
@Persisted 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 {
@Persisted var name = ""
@Persisted var siblings: List<Dog>
@Persisted 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.

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.

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

Dica

Veja também:

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

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 subclasses

  • A propriedade invalidated em Object e List

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.

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 runloop

  • Você 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.

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.

Dica

Veja também:

Exemplos de uso do Realm com o ReactiveCocoa da Objective-C, e ReactKit da Swift.

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
@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.

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 objeto Person subjacente da projeção de classe, mas não alterações em Person.lastName ou Person.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"
}

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.

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.

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.

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.

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

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

Executar uma gravação em um thread em segundo plano bloquear o 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 {
@ThreadSafe 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)
}
}
}
}
}

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.

Voltar

Filtre dados