I'm using RxCocoa and RxSwift to render a UITableView
against an array provided by a BehaviorRelay
. The code to bind the data is below:
// MyViewModel:
var itemList = BehaviorRelay(value: [MyItem]())
...
func loadData() {
var items = [...] // load items
itemList.accept(items)
}
// ==========
// MyView:
var viewModel = MyViewModel()
func bindData() {
viewModel.itemList.bind(to: tableView.rx.items) { table, index, item in
if item.type == ... {
... // pick cell type based on the item metadata
} else {
let cell = table.dequeueReusableCell(withIdentifier: "MyItemCell") as? MyItemCell {
cell.bindItem(item)
return cell
}
return new UITableViewCell()
}).disposed(by: viewModel.disposeBag)
}
// ==========
In most cases it works just fine but in some rare cases which I cannot reproduce the app crashes with the following stacktrace, reported by a few Firebase uses:
Crashed: com.apple.main-thread
0 libswiftCore.dylib 0x37d7c _assertionFailure(_:_:file:line:flags:) + 312
1 mycoolapp 0x8aa968 @objc TableViewDataSourceNotSet.tableView(_:cellForRowAt:) + 84 (RxCocoa.swift:84)
2 mycoolapp 0x8aae30 @objc RxTableViewDataSourceProxy.tableView(_:cellForRowAt:) + 64 (RxTableViewDataSourceProxy.swift:64)
3 UIKitCore 0x14b75c -[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:] + 808
4 UIKitCore 0x219c9c -[UITableView _createPreparedCellForRowAtIndexPath:willDisplay:] + 68
5 UIKitCore 0x219884 -[UITableView _heightForRowAtIndexPath:] + 124
6 UIKitCore 0x219720 -[UISectionRowData heightForRow:inSection:canGuess:] + 176
7 UIKitCore 0x44c328 -[UITableViewRowData heightForRow:inSection:canGuess:adjustForReorderedRow:] + 228
8 UIKitCore 0x5952c -[UITableViewRowData rectForRow:inSection:heightCanBeGuessed:] + 304
9 UIKitCore 0x44b8b0 -[UITableViewRowData rectForGlobalRow:heightCanBeGuessed:] + 112
10 UIKitCore 0x14b378 -[UITableView _prefetchCellAtGlobalRow:aboveVisibleRange:] + 240
11 UIKitCore 0x14b264 __48-[UITableView _configureCellPrefetchingHandlers]_block_invoke + 52
12 UIKitCore 0x2186a8 -[_UITableViewPrefetchContext updateVisibleIndexRange:withContentOffset:] + 2100
13 UIKitCore 0x81be8 -[UITableView _updateCycleIdleUntil:] + 168
14 UIKitCore 0x811ac ___UIUpdateCycleNotifyIdle_block_invoke + 612
15 libdispatch.dylib 0x2460 _dispatch_call_block_and_release + 32
16 libdispatch.dylib 0x3f88 _dispatch_client_callout + 20
17 libdispatch.dylib 0x127f4 _dispatch_main_queue_drain + 928
18 libdispatch.dylib 0x12444 _dispatch_main_queue_callback_4CF + 44
19 CoreFoundation 0x9a6c8 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 16
20 CoreFoundation 0x7c02c __CFRunLoopRun + 2036
21 CoreFoundation 0x80eb0 CFRunLoopRunSpecific + 612
22 GraphicsServices 0x1368 GSEventRunModal + 164
23 UIKitCore 0x3a1668 -[UIApplication _run] + 888
24 UIKitCore 0x3a12cc UIApplicationMain + 340
25 mycoolapp. 0x1d8564 main + 23 (AppDelegate.swift:23)
26 ??? 0x1b3d9c960 (Missing)
The crash is hard to reproduce (I wasn't able to) but it's affects multiple users. The bindData
method is called once at viewDidLoad
, and I call loadData
on every page appearance (willAppear, return back from another page or app activation deactivation) but it's not clear why it crashes and how to reproduce/fix it.
The crash is coming from TableViewDataSourceNotSet
which seems to be set by the RxCocoa/RxSwift framework and crashes when cell for an item is requested. I'm not setting that custom dataSource explicitly anywhere. Any ideas how I can prevent this from happening?