swift Diffable data source error - 'Fatal: supplied item identifiers are not unique. Duplicate identifiers:

895 views Asked by At

I think there is a problem with the calendar model or the data I put in the pile, but I can't find it no matter how much I look for it. I'm desperate for help

The contents of the error are as follows

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Fatal: supplied item identifiers are not unique. Duplicate identifiers: {(
        HomeDataSource.Item.calendar
class HomeDataSource {
    typealias DataSource = UITableViewDiffableDataSource<Section, Item>
    
    private let tableView: UITableView
    private lazy var dataSource = createDataSource()
    private let postType: PostCase
    
    var calendars: [Calendar] = [
        Calendar(date: "haha"),
        Calendar(date: "hhohoho"),
    ]
    
    private var posts: [UserPost]
    
    enum Section: CaseIterable {
        case calendar
        case post
        
        init(rawValue: Int) {
            switch rawValue {
            case 0: self = .calendar
            case 1: self = .post
            default:
                fatalError("not exist section")
            }
        }
    }
    
    enum Item: Hashable {
        case calendar
        case post
    }
    
    init(tableView: UITableView, postType: PostCase) {
        self.tableView = tableView
        self.postType = postType
        self.posts = .init()
    }
    
    func createDataSource() -> UITableViewDiffableDataSource<Section, Item> {
        tableView.register(CalendarTableViewCell.self, forCellReuseIdentifier: CalendarTableViewCell.identifier)
        tableView.register(UserPostTableViewCell.self, forCellReuseIdentifier: UserPostTableViewCell.identifier)
        tableView.register(EmptyPostTableViewCell.self, forCellReuseIdentifier: EmptyPostTableViewCell.identifier)
        
        
        dataSource = UITableViewDiffableDataSource<Section, Item>(tableView: tableView) {
            tableView, indexPath, item in
            switch item {
            case .calendar:
                let cell = tableView.dequeueReusableCell(withIdentifier: CalendarTableViewCell.identifier, for: indexPath)
                cell.selectionStyle = .none
                return cell
            case .post:
                switch self.postType {
                case .postExist:
                    guard let cell = tableView.dequeueReusableCell(withIdentifier: UserPostTableViewCell.identifier, for: indexPath) as? UserPostTableViewCell else { return UITableViewCell() }
                    cell.selectionStyle = .none
                    cell.configure()
                    return cell
                case .friendPostEmpty:
                    guard let cell = tableView.dequeueReusableCell(withIdentifier: EmptyPostTableViewCell.identifier, for: indexPath) as? EmptyPostTableViewCell else { return UITableViewCell() }
                    cell.configure(isFriend: true)
                    return cell
                case .ownerPostEmpty:
                    guard let cell = tableView.dequeueReusableCell(withIdentifier: EmptyPostTableViewCell.identifier, for: indexPath) as? EmptyPostTableViewCell else { return UITableViewCell() }
                    cell.configure(isFriend: false)
                    return cell
                }
            }
        }
        
        return dataSource
    }

    func updateSnapshot(posts: [UserPost]) {
        var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
        
        snapshot.appendSections([.calendar, .post])
        let calendarIds = calendars.map { _ in Item.calendar }
        let postIds = posts.map { _ in Item.post }

        snapshot.appendItems(calendarIds, toSection: .calendar)
        snapshot.appendItems(postIds, toSection: .post)

        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

Calendar model is as follows

struct Calendar: Hashable {
    
    let id = UUID()
    let date: String
    
    func hash(into hasher: inout Hasher) {
      hasher.combine(id)
    }

    static func == (lhs: Calendar, rhs: Calendar) -> Bool {
      lhs.id == rhs.id
    }
    
    init(date: String) {
        self.date = date
    }
}

Also, I would like to ask you additionally what should I do if I want to put only one value in the snapshot data?

1

There are 1 answers

2
Bulat Yakupov On

The problem is here:

let calendarIds = calendars.map { _ in Item.calendar }
let postIds = posts.map { _ in Item.post }

You just map all the different models into equal Item. Any Calendar with unique id turns into Item.calendar which have no id. All of them are equal.

if you still want to us enum then I suggest you declare it like that:

enum Item {
    case calendar(Calendar)
    case post(UserPost)
}

extension Item: Hashable {
    
    public static func == (lhs: Item, rhs: Item) -> Bool {
        switch (lhs, rhs) {
        case (.calendar(let calendar1), .calendar(let calendar2)):
            return calendar1.id == calendar2.id
        case (.post(let post1), .post(let post2)):
            return post1.id == post2.id
        default:
            return false
        }
    }
    
    public func hash(into hasher: inout Hasher) {
        switch self {
        case .calendar(let calendar):
            hasher.combine("\(calendar.id)_calendar")
        case .post(let post):
            hasher.combine("\(post.id)_post")
        }
    }
    
}

This should help you.

Moreover, as vadian mentioned, you better not use Calendar. Rename it to UserCalendar for example.