1

I have implemented such UICollectionView wrapper

struct CollectionView: UIViewControllerRepresentable {

    // MARK: - Properties
    let layout: UICollectionViewLayout
    let sections: [Section]
    let items: [Section: [Item]]

    // MARK: - Actions
    let content: (_ indexPath: IndexPath, _ item: Item) -> AnyView

    init(layout: UICollectionViewLayout,
         sections: [Section],
         items: [Section: [Item]],
         @ViewBuilder content:  @escaping (_ indexPath: IndexPath, _ item: Item) -> AnyView) {
        self.layout = layout

        self.sections = sections
        self.items = items

        self.content = content
    }

    func makeUIViewController(context: Context) -> CollectionViewController {

        let controller = CollectionViewController()
        controller.layout = layout
        controller.content = content
        controller.snapshot = snapshotForCurrentState()

        controller.collectionView.delegate = context.coordinator

        return controller
    }

    func updateUIViewController(_ controller: CollectionViewController, context: Context) {

        controller.snapshot = snapshotForCurrentState()
        controller.reloadDataSource()
    }


    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
}

And here is code of CollectionViewController

class CollectionViewController: UIViewController {

    var layout: UICollectionViewLayout! = nil
    var snapshot: NSDiffableDataSourceSnapshot<Section, Item>! = nil
    var content: ((_ indexPath: IndexPath, _ item: Item) -> AnyView)! = nil

    let queue = DispatchQueue(label: "diffQueue")

    lazy var dataSource: UICollectionViewDiffableDataSource<Section, Item> = {
        let dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView, cellProvider: cellProvider)
        return dataSource
    }()

    lazy var collectionView: UICollectionView = {
        let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
        collectionView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
        collectionView.backgroundColor = .red //.clear
        return collectionView
    }()

    var isLoaded: Bool = false

    override func viewDidLoad() {
        super.viewDidLoad()

        configureCollectionView()

        // load initial data
        reloadDataSource()

        isLoaded = true
    }
}

extension CollectionViewController {

    func reloadDataSource(animating: Bool = false) {

        dataSource.apply(snapshot, animatingDifferences: animating) {
            print("applying snapshot completed!")
        }
    }
}

extension CollectionViewController {

    private func configureCollectionView() {
        view.addSubview(collectionView)

        collectionView.register(HostingControllerCollectionViewCell<AnyView>.self, forCellWithReuseIdentifier: HostingControllerCollectionViewCell<AnyView>.reuseIdentifier)

        collectionView.delegate = self

        print("configured collection view")
    }

    private func cellProvider(collectionView: UICollectionView, indexPath: IndexPath, item: Item) -> UICollectionViewCell? {

        print("providing cell for \(indexPath)...")
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HostingControllerCollectionViewCell<AnyView>.reuseIdentifier, for: indexPath) as? HostingControllerCollectionViewCell<AnyView> else {
            fatalError("Could not load cell")
        }

        //cell.host(AnyView(Text(item.title)))
        cell.host(content(indexPath, item))

        return cell
    }
}

It works but I can only load data source in viewDidLoad calling reloadDatasource() if this method is then called again from updateUIViewController in UIViewControllerRepresentable then collection view is left empty.

Here is full repository of this sample CollectionView wrapper https://github.com/michzio/SwifUICollectionView

UPDATE

I've noticed that I am getting this error

2020-05-02 07:18:20.891685+0200 SwiftUICollectionView[39727:58991470] [CollectionView] Layout attributes <UICollectionViewLayoutAttributes: 0x7fbd6f30b270> index path: (<NSIndexPath: 0xd64d621a2e1d9027> {length = 2, path = 0 - 37}); frame = (187.667 792; 187.667 44);  were received from the layout <UICollectionViewCompositionalLayout: 0x7fbd6df04920> but are not valid for the data source counts. Attributes will be ignored.

I've also noticed that if I change in updateUIViewController

controller.reloadDataSource(animating: false)

to

controller.reloadDataSource(animating: true)

cell are displayed. But consecutive refresh enforced on tap gesture hangs UI ( I think maybe there is too many items and calculating animation has problems, but no even if I decreased number of items to 1000 it freezes.)

UPDATE 2

Now when animating: true in reloadDataSource() (from updateUIViewController) I can get crashes but there is no information only EXC_BAD_ARITHM

if I remove initial data loading in CollectionViewController.viewDidLoad() then only updateUIViewController is called and here another wierd exception happens

* Assertion failure in -[UICollectionView _dequeueReusableViewOfKind:withIdentifier:forIndexPath:viewCategory:], /Library/Caches/com.apple.xbs/Sources/UIKitCore_Sim/UIKit-3920.26.113/UICollectionView.m:5971 2020-05-02 07:33:02.037804+0200 SwiftUICollectionView[40091:59004149] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'could not dequeue a view of kind: UICollectionElementKindCell with identifier ItemCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard' *** First throw call stack: ( 0 CoreFoundation 0x00007fff23e39f0e exceptionPreprocess + 350 1 libobjc.A.dylib
0x00007fff50ad79b2 objc_exception_throw + 48 2 CoreFoundation
0x00007fff23e39c88 +[NSException raise:format:arguments:] + 88 3
Foundation 0x00007fff258a3cd2 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 191 4
UIKitCore 0x00007fff4838b36e -[UICollectionView _dequeueReusableViewOfKind:withIdentifier:forIndexPath:viewCategory:] + 2426 5 UIKitCore 0x00007fff4838b53d -[UICollectionView dequeueReusableCellWithReuseIdentifier:forIndexPath:] + 88 6
SwiftUICollectionView 0x0000000108b5fcb3 $s21SwiftUICollectionView010CollectionC10ControllerC12cellProvider33_7499D878310ABE7B5F37FEF32561A438LL010collectionC09indexPath4itemSo0bC4CellCSgSo0bC0C_10Foundation05IndexP0VAA4ItemCtF + 867 7 SwiftUICollectionView 0x0000000108b62ad0 $s21SwiftUICollectionView010CollectionC10ControllerC12cellProvider33_7499D878310ABE7B5F37FEF32561A438LL010collectionC09indexPath4itemSo0bC4CellCSgSo0bC0C_10Foundation05IndexP0VAA4ItemCtFTA + 16 8 SwiftUICollectionView 0x0000000108b5ffaf $sSo16UICollectionViewC10Foundation9IndexPathV05SwiftaB04ItemCSo0aB4CellCSgIeggngo_AbehKIeggnno_TR + 15 9 libswiftUIKit.dylib 0x00007fff5170a8ce $s5UIKit29UITableViewDiffableDataSourceC05tableC012cellProviderACyxq_GSo0bC0C_So0bC4CellCSgAH_10Foundation9IndexPathVq_tctcfcAkH_ANyptcfU_ + 126 10 libswiftUIKit.dylib 0x00007fff5170a998 $sSo11UITableViewC10Foundation9IndexPathVypSo0aB4CellCSgIeggnno_ABSo07NSIndexE0CyXlAHIeyByyya_TR + 168 11 UIKitCore 0x00007fff48342a89 -[__UIDiffableDataSource collectionView:cellForItemAtIndexPath:] + 165 12 UIKitCore 0x00007fff483432d5 -[__UIDiffableDataSource _cellForItemAtIndexPath:collectionView:] + 50 13 libswiftUIKit.dylib 0x00007fff5170b036 $s5UIKit34UICollectionViewDiffableDataSourceC010collectionC0_13cellForItemAtSo0bC4CellCSo0bC0C_10Foundation9IndexPathVtFTm + 70 14 libswiftUIKit.dylib 0x00007fff5170b110 $s5UIKit34UICollectionViewDiffableDataSourceC010collectionC0_13cellForItemAtSo0bC4CellCSo0bC0C_10Foundation9IndexPathVtFToTm + 128 15 UIKitCore 0x00007fff483759d5 -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:isFocused:notify:] + 416 16 UIKitCore 0x00007fff4837582f -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:] + 31 17 UIKitCore 0x00007fff48395e4e __51-[UICollectionView _viewAnimationsForCurrentUpdate]_block_invoke.1814 + 661 18 UIKitCore 0x00007fff483929e4 -[UICollectionView _viewAnimationsForCurrentUpdate] + 3213 19 UIKitCore 0x00007fff48399cde __71-[UICollectionView _updateWithItems:tentativelyForReordering:animator:]_block_invoke.1887 + 118 20 UIKitCore 0x00007fff490bb8a8 +[UIView(Animation) performWithoutAnimation:] + 84 21 UIKitCore 0x00007fff48398b24 -[UICollectionView _updateWithItems:tentativelyForReordering:animator:] + 4003 22 UIKitCore 0x00007fff48391689 -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:] + 16761 23 UIKitCore 0x00007fff4839b0e6 -[UICollectionView _endUpdatesWithInvalidationContext:tentativelyForReordering:animator:] + 71 24 UIKitCore 0x00007fff4839b445 -[UICollectionView _performBatchUpdates:completion:invalidationContext:tentativelyForReordering:animator:] + 462 25 UIKitCore 0x00007fff4839b254 -[UICollectionView _performBatchUpdates:completion:invalidationContext:tentativelyForReordering:] + 90 26 UIKitCore 0x00007fff4839b1d7 -[UICollectionView _performBatchUpdates:completion:invalidationContext:] + 74 27 UIKitCore 0x00007fff4839b12c -[UICollectionView performBatchUpdates:completion:] + 53 28 UIKitCore 0x00007fff483aa9ef -[UICollectionView _performDiffableUpdate:] + 44 29 UIKitCore 0x00007fff4834906e -[_UIDiffableDataSourceViewUpdater _performUpdateWithCollectionViewUpdateItems:dataSourceSnapshotter:updateHandler:completion:] + 467 30 UIKitCore 0x00007fff4834213a -[__UIDiffableDataSource _commitNewDataSource:withViewUpdates:completion:] + 246 31 UIKitCore 0x00007fff4833cb22 __66-[__UIDiffableDataSource applyDifferencesFromSnapshot:completion:]_block_invoke.154 + 190 32 UIKitCore 0x00007fff4833cdb1 __66-[__UIDiffableDataSource applyDifferencesFromSnapshot:completion:]_block_invoke.180 + 107 33 libdispatch.dylib 0x0000000108e01e8e _dispatch_client_callout + 8 34 libdispatch.dylib 0x0000000108e10ae2 _dispatch_lane_barrier_sync_invoke_and_complete + 132 35 UIKitCore 0x00007fff4833c62b -[__UIDiffableDataSource applyDifferencesFromSnapshot:completion:] + 952 36 UIKitCore 0x00007fff4833d63d -[__UIDiffableDataSource applyDifferencesFromSnapshot:animatingDifferences:completion:] + 71 37 libswiftUIKit.dylib 0x00007fff5170aaf4 $s5UIKit34UICollectionViewDiffableDataSourceC5apply_20animatingDifferences10completionyAA010NSDiffableeF8SnapshotVyxq_G_SbyycSgtFTm + 212 38 SwiftUICollectionView 0x0000000108b620b9 $s21SwiftUICollectionView010CollectionC10ControllerC16reloadDataSource8snapshot9animatingy5UIKit010NSDiffablegH8SnapshotVyAA7SectionOAA4ItemCG_SbtF + 985 39 SwiftUICollectionView 0x0000000108b5682f $s21SwiftUICollectionView010CollectionC0V22updateUIViewController_7contextyAA0dcG0C_0A2UI0fG20RepresentableContextVyACGtF + 335 40 SwiftUICollectionView 0x0000000108b56a2b $s21SwiftUICollectionView010CollectionC0V0A2UI29UIViewControllerRepresentableAadEP06updatefG0_7contexty0fG4TypeQz_AD0fgH7ContextVyxGtFTW + 59 41 SwiftUI 0x00007fff2c59a1b2 $s7SwiftUI42PlatformViewControllerRepresentableAdaptorV06updateD8Provider_7contexty06UIViewE4TypeQz_AA0cdF7ContextVyACyxGGtF + 290 42 SwiftUI 0x00007fff2c670439 $s7SwiftUI17PlatformViewChild33_A513612C07DFA438E70B9FA90719B40DLLV6update7contexty14AttributeGraph0O7ContextVyADyxGGz_tFyyXEfU_yyXEfU_ + 217 43 SwiftUI 0x00007fff2c7f5e20 $s7SwiftUI16ViewRendererHostPAAE21performExternalUpdateyyyyXEF + 192 44 SwiftUI 0x00007fff2c66f810 $s7SwiftUI17PlatformViewChild33_A513612C07DFA438E70B9FA90719B40DLLV6update7contexty14AttributeGraph0O7ContextVyADyxGGz_tFyyXEfU_7performL_4workyyyXE_tAA0cD13RepresentableRzlF + 224 45 SwiftUI 0x00007fff2c66e6b6 $s7SwiftUI17PlatformViewChild33_A513612C07DFA438E70B9FA90719B40DLLV6update7contexty14AttributeGraph0O7ContextVyADyxGGz_tFyyXEfU_ + 2454 46 SwiftUI 0x00007fff2c6674ae $s7SwiftUI17PlatformViewChild33_A513612C07DFA438E70B9FA90719B40DLLV6update7contexty14AttributeGraph0O7ContextVyADyxGGz_tF + 590 47 SwiftUI 0x00007fff2c670940 $s7SwiftUI17PlatformViewChild33_A513612C07DFA438E70B9FA90719B40DLLVyxG14AttributeGraph07UntypedM0AafGP7_update_5graph9attributeySv_So10AGGraphRefaSo11AGAttributeatFZTW + 32 48 AttributeGraph 0x00007fff2fc46309 $sTA + 25 49 AttributeGraph 0x00007fff2fc2ed45 _ZN2AG5Graph11UpdateStack6updateEv + 455 50 AttributeGraph 0x00007fff2fc2f253 _ZN2AG5Graph16update_attributeEjb + 373 51 AttributeGraph 0x00007fff2fc33d5b _ZN2AG8Subgraph6updateEj + 729 52 SwiftUI 0x00007fff2c51d690 $s7SwiftUI9ViewGraphC14runTransaction33_D63C4EB7F2B205694B6515509E76E98BLL2inySo10AGGraphRefa_tF + 224 53 SwiftUI 0x00007fff2c51da67 $s7SwiftUI9ViewGraphC13updateOutputs2atyAA4TimeV_tFSb5prefs_Sb9idealSizeAC0F0V7outputstSo10AGGraphRefaXEfU_ + 103 54 SwiftUI 0x00007fff2c51d74d $s7SwiftUI9ViewGraphC13updateOutputs2atyAA4TimeV_tF + 125 55 SwiftUI 0x00007fff2c80146b $s7SwiftUI16ViewRendererHostPAAE6render8interval17updateDisplayListySd_SbtFyyXEfU_yyXEfU_ + 811 56 SwiftUI 0x00007fff2c8008c3 $s7SwiftUI16ViewRendererHostPAAE6render8interval17updateDisplayListySd_SbtFyyXEfU_ + 547 57 SwiftUI 0x00007fff2c7f6415 $s7SwiftUI16ViewRendererHostPAAE6render8interval17updateDisplayListySd_SbtF + 373 58 SwiftUI 0x00007fff2c956102 $s7SwiftUI14_UIHostingViewC14layoutSubviewsyyF + 226 59 SwiftUI
0x00007fff2c956125 $s7SwiftUI14_UIHostingViewC14layoutSubviewsyyFTo + 21 60 UIKitCore 0x00007fff490c9848 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2478 61 QuartzCore 0x00007fff2b4ae3f0 -[CALayer layoutSublayers] + 255 62 QuartzCore
0x00007fff2b4b457b _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 523 63 QuartzCore 0x00007fff2b4bfc12 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 80 64 QuartzCore 0x00007fff2b408c84 _ZN2CA7Context18commit_transactionEPNS_11TransactionEd + 324 65 QuartzCore 0x00007fff2b43c65f _ZN2CA11Transaction6commitEv + 649 66 UIKitCore 0x00007fff48bdfc2b __34-[UIApplication _firstCommitBlock]_block_invoke_2 + 81 67 CoreFoundation 0x00007fff23d9dcdc __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK
+ 12 68 CoreFoundation 0x00007fff23d9d3d3 __CFRunLoopDoBlocks + 195 69 CoreFoundation 0x00007fff23d981c3 __CFRunLoopRun + 995 70 CoreFoundation
0x00007fff23d97ac4 CFRunLoopRunSpecific + 404 71 GraphicsServices
0x00007fff38b2fc1a GSEventRunModal + 139 72 UIKitCore
0x00007fff48bc7f80 UIApplicationMain + 1605 73 SwiftUICollectionView 0x0000000108b548fb main + 75 74 libdyld.dylib
0x00007fff519521fd start + 1 )

I forgot to say that this exceptions start to happen when I replaced CompositionalLayout wit simple UICollectionFlowLayout!

Michał Ziobro
  • 10,759
  • 11
  • 88
  • 143

1 Answers1

0

As I see you don't apply a new snapshot in updateUIViewController method. It should be something like this

func updateUIViewController(_ controller: CollectionViewController, context: Context) {

    controller.snapshot = snapshotForCurrentState()
    controller.reloadDataSource()
}
  • I have this code func updateUIViewController(_ controller: CollectionViewController, context: Context) { controller.snapshot = snapshotForCurrentState() controller.reloadDataSource() } Above I just pasted mistyped code. But with this reloadDataSource() it does not work – Michał Ziobro May 01 '20 at 20:00