iosuicollectionviewuicollectionviewcellrx-swiftrxdatasources

How do I properly return a CollectionViewCell with reuseIdentifier if weak self is nil in RxDataSource function?


I have an issue where [unowned self] was changed to [weak self] within the dataSource function used for a CollectionView using RxDataSource due to a memory leak. I now received a crash from returning a blank collectionViewCell that doesn't have a reuseIdentifier. I understand that I need to return a cell with a reuseID.

What changes are suggested to deal with this properly?

Someone suggested making collectionView.dataSource = nil in viewDidLoad() would fix this...

I was thinking instead of returning CanvasItemCollectionViewCell() in the 'guard' check, I return collectionView.dequeueReusableCell(for: indexPath, cellType: CanvasItemCollectionViewCell.self), but if self = self fails wouldn't that mean the collectionView is garbage?

This is a difficult problem to debug because this crash doesn't happen consistently.

Here are some screenshots to portray what I am looking at.

RxDataSource code:

func dataSource()
        -> RxCollectionViewSectionedAnimatedDataSource<CanvasSectionModel> {
        RxCollectionViewSectionedAnimatedDataSource<CanvasSectionModel>(
            animationConfiguration: AnimationConfiguration(
                insertAnimation: .fade,
                reloadAnimation: .fade,
                deleteAnimation: .fade
            ),
            configureCell: { [weak self] dataSource, collectionView, indexPath, _ in
                guard let self = self else { return CanvasItemCollectionViewCell() }
                
                switch dataSource[indexPath] {
                case let .CellModel(model):
                    let cell = collectionView
                        .dequeueReusableCell(
                            for: indexPath,
                            cellType: CanvasItemCollectionViewCell.self
                        )

                    cell.model = model

                    cell.onDeleteHandler = { _ in
                        self.presentDeleteConfirmation { deleteConfirmed in
                            guard deleteConfirmed else { return }
                            self.viewModel.inputs.deletePage(withProofID: model.id)
                        }
                    }

                    return cell
                }
            }

Crash:

enter image description here


Solution

  • Ultimately, the problem is here:

    cell.onDeleteHandler = { _ in
        self.presentDeleteConfirmation { deleteConfirmed in
            guard deleteConfirmed else { return }
            self.viewModel.inputs.deletePage(withProofID: model.id)
        }
    }
    

    Don't use self and you won't have a problem with self references so you won't need to import a weak self and then worry about the guard let self...

    extension UIViewController {
        static func top() -> UIViewController {
            guard let rootViewController = UIApplication.shared.delegate?.window??.rootViewController else { fatalError("No view controller present in app?") }
            var result = rootViewController
            while let vc = result.presentedViewController, !vc.isBeingDismissed {
                result = vc
            }
            return result
        }
    }