UITableViewDiffableDataSource and NSDiffableDataSourceSnapshot for different objects shows only one row

934 views Asked by At

I've implemented UITableView using UITableViewDiffableDataSource and NSDiffableDataSourceSnapshot like

    private typealias ListDataSource = UITableViewDiffableDataSource<Section, Wrapper> 
    private typealias ListSnapshot = NSDiffableDataSourceSnapshot<Section, Wrapper>


 enum Wrapper: Hashable {
    case one([Company])
    case two([Member])
}

private enum Section: CaseIterable {
    case main
}

private func configureDataSource() {

        dataSource = ListDataSource(tableView: listTableView,
                                    cellProvider: { [weak self] (_, indexPath, wrapper) -> UITableViewCell? in

                                        guard let `self` = self else {
                                            return UITableViewCell()
                                        }
                                        switch wrapper {

                                        case .one(let company):
                                            let cell = self.listTableView.dequeueReusableCell(withIdentifier: "Cell",
                                                                                              for: indexPath)
                                            cell.textLabel?.text = company[indexPath.row].name
                                            return cell
                                        case .two(let member):

                                            let cell = self.listTableView.dequeueReusableCell(withIdentifier: "Cell",
                                                                                              for: indexPath)
                                            cell.textLabel?.text = member[indexPath.row].name.first
                                            return cell
                                        }
        })

    }

func updateData(_ wrapper: Wrapper) {
        var snapshot = ListSnapshot()
        snapshot.appendSections([.main])

        switch  wrapper {
        case .one(let comp):
            snapshot.appendItems([.one(comp)])
            dataSource.apply(snapshot, animatingDifferences: true)
        case .two(let member):
            snapshot.appendItems([.two(member)])
            dataSource.apply(snapshot, animatingDifferences: true)
        }
    }

on segment change, updating data respective of Wrapper type. But the issue is only one record displayed every time.

func handleSegmentChanged(_ sender: UISegmentedControl) {

   

 let member = Member(name: Name(first: "Harshal", last: "Wani"),
                        memberId: "123", age: 30, email: "[email protected]", phone: "123456789")
    let member2 = Member(name: Name(first: "David", last: "John"),
                        memberId: "123", age: 30, email: "[email protected]", phone: "123456789")

    let comp = Company(name: "Comp 1", companyId: "", website: "", logo: "", about: "", members: [member, member2])
    let comp2 = Company(name: "Comp 2", companyId: "", website: "", logo: "", about: "", members: [member, member2])

    if sender.selectedSegmentIndex == 0 {
        updateData(.one([comp, comp2]))
    } else {
        updateData(.two(comp.members))
    }
}

Appreciate for any help, thanks

1

There are 1 answers

0
vadian On BEST ANSWER

You are applying only one item per section, you have to declare the wrapper

enum Wrapper: Hashable {
    case one(Company)
    case two(Member)
}

In handleSegmentChanged create an array of Wrapper items instead of one Wrapper with an array of associated types.

@IBAction func handleSegmentChanged(_ sender: UISegmentedControl) {
    
    let member = Member(name: Name(first: "Harshal", last: "Wani"), memberId: "123", age: 30, email: "[email protected]", phone: "123456789")
    let member2 = Member(name: Name(first: "David", last: "John"), memberId: "123", age: 30, email: "[email protected]", phone: "123456789")
    
    let comp = Company(name: "Comp 1", companyId: "", website: "", logo: "", about: "", members: [member, member2])
    let comp2 = Company(name: "Comp 2", companyId: "", website: "", logo: "", about: "", members: [member, member2])
    
    if sender.selectedSegmentIndex == 0 {
        updateData([.one(comp), .one(comp2)])
    } else {
        updateData(comp.members.map{.two($0)})
    }
}

And replace updateData with

func updateData(_ wrapper: [Wrapper]) {
    var snapshot = ListSnapshot()
    snapshot.appendSections([.main])
    snapshot.appendItems(wrapper)
    dataSource.apply(snapshot, animatingDifferences: true)
}

The [weak self] -> self dance in configureDataSource is nonsense. The first parameter of the closure is the table view. Use this instance to avoid any occurrence of self and replace configureDataSource with

 private func configureDataSource() {
    
    dataSource = ListDataSource(tableView: listTableView,
                                cellProvider: { (tableView, indexPath, wrapper) -> UITableViewCell? in
                                    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell",
                                                                                        for: indexPath)
                                    switch wrapper {
                                        case .one(let company):
                                            cell.textLabel?.text = company.name
                                        case .two(let member):
                                            cell.textLabel?.text = member.name.first
                                    }
                                    return cell
    })
}