swiftuitableviewios13nsdiffabledatasourcesnapshotuitableviewdiffabledatasource

UITableViewDiffableDataSource and NSDiffableDataSourceSnapshot for different objects shows only one row


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: "harshal@gmail.com", phone: "123456789")
    let member2 = Member(name: Name(first: "David", last: "John"),
                        memberId: "123", age: 30, email: "harshal@gmail.com", 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


Solution

  • 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: "harshal@gmail.com", phone: "123456789")
        let member2 = Member(name: Name(first: "David", last: "John"), memberId: "123", age: 30, email: "harshal@gmail.com", 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
        })
    }