Rxswift: Crashing with "Datasource is not set" error in UICollectionViewController

Created on 27 Mar 2017  路  10Comments  路  Source: ReactiveX/RxSwift

Short description of the issue:

App crashes with "Datasource is not set" error in UICollectionViewController after it is de-initialized by popping to the previous viewController.

This happens with RxSwift 3.1.0 or above. It works fine with RxSwift 3.0.1

Expected outcome:

It does not crash after popping back to the previous viewController.

What actually happens:

it crashes with "Datasource is not set" error.

Solution:

907 and #1081 are related a little to this issue. Method for refreshing datasource when a number of observers changed was added to DatasourceProxies because UITableView and UICollectionView cache selectors once delegate or dataSource is set. But in some cases, dataSource is set nil at unexpected time and causing crashes. Error logs are below.

2017-03-17 19:21:30.754 Atte[46311:344112] *** Assertion failure in -[UICollectionView _createPreparedSupplementaryViewForElementOfKind:atIndexPath:withLayoutAttributes:applyAttributes:], /SourceCache/UIKit_Sim/UIKit-3347.44.2/UICollectionView.m:1400
2017-03-17 19:21:30.776 Atte[46311:344112] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView dataSource is not set'
*** First throw call stack:
(
    0   CoreFoundation                      0x043a2746 __exceptionPreprocess + 182
    1   libobjc.A.dylib                     0x0386ca97 objc_exception_throw + 44
    2   CoreFoundation                      0x043a25da +[NSException raise:format:arguments:] + 138
    3   Foundation                          0x034d9720 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 118
    4   UIKit                               0x05a8e06d -[UICollectionView _createPreparedSupplementaryViewForElementOfKind:atIndexPath:withLayoutAttributes:applyAttributes:] + 148
    5   UIKit                               0x05a8fd16 -[UICollectionView _updateVisibleCellsNow:] + 4947
    6   UIKit                               0x05a94561 -[UICollectionView layoutSubviews] + 281
    7   UIKit                               0x0541352a -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 668
    8   libobjc.A.dylib                     0x03882771 -[NSObject performSelector:withObject:] + 70
    9   QuartzCore                          0x04fb2e47 -[CALayer layoutSublayers] + 144
    10  QuartzCore                          0x04fa6925 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 403
    11  QuartzCore                          0x04fb52de -[CALayer(CALayerPrivate) layoutBelowIfNeeded] + 44
    12  UIKit                               0x05404f08 -[UIView(Hierarchy) layoutBelowIfNeeded] + 738
    13  UIKit                               0x05404c11 -[UIView(Hierarchy) layoutIfNeeded] + 83
    14  RxCocoa                             0x02e411a2 _TFFE7RxCocoaP7RxSwift14ObservableType24subscribeProxyDataSourceuRd__S_17DelegateProxyTyperFT8ofObjectCSo6UIView10dataSourcePs9AnyObject_16retainDataSourceSb7bindingFTqd__GOS0_5Eventwx1E__T__PS0_10Disposable_U1_FT_T_ + 146
    15  RxCocoa                             0x02e45778 _TPA__TFFE7RxCocoaP7RxSwift14ObservableType24subscribeProxyDataSourceuRd__S_17DelegateProxyTyperFT8ofObjectCSo6UIView10dataSourcePs9AnyObject_16retainDataSourceSb7bindingFTqd__GOS0_5Eventwx1E__T__PS0_10Disposable_U1_FT_T_ + 72
    16  RxSwift                             0x029c9ee1 _TTRXFo___XFo_iT__iT__ + 17
    17  RxSwift                             0x029ea64c _TPA__TTRXFo___XFo_iT__iT__ + 60
    18  RxSwift                             0x029ea6ed _TFC7RxSwiftP33_AB3B9E8806A71B46FB498A7594F5E0D919AnonymousDisposable7disposefT_T_ + 141
    19  RxSwift                             0x0295621a _TTWC7RxSwiftP33_AB3B9E8806A71B46FB498A7594F5E0D919AnonymousDisposableS_10DisposableS_FS1_7disposefT_T_ + 26
    20  RxSwift                             0x029e4122 _TFC7RxSwift10DisposeBagD + 258
    21  Atte                                0x005ff1ec _TToFC4Atte27ItemOfferListViewControllerE + 460
    22  libobjc.A.dylib                     0x0386b44c _ZL27object_cxxDestructFromClassP11objc_objectP10objc_class + 116
    23  libobjc.A.dylib                     0x0386b3d3 object_cxxDestruct + 20
    24  libobjc.A.dylib                     0x03878f38 objc_destructInstance + 48
    25  libobjc.A.dylib                     0x03878f69 object_dispose + 20
    26  UIKit                               0x05544b5a -[UIResponder dealloc] + 106
    27  UIKit                               0x054e2ac7 -[UIViewController dealloc] + 2270
    28  UIKit                               0x05abb963 -[UICollectionViewController dealloc] + 220
    29  UIKit                               0x054deadb -[UIViewController release] + 89
    30  CoreFoundation                      0x04260d17 CFRelease + 743
    31  CoreFoundation                      0x04281dff -[__NSArrayI dealloc] + 79
    32  libobjc.A.dylib                     0x03881772 _ZN11objc_object17sidetable_releaseEb + 248
    33  libobjc.A.dylib                     0x03880e9b objc_release + 43
    34  libobjc.A.dylib                     0x03881d32 _ZN12_GLOBAL__N_119AutoreleasePoolPage3popEPv + 586
    35  CoreFoundation                      0x04282758 _CFAutoreleasePoolPop + 24
    36  CoreFoundation                      0x042b95fe __CFRunLoopRun + 2270
    37  CoreFoundation                      0x042b8a5b CFRunLoopRunSpecific + 443
    38  CoreFoundation                      0x042b888b CFRunLoopRunInMode + 123
    39  GraphicsServices                    0x0a4052c9 GSEventRunModal + 192
    40  GraphicsServices                    0x0a405106 GSEventRun + 104
    41  UIKit                               0x053800b6 UIApplicationMain + 1526
    42  Atte                                0x00e5fcc1 main + 145
    43  libdyld.dylib                       0x07c15ac9 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

I found it was caused in this method and changing this method to this solved this issue for me.

I would like to open a PR if it is a right solution.

RxSwift/RxCocoa/RxBlocking/RxTest version/commit

_version or commit here_ RxSwift 3.1.0 to 3.3.1.

Platform/Environment

  • iOS

Xcode version:

8.2.1

Installation method:

  • Carthage

I have multiple versions of Xcode installed:

  • no

Level of RxSwift knowledge:

  • I have a significant code base

Most helpful comment

Hi @yuzushioh ,

I've made some changes regarding the disposal logic and added unit tests. Could you please recheck does master branch work for you correctly?

@ishkawa could you also maybe give it a test run? I know you've reported some issues with the UITableView.

All 10 comments

Hi @yuzushioh ,

I've added that self.collectionView?.dataSource = nil because I've had similar issues with UITableView in prod.

It would cache numberOfRows from original data source and when deinit would switch to TableViewDataSourceNotSet, I would get a call to func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell -> crash.

The way I've solved that was to set the entire data source to nil, that seemed to clear internal cache for UITableView. Another way to clear it was to call, layoutIfNeeded on it.

This is how Dispose method for subscribeProxyDataSource proxy looks like.

                 return Disposables.create { [weak object] in
                    subscription.dispose()
                    unregisterDelegate.dispose()
                    object?.layoutIfNeeded()
                }

Could you please try with the following code?

                 return Disposables.create { [weak object] in
                    subscription.dispose()
                    object?.layoutIfNeeded() // trying to flush the cache before delegate is cleared
                    unregisterDelegate.dispose()   
                }

If would be also great if you could provide some repro project.

Hi @kzaher

Thank you for explaining about the background! I tried in your suggested way and it seems to work fine馃憤馃徎 I will find some time and make a project for this issue. should I make a PR?

@yuzushioh yep 馃憤

Awesome find! This has been a consistent issue for me on anything less than iOS10 for a while too. Thumbs up!

Hi @yuzushioh ,

I've merged your PR. Could you please add a unit test for that also?

You can observe layoutIfNeeded and setDelegate methods and write a test that makes sure layoutIfNeeded is called first.

Thanks for merging! will do馃憤馃徎

Hi @yuzushioh ,

I've made some changes regarding the disposal logic and added unit tests. Could you please recheck does master branch work for you correctly?

@ishkawa could you also maybe give it a test run? I know you've reported some issues with the UITableView.

Hi @kzaher I checked and master branch works just great! Thanks for the unit tests馃憤

I'm still seeing this issue with the 3.6.1 release. Clearing the data source and delegate before the deinit of view controllers resolves this for now.

@sureshven I'm facing the same issue

Was this page helpful?
0 / 5 - 0 ratings

Related issues

RobinFalko picture RobinFalko  路  3Comments

gaudecker picture gaudecker  路  3Comments

angerman picture angerman  路  3Comments

delebedev picture delebedev  路  3Comments

hannesstruss picture hannesstruss  路  3Comments