Thank you for noticing this mistake. You are correct than no changing the number of checked items doesn’t produce updated statistics when you add a new TODO.
The change you suggest is a good solution. The reason for zip
not working is an optimization provided by Realm: when you filter
a Realm collection, you are not merely using the Swift filter
operator. You are using Realm’s version of filter
which applies a predicate to the changes then emits appropriate changesets.
Inserting a new item doesn’t change the number of checked items, which is what the filter is looking at. Therefore, and since you don’t get a change, the zip
operator patiently waits for the todoTasks
sequence to emit something, which happens only when an items is checked or unchecked (due to the aforementioned optimization).
Using combineLatest
is a correct way to fix this problem. At least one of the sequences will emit on item insert / delete (the tasks
sequence) and trigger the production of a statistics update:
let tasks = realm.objects(TaskItem.self)
let todoTasks = tasks.filter("checked != nil")
return .combineLatest(
Observable.collection(from: tasks)
.map { $0.count },
Observable.collection(from: todoTasks)
.map { $0.count }) { all, done in
(todo: all - done, done: done)
}
Keeping zip
is possible, if you base both sequences off the tasks
sequence then further filter the results after tasks
emits something:
let tasks = realm.objects(TaskItem.self)
return .zip(
Observable.collection(from: tasks)
.map { $0.count },
Observable.collection(from: tasks)
.map { $0.filter("checked != nil").count }) { all, done in
(todo: all - done, done: done)
}
The combineLatest
solution is possible slightly more efficient since the other one has to further recreate filtered results, possibly eliminating internal optimizations that Realm may provide.
Congratulations on taking up the challenge so seriously as to beat the authors to their own challenges!