I am trying to make a timeline where each section is a day and each day has many items (records). Here is my section (day) class:
class YearMonthDay: Comparable, Hashable {
let year: Int
let month: Int
let day: Int
init(year: Int, month: Int, day: Int) {
self.year = year
self.month = month
self.day = day
}
init(date: Date) {
let comps = Calendar.current.dateComponents([.year, .month, .day], from: date)
self.year = comps.year!
self.month = comps.month!
self.day = comps.day!
}
func hash(into hasher: inout Hasher) {
hasher.combine(year)
hasher.combine(month)
hasher.combine(day)
}
var date: Date {
var dateComponents = DateComponents()
dateComponents.year = year
dateComponents.month = month
dateComponents.day = day
return Calendar.current.date(from: dateComponents)!
}
static func == (lhs: YearMonthDay, rhs: YearMonthDay) -> Bool {
return lhs.year == rhs.year && lhs.month == rhs.month && lhs.day == rhs.day
}
static func < (lhs: YearMonthDay, rhs: YearMonthDay) -> Bool {
if lhs.year != rhs.year {
return lhs.year < rhs.year
} else {
if lhs.month != rhs.month {
return lhs.month < rhs.month
} else {
return lhs.day < rhs.day
}
}
}
}
As you can see I am adding year month and day attributes to my sections and using them to make each one "washable" so hopefully there will be only 1 section for each day at most.
Here is my collectionViewController...
class TimelineViewController: UICollectionViewController {
private lazy var dataSource = makeDataSource()
fileprivate typealias DataSource = UICollectionViewDiffableDataSource<YearMonthDay,TestRecord>
fileprivate typealias DataSourceSnapshot = NSDiffableDataSourceSnapshot<YearMonthDay,TestRecord>
public var data: [YearMonthDay:[TestRecord]] = [:]
var delegate: TimelineViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
guard let data = delegate?.dataForTimelineView() else { return }
self.data = data
collectionView.showsHorizontalScrollIndicator = true
configureHierarchy()
configureDataSource()
applySnapshot()
}
}
extension TimelineViewController {
fileprivate func makeDataSource() -> DataSource {
let dataSource = DataSource(
collectionView: collectionView,
cellProvider: { (collectionView, indexPath, testRecord) ->
UICollectionViewCell? in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TimelineDayCell.identifier, for: indexPath) as? TimelineDayCell
cell?.configure(with: testRecord)
cell?.dayLabel.text = String(indexPath.section)+","+String(indexPath.row)
return cell
})
return dataSource
}
func configureDataSource() {
self.collectionView!.register(TimelineDayCell.nib, forCellWithReuseIdentifier: TimelineDayCell.identifier)
}
func applySnapshot(animatingDifferences: Bool = true) {
// 2
var snapshot = DataSourceSnapshot()
for (ymd,records) in data {
snapshot.appendSections([ymd])
snapshot.appendItems(records,toSection: ymd)
}
// This is where the error occurs.
dataSource.apply(snapshot, animatingDifferences: animatingDifferences)
}
}
But I get this crash error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'request for number of items in section 0 when there are only 0 sections in the collection view'
If I look at data, I can see that all of it is being populated correctly and when I print out snapshot.sectionIdentifiers it gives me all of those YearMonthDay objects. But somehow the dataSource is seeing 0 sections?
I think for a normal data source, I would just implement the delegate method - numberOfSections but since this is a diffable data source Im not sure what to do...
Edit:
data from the delegate is a dictionary of this type: [YearMonthDay:[TestRecord]]. Here is what it looks like when printed out
▿ 7 elements
▿ 0 : 2 elements
▿ key : <YearMonthDay: 0x600000e880f0>
▿ value : 1 element
▿ 0 : TestRecord
- identifier : 5D05C73D-8863-47E3-B1E5-3331791A3FB8
- type : SweatNetOffline.RecordType
- progression : 1
▿ timeStamp : 2020-10-16 04:56:59 +0000
- timeIntervalSinceReferenceDate : 624517019.639298
▿ 1 : 2 elements
▿ key : <YearMonthDay: 0x600000e89ec0>
▿ value : 1 element
▿ 0 : TestRecord
- identifier : C40F5884-AABF-43C8-9921-95DD1423AC4D
- type : SweatNetOffline.RecordType
- progression : 1
▿ timeStamp : 2020-10-22 04:56:59 +0000
- timeIntervalSinceReferenceDate : 625035419.639274
▿ 2 : 2 elements
▿ key : <YearMonthDay: 0x600000e88cf0>
▿ value : 1 element
▿ 0 : TestRecord
- identifier : 5EE70ABF-4C4D-4FB5-A850-3D0081D91D0D
- type : SweatNetOffline.RecordType
- progression : 2
▿ timeStamp : 2020-10-20 04:56:59 +0000
- timeIntervalSinceReferenceDate : 624862619.639284
▿ 3 : 2 elements
▿ key : <YearMonthDay: 0x600000e8a0a0>
▿ value : 1 element
▿ 0 : TestRecord
- identifier : 59152DCA-63EC-4EBF-ACDB-B45FA5E85EED
- type : SweatNetOffline.RecordType
- progression : 2
▿ timeStamp : 2020-10-18 04:56:59 +0000
- timeIntervalSinceReferenceDate : 624689819.639291
▿ 4 : 2 elements
▿ key : <YearMonthDay: 0x600000e89890>
▿ value : 1 element
▿ 0 : TestRecord
- identifier : 0E12D3D6-0650-41A6-AC12-EB75EFA0A151
- type : SweatNetOffline.RecordType
- progression : 0
▿ timeStamp : 2020-10-26 04:56:59 +0000
- timeIntervalSinceReferenceDate : 625381019.638627
▿ 5 : 2 elements
▿ key : <YearMonthDay: 0x600000e89e60>
▿ value : 1 element
▿ 0 : TestRecord
- identifier : 99A929C4-B94B-4F8F-8C6D-2C356D778D95
- type : SweatNetOffline.RecordType
- progression : 2
▿ timeStamp : 2020-10-24 04:56:59 +0000
- timeIntervalSinceReferenceDate : 625208219.639251
▿ 6 : 2 elements
▿ key : <YearMonthDay: 0x600000e89f20>
▿ value : 1 element
▿ 0 : TestRecord
- identifier : EF6EB971-504B-4587-8038-FB8C3FD7ACDC
- type : SweatNetOffline.RecordType
- progression : 1
▿ timeStamp : 2020-10-14 04:56:59 +0000
- timeIntervalSinceReferenceDate : 624344219.639307
I'm not super familiar with this new way of constructing a collectionView, but I believe the problem could be due to this line of code
Why are you passing in an array here, for each iteration in the For loop? This should be done once, should it not?
to