Skip to content

[bug] Potential crash in UITableView when collection changes observed synchronously  #113

@abeintopalo

Description

@abeintopalo

Let's take the sample code from the readme:

let realm = try! Realm()
let laps = realm.objects(Lap.self)

Observable.changeset(from: laps)
  .subscribe(onNext: { results, changes in
    if let changes = changes {
      // it's an update
      print(results)
      print("deleted: \(changes.deleted)")
      print("inserted: \(changes.inserted)")
      print("updated: \(changes.updated)")
    } else {
      // it's the initial data
      print(results)
    }
  })

The above code sets synchronousStart to true by default.

Here is the implementation of Observable.changeset(from: synchronousStart):

public static func changeset(from collection: E, synchronousStart: Bool = true)
        -> Observable<(AnyRealmCollection<E.ElementType>, RealmChangeset?)> {

        return Observable.create { observer in
            if synchronousStart {
                observer.onNext((collection.toAnyCollection(), nil))
            }

            let token = collection.toAnyCollection().observe { changeset in

                switch changeset {
                    case .initial(let value):
                        guard !synchronousStart else { return }
                        observer.onNext((value, nil))
                    case .update(let value, let deletes, let inserts, let updates):
                        observer.onNext((value, RealmChangeset(deleted: deletes, inserted: inserts, updated: updates)))
                    case .error(let error):
                        observer.onError(error)
                        return
                }
            }

            return Disposables.create {
                token.invalidate()
            }
        }
    }

If the database is modified continuously from a background thread it can happen that the database changes in between invoking code observer.onNext((collection.toAnyCollection(), nil)) and guard !synchronousStart else { return }. As a result one change is swallowed and the next change that is delivered by observer.onNext((value, RealmChangeset(deleted: deletes, inserted: inserts, updated: updates))) will be incompatible with the previously saved state of the collection in the view model.
Because of the lost/swallowed change of the collection the incremental update of UITableView will crash the app if the change to be applied is not valid. (Invalid update, etc)
The current workaround for me is to set _ synchronousStart_ to false.

So, basically synchronous start with the current implementation does not work in some situation.

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions