RxRealm icon indicating copy to clipboard operation
RxRealm copied to clipboard

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

Open abeintopalo opened this issue 7 years ago • 2 comments

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.

abeintopalo avatar Sep 25 '18 10:09 abeintopalo

Similar with - https://github.com/RxSwiftCommunity/RxRealm/issues/74 Can reproduce it

M0rtyMerr avatar Jun 05 '20 16:06 M0rtyMerr

Anything new on this? Any workaround?

tristangrichard avatar Nov 24 '20 10:11 tristangrichard