diff --git a/CHANGELOG.md b/CHANGELOG.md index bb2b00d..c33d1c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ The changelog for `ReactiveLists`. Also see the [releases](https://github.com/pl ------ +0.1.2 +----- + +### Changed + +- Section and cell view models are now diffable by default. ([#119](https://github.com/plangrid/ReactiveLists/pull/119), [@jessesquires](https://github.com/jessesquires)) +Each provide default values for `diffingKey`, but you can customize them for your own needs or opt-out of automatic diffing. + - `CollectionSectionViewModel` protocol now inherits from `DiffableViewModel` protocol + - `CollectionCellViewModel` protocol now inherits from `DiffableViewModel` protocol + - ` TableSectionViewModel` protocol now inherits from `DiffableViewModel` protocol + - `TableCellViewModel` protocol now inherits from `DiffableViewModel` protocol + 0.1.1 ----- @@ -22,7 +34,7 @@ This release closes the [0.1.1 milestone](https://github.com/plangrid/ReactiveLi ### New -- You can now customize the cell insertion and deletion animations on `TableViewDriver`. (#115, @wickwirew) +- You can now customize the cell insertion and deletion animations on `TableViewDriver`. ([#115](https://github.com/plangrid/ReactiveLists/pull/115), [@wickwirew](https://github.com/wickwirew)) - `ViewRegistrationInfo` properties `reuseIdentifier` and `registrationMethod` are now public - `ViewRegistrationInfo` now conforms to `Equatable` - `SupplementaryViewInfo` now conforms to `Equatable` diff --git a/Sources/CollectionViewModel.swift b/Sources/CollectionViewModel.swift index b618d31..78c6552 100644 --- a/Sources/CollectionViewModel.swift +++ b/Sources/CollectionViewModel.swift @@ -18,7 +18,7 @@ import Dwifft import UIKit /// View model for the individual cells of a `UICollectionView`. -public protocol CollectionCellViewModel: ReusableCellViewModelProtocol { +public protocol CollectionCellViewModel: ReusableCellViewModelProtocol, DiffableViewModel { /// `CollectionViewDriver` will automatically apply an `accessibilityIdentifier` to the cell based on this format var accessibilityFormat: CellAccessibilityFormat { get } @@ -110,27 +110,15 @@ public struct CollectionViewModel { var diffingKeys: SectionedValues { return SectionedValues( self.sectionModels.map { section in - // Ensure we have a diffing key for the current section - guard let sectionDiffingKey = section.diffingKey else { - fatalError("When diffing is enabled you need to provide a non-nil diffingKey for each section.") - } - - // Ensure we have a diffing key for each cell in this section - let cellDiffingKeys: [DiffingKey] = section.cellViewModels.map { cell in - guard let cell = cell as? DiffableViewModel else { - fatalError("When diffing is enabled you need to provide cells which are DiffableViews.") - } - return "\(type(of: cell))_\(cell.diffingKey)" - } - - return (sectionDiffingKey, cellDiffingKeys) + let cellDiffingKeys = section.cellViewModels.map { $0.diffingKey } + return (section.diffingKey, cellDiffingKeys) } ) } } /// View model for a collection view section. -public struct CollectionSectionViewModel { +public struct CollectionSectionViewModel: DiffableViewModel { /// Cells to be shown in this section. let cellViewModels: [CollectionCellViewModel] @@ -148,7 +136,7 @@ public struct CollectionSectionViewModel { /// For example: /// /// public var diffingKey = { group.identifier } - public var diffingKey: String? + public var diffingKey: String /// Initializes a collection view section view model. /// @@ -161,8 +149,7 @@ public struct CollectionSectionViewModel { cellViewModels: [CollectionCellViewModel], headerViewModel: CollectionSupplementaryViewModel? = nil, footerViewModel: CollectionSupplementaryViewModel? = nil, - diffingKey: String? = nil - ) { + diffingKey: String = UUID().uuidString) { self.cellViewModels = cellViewModels self.headerViewModel = headerViewModel self.footerViewModel = footerViewModel @@ -190,14 +177,12 @@ extension CollectionSectionViewModel { cellViewModels: [CollectionCellViewModel], headerHeight: CGFloat? = nil, footerViewModel: CollectionSupplementaryViewModel? = nil, - diffingKey: String? = nil - ) { + diffingKey: String = UUID().uuidString) { self.init( cellViewModels: cellViewModels, headerViewModel: BlankSupplementaryViewModel(height: headerHeight), footerViewModel: footerViewModel, - diffingKey: diffingKey - ) + diffingKey: diffingKey) } /// :nodoc: @@ -205,14 +190,12 @@ extension CollectionSectionViewModel { cellViewModels: [CollectionCellViewModel], headerViewModel: CollectionSupplementaryViewModel? = nil, footerHeight: CGFloat? = nil, - diffingKey: String? = nil - ) { + diffingKey: String = UUID().uuidString) { self.init( cellViewModels: cellViewModels, headerViewModel: headerViewModel, footerViewModel: BlankSupplementaryViewModel(height: footerHeight), - diffingKey: diffingKey - ) + diffingKey: diffingKey) } /// :nodoc: @@ -220,13 +203,11 @@ extension CollectionSectionViewModel { cellViewModels: [CollectionCellViewModel], headerHeight: CGFloat? = nil, footerHeight: CGFloat? = nil, - diffingKey: String? = nil - ) { + diffingKey: String = UUID().uuidString) { self.init( cellViewModels: cellViewModels, headerViewModel: BlankSupplementaryViewModel(height: headerHeight), footerViewModel: BlankSupplementaryViewModel(height: footerHeight), - diffingKey: diffingKey - ) + diffingKey: diffingKey) } } diff --git a/Sources/TableViewModel.swift b/Sources/TableViewModel.swift index a172043..4e603ac 100644 --- a/Sources/TableViewModel.swift +++ b/Sources/TableViewModel.swift @@ -18,7 +18,7 @@ import Dwifft import UIKit /// View model for the individual cells of a `TableViewModel`. -public protocol TableCellViewModel: ReusableCellViewModelProtocol { +public protocol TableCellViewModel: ReusableCellViewModelProtocol, DiffableViewModel { /// `TableViewDriver` will automatically apply an `accessibilityIdentifier` to the cell based on this format. var accessibilityFormat: CellAccessibilityFormat { get } @@ -114,7 +114,7 @@ public protocol TableSectionHeaderFooterViewModel: ReusableSupplementaryViewMode } /// View model for a table view section. -public struct TableSectionViewModel { +public struct TableSectionViewModel: DiffableViewModel { /// Cells to be shown in this section. public let cellViewModels: [TableCellViewModel] @@ -136,7 +136,7 @@ public struct TableSectionViewModel { /// For example: /// /// public var diffingKey = { group.identifier } - public var diffingKey: String? + public var diffingKey: String /// Returns `true` is the section is empty, `false` otherwise. public var isEmpty: Bool { @@ -146,18 +146,17 @@ public struct TableSectionViewModel { /// Initializes a `TableSectionViewModel`. /// /// - Parameters: - /// - cellViewModels: the cell view models contained in this section. - /// - headerViewModel: a header view model for this section (defaults to `nil`). - /// - footerViewModel: a footer view model for this section (defaults to `nil`). - /// - collapsed: whether or not this section is collapsed (defaults to `false`). - /// - diffingKey: the diffing key, or `nil`. Required for automated diffing. + /// - cellViewModels: The cell view models contained in this section. + /// - headerViewModel: A header view model for this section (defaults to `nil`). + /// - footerViewModel: A footer view model for this section (defaults to `nil`). + /// - collapsed: Whether or not this section is collapsed (defaults to `false`). + /// - diffingKey: A diffing key. public init( cellViewModels: [TableCellViewModel], headerViewModel: TableSectionHeaderFooterViewModel? = nil, footerViewModel: TableSectionHeaderFooterViewModel? = nil, collapsed: Bool = false, - diffingKey: String? = nil - ) { + diffingKey: String = UUID().uuidString) { self.cellViewModels = cellViewModels self.headerViewModel = headerViewModel self.footerViewModel = footerViewModel @@ -168,22 +167,19 @@ public struct TableSectionViewModel { /// Initializes a `TableSectionViewModel`. /// /// - Parameters: - /// - headerTitle: title for the header, or `nil`. Setting a title will cause a default header - /// to be added to this section. - /// - headerHeight: the height of the default header, if one exists. - /// - cellViewModels: the cell view models contained in this section. - /// - footerTitle: title for the footer, or `nil`. Setting a title will cause a default footer - /// to be added to this section. - /// - footerHeight: the height of the default footer, if one exists. - /// - diffingKey: the diffing key, or `nil`. Required for automated diffing. + /// - headerTitle: The title for the header, or `nil`. Setting a title will cause a default header to be added to this section. + /// - headerHeight: The height of the default header, if one exists. + /// - cellViewModels: The cell view models contained in this section. + /// - footerTitle: The title for the footer, or `nil`. Setting a title will cause a default footeer to be added to this section. + /// - footerHeight: The height of the default footer, if one exists. + /// - diffingKey: A diffing key. public init( headerTitle: String?, headerHeight: CGFloat?, cellViewModels: [TableCellViewModel], footerTitle: String? = nil, footerHeight: CGFloat? = 0, - diffingKey: String? = nil - ) { + diffingKey: String = UUID().uuidString) { self.cellViewModels = cellViewModels self.headerViewModel = PlainHeaderFooterViewModel(title: headerTitle, height: headerHeight) self.footerViewModel = PlainHeaderFooterViewModel(title: footerTitle, height: footerHeight) @@ -251,20 +247,8 @@ public struct TableViewModel { var diffingKeys: SectionedValues { return SectionedValues( self.sectionModels.map { section in - // Ensure we have a diffing key for the current section - guard let sectionDiffingKey = section.diffingKey else { - fatalError("When diffing is enabled you need to provide a non-nil diffingKey for each section.") - } - - // Ensure we have a diffing key for each cell in this section - let cellDiffingKeys: [DiffingKey] = section.cellViewModels.map { cell in - guard let cell = cell as? DiffableViewModel else { - fatalError("When diffing is enabled you need to provide cells which are DiffableViews.") - } - return "\(type(of: cell))_\(cell.diffingKey)" - } - - return (sectionDiffingKey, cellDiffingKeys) + let cellDiffingKeys = section.cellViewModels.map { $0.diffingKey } + return (section.diffingKey, cellDiffingKeys) } ) }