From 601ff94f3e1ade1fee877d1965aab7699587e810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikola=CC=81s=CC=8C=20Stuchli=CC=81k?= Date: Wed, 11 Sep 2019 10:53:52 +0200 Subject: [PATCH 01/16] Implement DifferenceKit --- Package.swift | 6 +- .../DiffableCellKit/EquatableCellModel.swift | 30 +++++--- .../EquatableCellModelDataSource.swift | 77 +++++++++++-------- 3 files changed, 68 insertions(+), 45 deletions(-) diff --git a/Package.swift b/Package.swift index 69b86e0..e3a37e9 100644 --- a/Package.swift +++ b/Package.swift @@ -4,7 +4,7 @@ import PackageDescription let package = Package( name: "CellKit", - platforms: [.iOS(.v8), .tvOS(.v9)], + platforms: [.iOS(.v9), .tvOS(.v9)], products: [ .library( name: "CellKit", @@ -14,7 +14,7 @@ let package = Package( targets: ["DiffableCellKit"]) ], dependencies: [ - .package(url: "https://github.com/jflinter/Dwifft", .revision("6fec2bc0246091b3e17a9d42e722fb98e05ac3ff")) + .package(url: "https://github.com/ra1028/DifferenceKit.git", from: "1.0.0") ], targets: [ .target( @@ -22,6 +22,6 @@ let package = Package( dependencies: []), .target( name: "DiffableCellKit", - dependencies: ["CellKit", "Dwifft"]) + dependencies: ["CellKit", "DifferenceKit"]) ] ) diff --git a/Sources/DiffableCellKit/EquatableCellModel.swift b/Sources/DiffableCellKit/EquatableCellModel.swift index 61197bb..6fa2e20 100644 --- a/Sources/DiffableCellKit/EquatableCellModel.swift +++ b/Sources/DiffableCellKit/EquatableCellModel.swift @@ -5,39 +5,49 @@ // Created by Matěj Kašpar Jirásek on 10/07/2019. // +import DifferenceKit #if SWIFT_PACKAGE import CellKit #endif -public protocol EquatableCellModel: CellModel { +public protocol DifferentiableCellModel: CellModel { func isEqual(to other: CellModel) -> Bool + func hash() -> Int } -extension EquatableCellModel { - func asEquatable() -> EquatableCellModelWrapper { - return EquatableCellModelWrapper(self) +extension DifferentiableCellModel { + func asEquatable() -> DifferentiableCellModelWrapper { + return DifferentiableCellModelWrapper(self) } } -extension EquatableCellModel where Self: Equatable { +extension DifferentiableCellModel where Self: Hashable { public func isEqual(to other: CellModel) -> Bool { guard let otherCellModel = other as? Self else { return false } return self == otherCellModel } + + public func hash() -> Int { + return self.hashValue + } } -struct EquatableCellModelWrapper { - let cellModel: EquatableCellModel +struct DifferentiableCellModelWrapper { + let cellModel: DifferentiableCellModel - init(_ cellModel: EquatableCellModel) { + init(_ cellModel: DifferentiableCellModel) { self.cellModel = cellModel } } -extension EquatableCellModelWrapper: Equatable { - static func == (lhs: EquatableCellModelWrapper, rhs: EquatableCellModelWrapper) -> Bool { +extension DifferentiableCellModelWrapper: Hashable, Differentiable { + static func == (lhs: DifferentiableCellModelWrapper, rhs: DifferentiableCellModelWrapper) -> Bool { return lhs.cellModel.isEqual(to: rhs.cellModel) } + + public func hash(into hasher: inout Hasher) { + hasher.combine(cellModel.hash()) + } } diff --git a/Sources/DiffableCellKit/EquatableCellModelDataSource.swift b/Sources/DiffableCellKit/EquatableCellModelDataSource.swift index d67d660..ce35d29 100644 --- a/Sources/DiffableCellKit/EquatableCellModelDataSource.swift +++ b/Sources/DiffableCellKit/EquatableCellModelDataSource.swift @@ -7,61 +7,74 @@ // import UIKit -import Dwifft +import DifferenceKit #if SWIFT_PACKAGE import CellKit #endif -public typealias EquatableCellModelSection = GenericCellModelSection +public typealias DifferentiableCellModelSection = GenericCellModelSection + +private typealias DiffSection = ArraySection +private extension DifferentiableCellModelSection { + var arraySection: DiffSection { + return DiffSection(model: identifier, elements: cells.map(DifferentiableCellModelWrapper.init)) + } +} + +extension String: Differentiable { } open class EquatableCellModelDataSource: AbstractDataSource, DataSource { - public var sections: [EquatableCellModelSection] { + private enum Container { + case table(UITableView) + case collection(UICollectionView) + + func reload( using stagedChangeset: StagedChangeset, setData: (C) -> Void) { + switch self { + case .table(let table): + table.reload(using: stagedChangeset, with: .automatic, setData: setData) + case .collection(let collection): + collection.reload(using: stagedChangeset, setData: setData) + } + } + } + + + private let container: Container + + private var _sections: [DifferentiableCellModelSection] + public var sections: [DifferentiableCellModelSection] { get { - return diffCalculator.sectionedValues.sectionsAndValues.map { $0.0 } + return _sections } set { - diffCalculator.sectionedValues = SectionedValues(newValue.map { ($0, $0.cells.map { $0.asEquatable() }) }) + let oldSection = _sections.map { $0.arraySection } + let newSection = newValue.map { $0.arraySection } + let stagedSet = StagedChangeset(source: oldSection, target: newSection) + container.reload(using: stagedSet) { _ in + _sections = newValue + } } } - let diffCalculator: AbstractDiffCalculator - public init(_ tableView: UITableView, sections: [EquatableCellModelSection]) { - self.diffCalculator = TableViewDiffCalculator(tableView: tableView) + public init(_ tableView: UITableView, sections: [DifferentiableCellModelSection]) { + container = .table(tableView) + _sections = [] super.init() self.sections = sections } - public init(_ collectionView: UICollectionView, sections: [EquatableCellModelSection]) { - self.diffCalculator = CollectionViewDiffCalculator(collectionView: collectionView) + public init(_ collectionView: UICollectionView, sections: [DifferentiableCellModelSection]) { + container = .collection(collectionView) + _sections = [] super.init() self.sections = sections } - public subscript(index: Int) -> EquatableCellModelSection { - return diffCalculator.value(forSection: index) - } - - override open func numberOfSections() -> Int { - return self.diffCalculator.numberOfSections() - } - - override open func cellModels(in section: Int) -> [CellModel] { - return self.diffCalculator.value(forSection: section).cells - } - - override open func header(in section: Int) -> SupplementaryViewModel? { - return self.diffCalculator.value(forSection: section).headerView - } - - override open func footer(in section: Int) -> SupplementaryViewModel? { - return self.diffCalculator.value(forSection: section).footerView - } - - override open func cellModel(at indexPath: IndexPath) -> CellModel { - return self.diffCalculator.value(atIndexPath: indexPath).cellModel + public subscript(index: Int) -> DifferentiableCellModelSection { + return sections[index] } } From 2b5f3df71feace5d2bb4e04bfaf10b6245e416db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikola=CC=81s=CC=8C=20Stuchli=CC=81k?= Date: Wed, 11 Sep 2019 10:56:03 +0200 Subject: [PATCH 02/16] Update podfile --- CellKit.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CellKit.podspec b/CellKit.podspec index bd4adda..f6ff20b 100644 --- a/CellKit.podspec +++ b/CellKit.podspec @@ -25,6 +25,6 @@ Pod::Spec.new do |s| s.subspec "Diffable" do |ss| ss.dependency "CellKit/Core" ss.source_files = "Sources/DiffableCellKit/*" - ss.dependency "Dwifft", "~> 0.9" + ss.dependency "DifferenceKit", "~> 2.0" end end From 2375ab48f576a4389b8318227df151f21e6ff15f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikola=CC=81s=CC=8C=20Stuchli=CC=81k?= Date: Wed, 11 Sep 2019 10:57:45 +0200 Subject: [PATCH 03/16] Update podfile --- CellKit.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CellKit.podspec b/CellKit.podspec index f6ff20b..857cf65 100644 --- a/CellKit.podspec +++ b/CellKit.podspec @@ -25,6 +25,6 @@ Pod::Spec.new do |s| s.subspec "Diffable" do |ss| ss.dependency "CellKit/Core" ss.source_files = "Sources/DiffableCellKit/*" - ss.dependency "DifferenceKit", "~> 2.0" + ss.dependency "DifferenceKit", "~> 1.0" end end From b87bcbafded3731fe4f27091cb75613a8712270e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikola=CC=81s=CC=8C=20Stuchli=CC=81k?= Date: Wed, 11 Sep 2019 11:01:02 +0200 Subject: [PATCH 04/16] Update podfile iOS version --- CellKit.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CellKit.podspec b/CellKit.podspec index 857cf65..11c5107 100644 --- a/CellKit.podspec +++ b/CellKit.podspec @@ -11,7 +11,7 @@ Pod::Spec.new do |s| s.license = { type: "MIT", file: "LICENSE" } s.author = { "Matěj K. Jirásek": "matej.jirasek@thefuntasty.com", "Petr Zvoníček": "zvonicek@gmail.com" } s.social_media_url = "https://twitter.com/TheFuntasty" - s.ios.deployment_target = "8.0" + s.ios.deployment_target = "9.0" s.tvos.deployment_target = "9.0" s.swift_versions = ["4.2", "5.0", "5.1"] s.source = { git: "https://github.com/thefuntasty/CellKit.git", tag: s.version.to_s } From 29cc5fb630cfa91ee4b5b47c166675bafe40bb15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikola=CC=81s=CC=8C=20Stuchli=CC=81k?= Date: Wed, 11 Sep 2019 11:09:12 +0200 Subject: [PATCH 05/16] Implement missing methods --- .../EquatableCellModelDataSource.swift | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Sources/DiffableCellKit/EquatableCellModelDataSource.swift b/Sources/DiffableCellKit/EquatableCellModelDataSource.swift index ce35d29..be3b62f 100644 --- a/Sources/DiffableCellKit/EquatableCellModelDataSource.swift +++ b/Sources/DiffableCellKit/EquatableCellModelDataSource.swift @@ -77,4 +77,24 @@ open class EquatableCellModelDataSource: AbstractDataSource, DataSource { public subscript(index: Int) -> DifferentiableCellModelSection { return sections[index] } + + override open func numberOfSections() -> Int { + return sections.count + } + + override open func cellModels(in section: Int) -> [CellModel] { + return sections[section].cells + } + + override open func header(in section: Int) -> SupplementaryViewModel? { + return sections[section].headerView + } + + override open func footer(in section: Int) -> SupplementaryViewModel? { + return sections[section].footerView + } + + override open func cellModel(at indexPath: IndexPath) -> CellModel { + return sections[indexPath.section].cells[indexPath.row] + } } From c45d1f58cce44813b893b5e18881d163bfbb72b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikola=CC=81s=CC=8C=20Stuchli=CC=81k?= Date: Wed, 11 Sep 2019 15:17:16 +0200 Subject: [PATCH 06/16] Implement difference kit --- .../DifferentiableCellModel.swift | 60 +++++++++++++++++++ ...> DifferentiableCellModelDataSource.swift} | 24 +++----- .../DifferentiableSection.swift | 41 +++++++++++++ .../DiffableCellKit/EquatableCellModel.swift | 53 ---------------- 4 files changed, 108 insertions(+), 70 deletions(-) create mode 100644 Sources/DiffableCellKit/DifferentiableCellModel.swift rename Sources/DiffableCellKit/{EquatableCellModelDataSource.swift => DifferentiableCellModelDataSource.swift} (76%) create mode 100644 Sources/DiffableCellKit/DifferentiableSection.swift delete mode 100644 Sources/DiffableCellKit/EquatableCellModel.swift diff --git a/Sources/DiffableCellKit/DifferentiableCellModel.swift b/Sources/DiffableCellKit/DifferentiableCellModel.swift new file mode 100644 index 0000000..96d9871 --- /dev/null +++ b/Sources/DiffableCellKit/DifferentiableCellModel.swift @@ -0,0 +1,60 @@ +// +// DifferentiableCellModel.swift +// +// +// Created by Matěj Kašpar Jirásek on 10/07/2019. +// + +import DifferenceKit +#if SWIFT_PACKAGE +import CellKit +#endif + +public protocol IdentifiableWithinReusableDomain { + var domainIndentifier: Int { get } +} + +public protocol DifferentiableCellModel: CellModel { + var uniqueIdentifier: String { get } + + func hasEqualContent(with other: CellModel) -> Bool +} + +extension DifferentiableCellModel { + func asDifferentaibleBox() -> DifferentiableCellModelWrapper { + return DifferentiableCellModelWrapper(self) + } +} + +extension DifferentiableCellModel where Self: Equatable { + public func hasEqualContent(with other: CellModel) -> Bool { + guard let otherCellModel = other as? Self else { + return false + } + return self == otherCellModel + } +} + +extension DifferentiableCellModel where Self: IdentifiableWithinReusableDomain { + var uniqueIdentifier: String { + return "\(reuseIdentifier)<.>\(domainIndentifier)" + } +} + +struct DifferentiableCellModelWrapper { + let cellModel: DifferentiableCellModel + + init(_ cellModel: DifferentiableCellModel) { + self.cellModel = cellModel + } +} + +extension DifferentiableCellModelWrapper: Equatable, Differentiable { + static func == (lhs: DifferentiableCellModelWrapper, rhs: DifferentiableCellModelWrapper) -> Bool { + return lhs.cellModel.hasEqualContent(with: rhs.cellModel) + } + + var differenceIdentifier: String { + return cellModel.uniqueIdentifier + } +} diff --git a/Sources/DiffableCellKit/EquatableCellModelDataSource.swift b/Sources/DiffableCellKit/DifferentiableCellModelDataSource.swift similarity index 76% rename from Sources/DiffableCellKit/EquatableCellModelDataSource.swift rename to Sources/DiffableCellKit/DifferentiableCellModelDataSource.swift index be3b62f..0419956 100644 --- a/Sources/DiffableCellKit/EquatableCellModelDataSource.swift +++ b/Sources/DiffableCellKit/DifferentiableCellModelDataSource.swift @@ -1,5 +1,5 @@ // -// CellModelManager.swift +// DifferentiableCellModelDataSource.swift // Example // // Created by Petr Zvoníček on 08.06.18. @@ -12,19 +12,8 @@ import DifferenceKit import CellKit #endif -public typealias DifferentiableCellModelSection = GenericCellModelSection - -private typealias DiffSection = ArraySection -private extension DifferentiableCellModelSection { - var arraySection: DiffSection { - return DiffSection(model: identifier, elements: cells.map(DifferentiableCellModelWrapper.init)) - } -} - -extension String: Differentiable { } - -open class EquatableCellModelDataSource: AbstractDataSource, DataSource { - +open class DifferentiableCellModelDataSource: AbstractDataSource, DataSource { + private enum Container { case table(UITableView) case collection(UICollectionView) @@ -50,9 +39,10 @@ open class EquatableCellModelDataSource: AbstractDataSource, DataSource { set { let oldSection = _sections.map { $0.arraySection } let newSection = newValue.map { $0.arraySection } - let stagedSet = StagedChangeset(source: oldSection, target: newSection) - container.reload(using: stagedSet) { _ in - _sections = newValue + let stagedSet = StagedChangeset.init(source: oldSection, target: newSection) + container.reload(using: stagedSet) { data in + let sections = data.map { DifferentiableCellModelSection(cells: $0.elements.map { $0.cellModel }, headerView: $0.model.headerView, footerView: $0.model.footerView, identifier: $0.model.identifier) } + _sections = sections } } } diff --git a/Sources/DiffableCellKit/DifferentiableSection.swift b/Sources/DiffableCellKit/DifferentiableSection.swift new file mode 100644 index 0000000..3250a96 --- /dev/null +++ b/Sources/DiffableCellKit/DifferentiableSection.swift @@ -0,0 +1,41 @@ +// +// DifferentiableSection.swift +// +// +// Created by Mikoláš Stuchlík on 11/09/2019. +// + +import DifferenceKit +#if SWIFT_PACKAGE +import CellKit +#endif + +struct SectionMetadata: ContentEquatable, Differentiable { + func isContentEqual(to source: SectionMetadata) -> Bool { + return identifier == identifier + } + + var differenceIdentifier: String { + return identifier + } + + var headerView: SupplementaryViewModel? + var footerView: SupplementaryViewModel? + let identifier: String + + init(from section: GenericCellModelSection) { + headerView = section.headerView + footerView = section.footerView + identifier = section.identifier + } +} + +public typealias DifferentiableCellModelSection = GenericCellModelSection + +extension DifferentiableCellModelSection { + var arraySection: ArraySection { + return DiffSection(model: SectionMetadata(from: self), elements: self.cells.map(DifferentiableCellModelWrapper.init)) + } +} + +typealias DiffSection = ArraySection diff --git a/Sources/DiffableCellKit/EquatableCellModel.swift b/Sources/DiffableCellKit/EquatableCellModel.swift deleted file mode 100644 index 6fa2e20..0000000 --- a/Sources/DiffableCellKit/EquatableCellModel.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// File.swift -// -// -// Created by Matěj Kašpar Jirásek on 10/07/2019. -// - -import DifferenceKit -#if SWIFT_PACKAGE -import CellKit -#endif - -public protocol DifferentiableCellModel: CellModel { - func isEqual(to other: CellModel) -> Bool - func hash() -> Int -} - -extension DifferentiableCellModel { - func asEquatable() -> DifferentiableCellModelWrapper { - return DifferentiableCellModelWrapper(self) - } -} - -extension DifferentiableCellModel where Self: Hashable { - public func isEqual(to other: CellModel) -> Bool { - guard let otherCellModel = other as? Self else { - return false - } - return self == otherCellModel - } - - public func hash() -> Int { - return self.hashValue - } -} - -struct DifferentiableCellModelWrapper { - let cellModel: DifferentiableCellModel - - init(_ cellModel: DifferentiableCellModel) { - self.cellModel = cellModel - } -} - -extension DifferentiableCellModelWrapper: Hashable, Differentiable { - static func == (lhs: DifferentiableCellModelWrapper, rhs: DifferentiableCellModelWrapper) -> Bool { - return lhs.cellModel.isEqual(to: rhs.cellModel) - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(cellModel.hash()) - } -} From 0324b3e66ea89fa5540b4b6c059ea73425285652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikola=CC=81s=CC=8C=20Stuchli=CC=81k?= Date: Wed, 11 Sep 2019 15:32:36 +0200 Subject: [PATCH 07/16] Refactor protocols --- .../DiffableCellKit/DifferentiableCellModel.swift | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/Sources/DiffableCellKit/DifferentiableCellModel.swift b/Sources/DiffableCellKit/DifferentiableCellModel.swift index 96d9871..c801255 100644 --- a/Sources/DiffableCellKit/DifferentiableCellModel.swift +++ b/Sources/DiffableCellKit/DifferentiableCellModel.swift @@ -10,12 +10,8 @@ import DifferenceKit import CellKit #endif -public protocol IdentifiableWithinReusableDomain { - var domainIndentifier: Int { get } -} - public protocol DifferentiableCellModel: CellModel { - var uniqueIdentifier: String { get } + var domainIndentifier: Int { get } func hasEqualContent(with other: CellModel) -> Bool } @@ -35,12 +31,6 @@ extension DifferentiableCellModel where Self: Equatable { } } -extension DifferentiableCellModel where Self: IdentifiableWithinReusableDomain { - var uniqueIdentifier: String { - return "\(reuseIdentifier)<.>\(domainIndentifier)" - } -} - struct DifferentiableCellModelWrapper { let cellModel: DifferentiableCellModel @@ -55,6 +45,6 @@ extension DifferentiableCellModelWrapper: Equatable, Differentiable { } var differenceIdentifier: String { - return cellModel.uniqueIdentifier + return "\(cellModel.reuseIdentifier)<.>\(cellModel.domainIndentifier)" } } From 75922621892ede5c4b2668856d7b1bfe4f3bd487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikola=CC=81s=CC=8C=20Stuchli=CC=81k?= Date: Wed, 11 Sep 2019 16:00:37 +0200 Subject: [PATCH 08/16] Add documentation --- Sources/DiffableCellKit/DifferentiableCellModel.swift | 7 +++++++ Sources/DiffableCellKit/DifferentiableSection.swift | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Sources/DiffableCellKit/DifferentiableCellModel.swift b/Sources/DiffableCellKit/DifferentiableCellModel.swift index c801255..8bac20a 100644 --- a/Sources/DiffableCellKit/DifferentiableCellModel.swift +++ b/Sources/DiffableCellKit/DifferentiableCellModel.swift @@ -10,9 +10,16 @@ import DifferenceKit import CellKit #endif + +/// Support for determining whether cell model belongs to a cretain cell, whether cell should be inserted, removed, updated or moved. public protocol DifferentiableCellModel: CellModel { + + /// Identifier of a cell model inside it's own domain determined by reusable identifier. Assuming model is already presented inside a view, changing of this value will result in it's removal and insertion into the view. var domainIndentifier: Int { get } + + /// This method return true, whenever content of a view model is equal to content of other view model within it's domain. However, argument may contain view model from different domain. If cell model conforms to protocol Equatable, default implementation is provided. + /// - Parameter other: other cell model to compare with - DO NOT FORGET to cast this argument to your view model's type func hasEqualContent(with other: CellModel) -> Bool } diff --git a/Sources/DiffableCellKit/DifferentiableSection.swift b/Sources/DiffableCellKit/DifferentiableSection.swift index 3250a96..7fe06f4 100644 --- a/Sources/DiffableCellKit/DifferentiableSection.swift +++ b/Sources/DiffableCellKit/DifferentiableSection.swift @@ -10,6 +10,8 @@ import DifferenceKit import CellKit #endif +public typealias DifferentiableCellModelSection = GenericCellModelSection + struct SectionMetadata: ContentEquatable, Differentiable { func isContentEqual(to source: SectionMetadata) -> Bool { return identifier == identifier @@ -30,8 +32,6 @@ struct SectionMetadata: ContentEquatable, Differentiable { } } -public typealias DifferentiableCellModelSection = GenericCellModelSection - extension DifferentiableCellModelSection { var arraySection: ArraySection { return DiffSection(model: SectionMetadata(from: self), elements: self.cells.map(DifferentiableCellModelWrapper.init)) From 0e3e5a465ed945e036140998d9501f949a55311b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikola=CC=81s=CC=8C=20Stuchli=CC=81k?= Date: Thu, 7 Nov 2019 12:22:11 +0100 Subject: [PATCH 09/16] Apple PR suggestions --- Package.swift | 2 +- Sources/DiffableCellKit/DifferentiableCellModel.swift | 10 +++------- .../DifferentiableCellModelDataSource.swift | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Package.swift b/Package.swift index e3a37e9..3a1602e 100644 --- a/Package.swift +++ b/Package.swift @@ -14,7 +14,7 @@ let package = Package( targets: ["DiffableCellKit"]) ], dependencies: [ - .package(url: "https://github.com/ra1028/DifferenceKit.git", from: "1.0.0") + .package(url: "https://github.com/ra1028/DifferenceKit.git", from: "1.0.0") ], targets: [ .target( diff --git a/Sources/DiffableCellKit/DifferentiableCellModel.swift b/Sources/DiffableCellKit/DifferentiableCellModel.swift index 8bac20a..fae8822 100644 --- a/Sources/DiffableCellKit/DifferentiableCellModel.swift +++ b/Sources/DiffableCellKit/DifferentiableCellModel.swift @@ -11,7 +11,7 @@ import CellKit #endif -/// Support for determining whether cell model belongs to a cretain cell, whether cell should be inserted, removed, updated or moved. +/// Support for determining whether cell model belongs to a certain cell, whether cell should be inserted, removed, updated or moved. public protocol DifferentiableCellModel: CellModel { /// Identifier of a cell model inside it's own domain determined by reusable identifier. Assuming model is already presented inside a view, changing of this value will result in it's removal and insertion into the view. @@ -24,8 +24,8 @@ public protocol DifferentiableCellModel: CellModel { } extension DifferentiableCellModel { - func asDifferentaibleBox() -> DifferentiableCellModelWrapper { - return DifferentiableCellModelWrapper(self) + func asDifferentaibleWrapper() -> DifferentiableCellModelWrapper { + return DifferentiableCellModelWrapper(cellModel: self) } } @@ -40,10 +40,6 @@ extension DifferentiableCellModel where Self: Equatable { struct DifferentiableCellModelWrapper { let cellModel: DifferentiableCellModel - - init(_ cellModel: DifferentiableCellModel) { - self.cellModel = cellModel - } } extension DifferentiableCellModelWrapper: Equatable, Differentiable { diff --git a/Sources/DiffableCellKit/DifferentiableCellModelDataSource.swift b/Sources/DiffableCellKit/DifferentiableCellModelDataSource.swift index 0419956..4d5d45f 100644 --- a/Sources/DiffableCellKit/DifferentiableCellModelDataSource.swift +++ b/Sources/DiffableCellKit/DifferentiableCellModelDataSource.swift @@ -39,7 +39,7 @@ open class DifferentiableCellModelDataSource: AbstractDataSource, DataSource { set { let oldSection = _sections.map { $0.arraySection } let newSection = newValue.map { $0.arraySection } - let stagedSet = StagedChangeset.init(source: oldSection, target: newSection) + let stagedSet = StagedChangeset(source: oldSection, target: newSection) container.reload(using: stagedSet) { data in let sections = data.map { DifferentiableCellModelSection(cells: $0.elements.map { $0.cellModel }, headerView: $0.model.headerView, footerView: $0.model.footerView, identifier: $0.model.identifier) } _sections = sections From 0949a29a94194b2e13344ed444efdf4f18de1066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikola=CC=81s=CC=8C=20Stuchli=CC=81k?= Date: Thu, 7 Nov 2019 13:10:05 +0100 Subject: [PATCH 10/16] Fix example project for DifferenceKit --- Example/Example.xcodeproj/project.pbxproj | 68 +++---- .../AppIcon.appiconset/Contents.json | 98 ---------- Example/Sources/Assets.xcassets/Contents.json | 6 - .../Base.lproj/LaunchScreen.storyboard | 25 --- Example/Sources/Base.lproj/Main.storyboard | 178 ++++++++---------- .../Cells/DeviceAndroidCellModel.swift | 13 +- .../Sources/Cells/DeviceiOSCellModel.swift | 13 +- Example/Sources/Cells/NibTableViewCell.swift | 14 +- Example/Sources/Info.plist | 4 +- Example/Sources/Storyboard.storyboard | 56 ++++++ Example/Sources/ViewController.swift | 29 +-- 11 files changed, 214 insertions(+), 290 deletions(-) delete mode 100644 Example/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 Example/Sources/Assets.xcassets/Contents.json delete mode 100644 Example/Sources/Base.lproj/LaunchScreen.storyboard create mode 100644 Example/Sources/Storyboard.storyboard diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index b475010..318a0c7 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -7,9 +7,9 @@ objects = { /* Begin PBXBuildFile section */ - E72976192313B07A008B6E2F /* CellKit in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E72976182313B04B008B6E2F /* CellKit */; }; - E729761B2313B089008B6E2F /* CellKit in Frameworks */ = {isa = PBXBuildFile; productRef = E729761A2313B089008B6E2F /* CellKit */; }; - E729761D2313B089008B6E2F /* DiffableCellKit in Frameworks */ = {isa = PBXBuildFile; productRef = E729761C2313B089008B6E2F /* DiffableCellKit */; }; + BEFF59F22374373900B3A342 /* CellKit in Frameworks */ = {isa = PBXBuildFile; productRef = BEFF59F12374373900B3A342 /* CellKit */; }; + BEFF59F42374373D00B3A342 /* DiffableCellKit in Frameworks */ = {isa = PBXBuildFile; productRef = BEFF59F32374373D00B3A342 /* DiffableCellKit */; }; + BEFF59F623743F1000B3A342 /* Storyboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BEFF59F523743F1000B3A342 /* Storyboard.storyboard */; }; E7BB267522D4B63800A7713E /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7BB266322D4B63800A7713E /* ViewController.swift */; }; E7BB267622D4B63800A7713E /* DeviceAndroidCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7BB266522D4B63800A7713E /* DeviceAndroidCell.swift */; }; E7BB267722D4B63800A7713E /* NibTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E7BB266622D4B63800A7713E /* NibTableViewCell.xib */; }; @@ -17,13 +17,11 @@ E7BB267922D4B63800A7713E /* DeviceAndroidCellModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7BB266822D4B63800A7713E /* DeviceAndroidCellModel.swift */; }; E7BB267A22D4B63800A7713E /* DeviceiOSCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7BB266922D4B63800A7713E /* DeviceiOSCell.swift */; }; E7BB267B22D4B63800A7713E /* DeviceiOSCellModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7BB266A22D4B63800A7713E /* DeviceiOSCellModel.swift */; }; - E7BB267C22D4B63800A7713E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E7BB266B22D4B63800A7713E /* LaunchScreen.storyboard */; }; E7BB267D22D4B63800A7713E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E7BB266D22D4B63800A7713E /* Main.storyboard */; }; E7BB267E22D4B63800A7713E /* PhonesHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = E7BB267022D4B63800A7713E /* PhonesHeader.xib */; }; E7BB267F22D4B63800A7713E /* PhonesHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7BB267122D4B63800A7713E /* PhonesHeader.swift */; }; E7BB268022D4B63800A7713E /* PhonesHeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7BB267222D4B63800A7713E /* PhonesHeaderModel.swift */; }; E7BB268122D4B63800A7713E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7BB267322D4B63800A7713E /* AppDelegate.swift */; }; - E7BB268422D4B65600A7713E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E7BB268322D4B65600A7713E /* Assets.xcassets */; }; E7BCF6BE22DF145F00E71478 /* ExampleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7BCF6BD22DF145F00E71478 /* ExampleUITests.swift */; }; /* End PBXBuildFile section */ @@ -44,7 +42,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - E72976192313B07A008B6E2F /* CellKit in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -52,8 +49,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + BEFF59EF2374371D00B3A342 /* CellKit */ = {isa = PBXFileReference; lastKnownFileType = folder; name = CellKit; path = ..; sourceTree = ""; }; + BEFF59F523743F1000B3A342 /* Storyboard.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Storyboard.storyboard; sourceTree = ""; }; D02135A420CA609D00EE79D1 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; - E72976182313B04B008B6E2F /* CellKit */ = {isa = PBXFileReference; lastKnownFileType = folder; name = CellKit; path = ../..; sourceTree = ""; }; E7BB266322D4B63800A7713E /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; E7BB266522D4B63800A7713E /* DeviceAndroidCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceAndroidCell.swift; sourceTree = ""; }; E7BB266622D4B63800A7713E /* NibTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NibTableViewCell.xib; sourceTree = ""; }; @@ -61,14 +59,12 @@ E7BB266822D4B63800A7713E /* DeviceAndroidCellModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceAndroidCellModel.swift; sourceTree = ""; }; E7BB266922D4B63800A7713E /* DeviceiOSCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceiOSCell.swift; sourceTree = ""; }; E7BB266A22D4B63800A7713E /* DeviceiOSCellModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceiOSCellModel.swift; sourceTree = ""; }; - E7BB266C22D4B63800A7713E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; E7BB266E22D4B63800A7713E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; E7BB267022D4B63800A7713E /* PhonesHeader.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PhonesHeader.xib; sourceTree = ""; }; E7BB267122D4B63800A7713E /* PhonesHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhonesHeader.swift; sourceTree = ""; }; E7BB267222D4B63800A7713E /* PhonesHeaderModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhonesHeaderModel.swift; sourceTree = ""; }; E7BB267322D4B63800A7713E /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; E7BB267422D4B63800A7713E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - E7BB268322D4B65600A7713E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; E7BCF6BB22DF145F00E71478 /* ExampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; E7BCF6BD22DF145F00E71478 /* ExampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleUITests.swift; sourceTree = ""; }; E7BCF6BF22DF145F00E71478 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -79,8 +75,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - E729761D2313B089008B6E2F /* DiffableCellKit in Frameworks */, - E729761B2313B089008B6E2F /* CellKit in Frameworks */, + BEFF59F42374373D00B3A342 /* DiffableCellKit in Frameworks */, + BEFF59F22374373900B3A342 /* CellKit in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -100,7 +96,8 @@ E7BCF6C522DF146B00E71478 /* Tests */, E7BB266222D4B63800A7713E /* Sources */, 52D6D97D1BEFF229002C0205 /* Products */, - E7BB269B22D5D42D00A7713E /* Frameworks */, + BEFF59EF2374371D00B3A342 /* CellKit */, + BEFF59F02374373900B3A342 /* Frameworks */, ); sourceTree = ""; }; @@ -113,18 +110,23 @@ name = Products; sourceTree = ""; }; + BEFF59F02374373900B3A342 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; E7BB266222D4B63800A7713E /* Sources */ = { isa = PBXGroup; children = ( - E72976182313B04B008B6E2F /* CellKit */, - E7BB266322D4B63800A7713E /* ViewController.swift */, + E7BB266F22D4B63800A7713E /* Headers */, E7BB266422D4B63800A7713E /* Cells */, - E7BB266B22D4B63800A7713E /* LaunchScreen.storyboard */, + E7BB266322D4B63800A7713E /* ViewController.swift */, E7BB266D22D4B63800A7713E /* Main.storyboard */, - E7BB266F22D4B63800A7713E /* Headers */, E7BB267322D4B63800A7713E /* AppDelegate.swift */, E7BB267422D4B63800A7713E /* Info.plist */, - E7BB268322D4B65600A7713E /* Assets.xcassets */, + BEFF59F523743F1000B3A342 /* Storyboard.storyboard */, ); path = Sources; sourceTree = ""; @@ -152,13 +154,6 @@ path = Headers; sourceTree = ""; }; - E7BB269B22D5D42D00A7713E /* Frameworks */ = { - isa = PBXGroup; - children = ( - ); - name = Frameworks; - sourceTree = ""; - }; E7BCF6BC22DF145F00E71478 /* ExampleUITests */ = { isa = PBXGroup; children = ( @@ -194,8 +189,8 @@ ); name = Example; packageProductDependencies = ( - E729761A2313B089008B6E2F /* CellKit */, - E729761C2313B089008B6E2F /* DiffableCellKit */, + BEFF59F12374373900B3A342 /* CellKit */, + BEFF59F32374373D00B3A342 /* DiffableCellKit */, ); productName = Example; productReference = D02135A420CA609D00EE79D1 /* Example.app */; @@ -265,9 +260,8 @@ buildActionMask = 2147483647; files = ( E7BB267D22D4B63800A7713E /* Main.storyboard in Resources */, + BEFF59F623743F1000B3A342 /* Storyboard.storyboard in Resources */, E7BB267722D4B63800A7713E /* NibTableViewCell.xib in Resources */, - E7BB267C22D4B63800A7713E /* LaunchScreen.storyboard in Resources */, - E7BB268422D4B65600A7713E /* Assets.xcassets in Resources */, E7BB267E22D4B63800A7713E /* PhonesHeader.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -317,14 +311,6 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ - E7BB266B22D4B63800A7713E /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - E7BB266C22D4B63800A7713E /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; E7BB266D22D4B63800A7713E /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( @@ -452,7 +438,6 @@ D02135B420CA609F00EE79D1 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -476,14 +461,13 @@ PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; D02135B520CA609F00EE79D1 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -508,7 +492,7 @@ SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -603,11 +587,11 @@ /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ - E729761A2313B089008B6E2F /* CellKit */ = { + BEFF59F12374373900B3A342 /* CellKit */ = { isa = XCSwiftPackageProductDependency; productName = CellKit; }; - E729761C2313B089008B6E2F /* DiffableCellKit */ = { + BEFF59F32374373D00B3A342 /* DiffableCellKit */ = { isa = XCSwiftPackageProductDependency; productName = DiffableCellKit; }; diff --git a/Example/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json b/Example/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d8db8d6..0000000 --- a/Example/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - }, - { - "idiom" : "ipad", - "size" : "20x20", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "20x20", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "83.5x83.5", - "scale" : "2x" - }, - { - "idiom" : "ios-marketing", - "size" : "1024x1024", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/Sources/Assets.xcassets/Contents.json b/Example/Sources/Assets.xcassets/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/Example/Sources/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Example/Sources/Base.lproj/LaunchScreen.storyboard b/Example/Sources/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index f83f6fd..0000000 --- a/Example/Sources/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Example/Sources/Base.lproj/Main.storyboard b/Example/Sources/Base.lproj/Main.storyboard index 1209c29..38ea23a 100644 --- a/Example/Sources/Base.lproj/Main.storyboard +++ b/Example/Sources/Base.lproj/Main.storyboard @@ -1,139 +1,119 @@ - - - - + + - - - + - + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - - - - + + - + + - - + + - + - diff --git a/Example/Sources/Cells/DeviceAndroidCellModel.swift b/Example/Sources/Cells/DeviceAndroidCellModel.swift index 34f2f6a..ff88e03 100644 --- a/Example/Sources/Cells/DeviceAndroidCellModel.swift +++ b/Example/Sources/Cells/DeviceAndroidCellModel.swift @@ -10,7 +10,18 @@ import UIKit import CellKit import DiffableCellKit -struct DeviceAndroidCellModel: CellConvertible, EquatableCellModel, Equatable { +struct DeviceAndroidCellModel: CellConvertible, DifferentiableCellModel { + var domainIndentifier: Int { + return name.hashValue + } + + func hasEqualContent(with other: CellModel) -> Bool { + guard let other = other as? DeviceAndroidCellModel else { + return false + } + return other.name == self.name + } + typealias Cell = DeviceAndroidCell let name: String diff --git a/Example/Sources/Cells/DeviceiOSCellModel.swift b/Example/Sources/Cells/DeviceiOSCellModel.swift index 993e10f..ab6488a 100644 --- a/Example/Sources/Cells/DeviceiOSCellModel.swift +++ b/Example/Sources/Cells/DeviceiOSCellModel.swift @@ -10,7 +10,18 @@ import UIKit import CellKit import DiffableCellKit -struct DeviceiOSCellModel: CellConvertible, EquatableCellModel, Equatable { +struct DeviceiOSCellModel: CellConvertible, DifferentiableCellModel { + var domainIndentifier: Int { + return name.hashValue + } + + func hasEqualContent(with other: CellModel) -> Bool { + guard let other = other as? DeviceiOSCellModel else { + return false + } + return other.name == self.name + } + typealias Cell = DeviceiOSCell let name: String diff --git a/Example/Sources/Cells/NibTableViewCell.swift b/Example/Sources/Cells/NibTableViewCell.swift index aa8e3fa..afe645a 100644 --- a/Example/Sources/Cells/NibTableViewCell.swift +++ b/Example/Sources/Cells/NibTableViewCell.swift @@ -10,7 +10,19 @@ import UIKit import CellKit import DiffableCellKit -struct NibTableViewCellModel: ReusableCellConvertible, EquatableCellModel, Equatable { +struct NibTableViewCellModel: ReusableCellConvertible, DifferentiableCellModel { + var domainIndentifier: Int { + return text.hashValue + } + + func hasEqualContent(with other: CellModel) -> Bool { + guard let other = other as? NibTableViewCellModel else { + return false + } + + return other.text == self.text + } + typealias Cell = NibTableViewCell let text: String diff --git a/Example/Sources/Info.plist b/Example/Sources/Info.plist index 16be3b6..39efe35 100644 --- a/Example/Sources/Info.plist +++ b/Example/Sources/Info.plist @@ -21,7 +21,7 @@ LSRequiresIPhoneOS UILaunchStoryboardName - LaunchScreen + Storyboard UIMainStoryboardFile Main UIRequiredDeviceCapabilities @@ -31,8 +31,6 @@ UISupportedInterfaceOrientations UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad diff --git a/Example/Sources/Storyboard.storyboard b/Example/Sources/Storyboard.storyboard new file mode 100644 index 0000000..e8f835c --- /dev/null +++ b/Example/Sources/Storyboard.storyboard @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/Sources/ViewController.swift b/Example/Sources/ViewController.swift index eb30983..bd1a0a5 100644 --- a/Example/Sources/ViewController.swift +++ b/Example/Sources/ViewController.swift @@ -10,29 +10,30 @@ import UIKit import CellKit import DiffableCellKit -final class ViewController: UIViewController { +enum Constants { - @IBOutlet private var tableView: UITableView! + static var iPhones = ["iPhone", "iPhone 3G", "iPhone 3GS", "iPhone 4", "iPhone 4S", "iPhone 5", "iPhone 5s", "iPhone 6", "iPhone 6s", "iPhone SE", "iPhone 7", "iPhone 8", "iPhone X"] + static var androids = ["Samsung Galaxy Note", "OnePlus 2", "Nexus 6", "Huawei P9", "Kindle Fire", "Samsung Galaxy S6", "Nexus 5"] +} - private var dataSource: EquatableCellModelDataSource! +final class ViewController: UITableViewController { - private var iPhones = ["iPhone", "iPhone 3G", "iPhone 3GS", "iPhone 4", "iPhone 4S", "iPhone 5", "iPhone 5s", "iPhone 6", "iPhone 6s", "iPhone SE", "iPhone 7", "iPhone 8", "iPhone X"] - private var androids = ["Samsung Galaxy Note", "OnePlus 2", "Nexus 6", "Huawei P9", "Kindle Fire", "Samsung Galaxy S6", "Nexus 5"] + private var dataSource: DifferentiableCellModelDataSource! - private var phonesSection: EquatableCellModelSection { - let iPhoneCellModels: [EquatableCellModel] = iPhones.prefix(5).map { DeviceiOSCellModel(name: $0) } - let androidCellModels: [EquatableCellModel] = androids.prefix(5).map { DeviceAndroidCellModel(name: $0) } + private var phonesSection: DifferentiableCellModelSection { + let iPhoneCellModels: [DifferentiableCellModel] = Constants.iPhones.prefix(5).map { DeviceiOSCellModel(name: $0) } + let androidCellModels: [DifferentiableCellModel] = Constants.androids.prefix(5).map { DeviceAndroidCellModel(name: $0) } let cellModels = iPhoneCellModels + androidCellModels let header = PhonesHeaderModel(title: "Cell Phones") - return EquatableCellModelSection(cells: cellModels, headerView: header, identifier: "Section 1") + return DifferentiableCellModelSection(cells: cellModels, headerView: header, identifier: "Section 1") } - private var welcomeSection: EquatableCellModelSection { + private var welcomeSection: DifferentiableCellModelSection { return [NibTableViewCellModel(text: "Welcome!")] } - private var defaultSections: [EquatableCellModelSection] { + private var defaultSections: [DifferentiableCellModelSection] { return [ welcomeSection, phonesSection @@ -42,7 +43,7 @@ final class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - dataSource = EquatableCellModelDataSource(tableView, sections: defaultSections) + dataSource = DifferentiableCellModelDataSource(tableView, sections: defaultSections) dataSource.delegate = self tableView.dataSource = dataSource tableView.delegate = dataSource @@ -57,7 +58,7 @@ final class ViewController: UIViewController { .compactMap { $0 as? DeviceiOSCellModel } .compactMap { $0.name } ?? [] - let available = Set(iPhones).subtracting(Set(visibleItems)) + let available = Set(Constants.iPhones).subtracting(Set(visibleItems)) if let item = available.first { dataSource?.sections[1].cells.insert(DeviceiOSCellModel(name: item), at: 0) } @@ -68,7 +69,7 @@ final class ViewController: UIViewController { .compactMap { $0 as? DeviceAndroidCellModel } .compactMap { $0.name } ?? [] - let available = Set(androids).subtracting(Set(visibleItems)) + let available = Set(Constants.androids).subtracting(Set(visibleItems)) if let item = available.first { dataSource?.sections[1].cells.insert(DeviceAndroidCellModel(name: item), at: 0) } From b0f06299745638063200e2640b8674d0c7c3b0b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikola=CC=81s=CC=8C=20Stuchli=CC=81k?= Date: Thu, 7 Nov 2019 14:21:11 +0100 Subject: [PATCH 11/16] Expand example app --- Example/Example.xcodeproj/project.pbxproj | 58 ++++++++++++++----- .../ActionSensitiveDiffDataSource.swift | 38 ++++++++++++ .../{ => Supporting}/AppDelegate.swift | 0 .../Launch.storyboard} | 0 .../AndroidCell}/DeviceAndroidCell.swift | 2 +- .../AndroidCell}/DeviceAndroidCellModel.swift | 7 ++- .../IOSCell}/DeviceiOSCell.swift | 2 +- .../IOSCell}/DeviceiOSCellModel.swift | 7 ++- .../NibTableViewCell.swift | 0 .../NibTableViewCell.xib | 0 .../PhonesHeader.swift | 0 .../PhonesHeader.xib | 0 .../PhonesHeaderModel.swift | 0 Example/Sources/ViewController.swift | 34 ++++++++--- 14 files changed, 119 insertions(+), 29 deletions(-) create mode 100644 Example/Sources/ActionSensitiveDiffDataSource.swift rename Example/Sources/{ => Supporting}/AppDelegate.swift (100%) rename Example/Sources/{Storyboard.storyboard => Supporting/Launch.storyboard} (100%) rename Example/Sources/{Cells => Table Cells/AndroidCell}/DeviceAndroidCell.swift (84%) rename Example/Sources/{Cells => Table Cells/AndroidCell}/DeviceAndroidCellModel.swift (82%) rename Example/Sources/{Cells => Table Cells/IOSCell}/DeviceiOSCell.swift (83%) rename Example/Sources/{Cells => Table Cells/IOSCell}/DeviceiOSCellModel.swift (81%) rename Example/Sources/{Cells => Table Cells}/NibTableViewCell.swift (100%) rename Example/Sources/{Cells => Table Cells}/NibTableViewCell.xib (100%) rename Example/Sources/{Headers => Table Headers}/PhonesHeader.swift (100%) rename Example/Sources/{Headers => Table Headers}/PhonesHeader.xib (100%) rename Example/Sources/{Headers => Table Headers}/PhonesHeaderModel.swift (100%) diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index 318a0c7..8174cc8 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -9,7 +9,8 @@ /* Begin PBXBuildFile section */ BEFF59F22374373900B3A342 /* CellKit in Frameworks */ = {isa = PBXBuildFile; productRef = BEFF59F12374373900B3A342 /* CellKit */; }; BEFF59F42374373D00B3A342 /* DiffableCellKit in Frameworks */ = {isa = PBXBuildFile; productRef = BEFF59F32374373D00B3A342 /* DiffableCellKit */; }; - BEFF59F623743F1000B3A342 /* Storyboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BEFF59F523743F1000B3A342 /* Storyboard.storyboard */; }; + BEFF59F623743F1000B3A342 /* Launch.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BEFF59F523743F1000B3A342 /* Launch.storyboard */; }; + BEFF59FB23744D1C00B3A342 /* ActionSensitiveDiffDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEFF59FA23744D1C00B3A342 /* ActionSensitiveDiffDataSource.swift */; }; E7BB267522D4B63800A7713E /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7BB266322D4B63800A7713E /* ViewController.swift */; }; E7BB267622D4B63800A7713E /* DeviceAndroidCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7BB266522D4B63800A7713E /* DeviceAndroidCell.swift */; }; E7BB267722D4B63800A7713E /* NibTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E7BB266622D4B63800A7713E /* NibTableViewCell.xib */; }; @@ -50,7 +51,8 @@ /* Begin PBXFileReference section */ BEFF59EF2374371D00B3A342 /* CellKit */ = {isa = PBXFileReference; lastKnownFileType = folder; name = CellKit; path = ..; sourceTree = ""; }; - BEFF59F523743F1000B3A342 /* Storyboard.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Storyboard.storyboard; sourceTree = ""; }; + BEFF59F523743F1000B3A342 /* Launch.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Launch.storyboard; sourceTree = ""; }; + BEFF59FA23744D1C00B3A342 /* ActionSensitiveDiffDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionSensitiveDiffDataSource.swift; sourceTree = ""; }; D02135A420CA609D00EE79D1 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; E7BB266322D4B63800A7713E /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; E7BB266522D4B63800A7713E /* DeviceAndroidCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceAndroidCell.swift; sourceTree = ""; }; @@ -117,41 +119,66 @@ name = Frameworks; sourceTree = ""; }; + BEFF59F7237448BC00B3A342 /* AndroidCell */ = { + isa = PBXGroup; + children = ( + E7BB266522D4B63800A7713E /* DeviceAndroidCell.swift */, + E7BB266822D4B63800A7713E /* DeviceAndroidCellModel.swift */, + ); + path = AndroidCell; + sourceTree = ""; + }; + BEFF59F8237448C300B3A342 /* IOSCell */ = { + isa = PBXGroup; + children = ( + E7BB266922D4B63800A7713E /* DeviceiOSCell.swift */, + E7BB266A22D4B63800A7713E /* DeviceiOSCellModel.swift */, + ); + path = IOSCell; + sourceTree = ""; + }; + BEFF59F923744CE700B3A342 /* Supporting */ = { + isa = PBXGroup; + children = ( + E7BB267322D4B63800A7713E /* AppDelegate.swift */, + BEFF59F523743F1000B3A342 /* Launch.storyboard */, + ); + path = Supporting; + sourceTree = ""; + }; E7BB266222D4B63800A7713E /* Sources */ = { isa = PBXGroup; children = ( - E7BB266F22D4B63800A7713E /* Headers */, - E7BB266422D4B63800A7713E /* Cells */, + E7BB266F22D4B63800A7713E /* Table Headers */, + E7BB266422D4B63800A7713E /* Table Cells */, + BEFF59FA23744D1C00B3A342 /* ActionSensitiveDiffDataSource.swift */, E7BB266322D4B63800A7713E /* ViewController.swift */, E7BB266D22D4B63800A7713E /* Main.storyboard */, - E7BB267322D4B63800A7713E /* AppDelegate.swift */, E7BB267422D4B63800A7713E /* Info.plist */, - BEFF59F523743F1000B3A342 /* Storyboard.storyboard */, + BEFF59F923744CE700B3A342 /* Supporting */, ); path = Sources; sourceTree = ""; }; - E7BB266422D4B63800A7713E /* Cells */ = { + E7BB266422D4B63800A7713E /* Table Cells */ = { isa = PBXGroup; children = ( - E7BB266522D4B63800A7713E /* DeviceAndroidCell.swift */, E7BB266622D4B63800A7713E /* NibTableViewCell.xib */, E7BB266722D4B63800A7713E /* NibTableViewCell.swift */, - E7BB266822D4B63800A7713E /* DeviceAndroidCellModel.swift */, - E7BB266922D4B63800A7713E /* DeviceiOSCell.swift */, - E7BB266A22D4B63800A7713E /* DeviceiOSCellModel.swift */, + BEFF59F7237448BC00B3A342 /* AndroidCell */, + BEFF59F8237448C300B3A342 /* IOSCell */, ); - path = Cells; + path = "Table Cells"; sourceTree = ""; }; - E7BB266F22D4B63800A7713E /* Headers */ = { + E7BB266F22D4B63800A7713E /* Table Headers */ = { isa = PBXGroup; children = ( E7BB267022D4B63800A7713E /* PhonesHeader.xib */, E7BB267122D4B63800A7713E /* PhonesHeader.swift */, E7BB267222D4B63800A7713E /* PhonesHeaderModel.swift */, ); - path = Headers; + path = "Table Headers"; sourceTree = ""; }; E7BCF6BC22DF145F00E71478 /* ExampleUITests */ = { @@ -260,7 +287,7 @@ buildActionMask = 2147483647; files = ( E7BB267D22D4B63800A7713E /* Main.storyboard in Resources */, - BEFF59F623743F1000B3A342 /* Storyboard.storyboard in Resources */, + BEFF59F623743F1000B3A342 /* Launch.storyboard in Resources */, E7BB267722D4B63800A7713E /* NibTableViewCell.xib in Resources */, E7BB267E22D4B63800A7713E /* PhonesHeader.xib in Resources */, ); @@ -280,6 +307,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + BEFF59FB23744D1C00B3A342 /* ActionSensitiveDiffDataSource.swift in Sources */, E7BB267A22D4B63800A7713E /* DeviceiOSCell.swift in Sources */, E7BB268022D4B63800A7713E /* PhonesHeaderModel.swift in Sources */, E7BB267B22D4B63800A7713E /* DeviceiOSCellModel.swift in Sources */, diff --git a/Example/Sources/ActionSensitiveDiffDataSource.swift b/Example/Sources/ActionSensitiveDiffDataSource.swift new file mode 100644 index 0000000..984db71 --- /dev/null +++ b/Example/Sources/ActionSensitiveDiffDataSource.swift @@ -0,0 +1,38 @@ +// +// ActionSensitiveDiffDataSource.swift +// Example +// +// Created by Mikoláš Stuchlík on 07/11/2019. +// Copyright © 2019 FUNTASTY Digital, s.r.o. All rights reserved. +// + +import UIKit +import CellKit +import DiffableCellKit + +protocol DeletableCellModel { + var allowDelete: Bool { get } +} + +final class ActionSensitiveDiffDataSource: DifferentiableCellModelDataSource { + + var deletionHandler: ((IndexPath, DeletableCellModel)->Void)? + + func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + return (sections[indexPath.section].cells[indexPath.row] as? DeletableCellModel)?.allowDelete == true + } + + func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? { + guard indexPath.section == 1 else { + return nil + } + + return [UITableViewRowAction(style: .destructive, title: "Delete") { [weak self] (_, indexPath) in + guard let model = self?.sections[indexPath.section].cells[indexPath.row] as? DeletableCellModel else { + return + } + self?.deletionHandler?(indexPath, model) + }] + } + +} diff --git a/Example/Sources/AppDelegate.swift b/Example/Sources/Supporting/AppDelegate.swift similarity index 100% rename from Example/Sources/AppDelegate.swift rename to Example/Sources/Supporting/AppDelegate.swift diff --git a/Example/Sources/Storyboard.storyboard b/Example/Sources/Supporting/Launch.storyboard similarity index 100% rename from Example/Sources/Storyboard.storyboard rename to Example/Sources/Supporting/Launch.storyboard diff --git a/Example/Sources/Cells/DeviceAndroidCell.swift b/Example/Sources/Table Cells/AndroidCell/DeviceAndroidCell.swift similarity index 84% rename from Example/Sources/Cells/DeviceAndroidCell.swift rename to Example/Sources/Table Cells/AndroidCell/DeviceAndroidCell.swift index 34e676d..755aa73 100644 --- a/Example/Sources/Cells/DeviceAndroidCell.swift +++ b/Example/Sources/Table Cells/AndroidCell/DeviceAndroidCell.swift @@ -15,6 +15,6 @@ final class DeviceAndroidCell: UITableViewCell { extension DeviceAndroidCell: CellConfigurable { func configure(with model: DeviceAndroidCellModel) { - self.nameLabel.text = model.name + self.nameLabel.text = model.name + " tapped: \(model.numberOfTaps)" } } diff --git a/Example/Sources/Cells/DeviceAndroidCellModel.swift b/Example/Sources/Table Cells/AndroidCell/DeviceAndroidCellModel.swift similarity index 82% rename from Example/Sources/Cells/DeviceAndroidCellModel.swift rename to Example/Sources/Table Cells/AndroidCell/DeviceAndroidCellModel.swift index ff88e03..35d9e8b 100644 --- a/Example/Sources/Cells/DeviceAndroidCellModel.swift +++ b/Example/Sources/Table Cells/AndroidCell/DeviceAndroidCellModel.swift @@ -10,7 +10,8 @@ import UIKit import CellKit import DiffableCellKit -struct DeviceAndroidCellModel: CellConvertible, DifferentiableCellModel { +struct DeviceAndroidCellModel: CellConvertible, DifferentiableCellModel, DeletableCellModel { + var domainIndentifier: Int { return name.hashValue } @@ -19,14 +20,16 @@ struct DeviceAndroidCellModel: CellConvertible, DifferentiableCellModel { guard let other = other as? DeviceAndroidCellModel else { return false } - return other.name == self.name + return other.name == self.name && other.numberOfTaps == self.numberOfTaps } typealias Cell = DeviceAndroidCell + var numberOfTaps: Int let name: String let cellHeight: CGFloat = 60.0 let registersLazily: Bool = false + let allowDelete: Bool = true } extension DeviceAndroidCellModel: CellModelSelectable { diff --git a/Example/Sources/Cells/DeviceiOSCell.swift b/Example/Sources/Table Cells/IOSCell/DeviceiOSCell.swift similarity index 83% rename from Example/Sources/Cells/DeviceiOSCell.swift rename to Example/Sources/Table Cells/IOSCell/DeviceiOSCell.swift index bee5941..5544073 100644 --- a/Example/Sources/Cells/DeviceiOSCell.swift +++ b/Example/Sources/Table Cells/IOSCell/DeviceiOSCell.swift @@ -15,6 +15,6 @@ final class DeviceiOSCell: UITableViewCell { extension DeviceiOSCell: CellConfigurable { func configure(with model: DeviceiOSCellModel) { - self.nameLabel.text = model.name + self.nameLabel.text = model.name + " tapped: \(model.numberOfTaps)" } } diff --git a/Example/Sources/Cells/DeviceiOSCellModel.swift b/Example/Sources/Table Cells/IOSCell/DeviceiOSCellModel.swift similarity index 81% rename from Example/Sources/Cells/DeviceiOSCellModel.swift rename to Example/Sources/Table Cells/IOSCell/DeviceiOSCellModel.swift index ab6488a..6e5e1df 100644 --- a/Example/Sources/Cells/DeviceiOSCellModel.swift +++ b/Example/Sources/Table Cells/IOSCell/DeviceiOSCellModel.swift @@ -10,7 +10,8 @@ import UIKit import CellKit import DiffableCellKit -struct DeviceiOSCellModel: CellConvertible, DifferentiableCellModel { +struct DeviceiOSCellModel: CellConvertible, DifferentiableCellModel, DeletableCellModel { + var domainIndentifier: Int { return name.hashValue } @@ -19,14 +20,16 @@ struct DeviceiOSCellModel: CellConvertible, DifferentiableCellModel { guard let other = other as? DeviceiOSCellModel else { return false } - return other.name == self.name + return other.name == self.name && other.numberOfTaps == self.numberOfTaps } typealias Cell = DeviceiOSCell + var numberOfTaps: Int let name: String let cellHeight: CGFloat = 60.0 let registersLazily: Bool = false + let allowDelete: Bool = true } extension DeviceiOSCellModel: CellModelSelectable { diff --git a/Example/Sources/Cells/NibTableViewCell.swift b/Example/Sources/Table Cells/NibTableViewCell.swift similarity index 100% rename from Example/Sources/Cells/NibTableViewCell.swift rename to Example/Sources/Table Cells/NibTableViewCell.swift diff --git a/Example/Sources/Cells/NibTableViewCell.xib b/Example/Sources/Table Cells/NibTableViewCell.xib similarity index 100% rename from Example/Sources/Cells/NibTableViewCell.xib rename to Example/Sources/Table Cells/NibTableViewCell.xib diff --git a/Example/Sources/Headers/PhonesHeader.swift b/Example/Sources/Table Headers/PhonesHeader.swift similarity index 100% rename from Example/Sources/Headers/PhonesHeader.swift rename to Example/Sources/Table Headers/PhonesHeader.swift diff --git a/Example/Sources/Headers/PhonesHeader.xib b/Example/Sources/Table Headers/PhonesHeader.xib similarity index 100% rename from Example/Sources/Headers/PhonesHeader.xib rename to Example/Sources/Table Headers/PhonesHeader.xib diff --git a/Example/Sources/Headers/PhonesHeaderModel.swift b/Example/Sources/Table Headers/PhonesHeaderModel.swift similarity index 100% rename from Example/Sources/Headers/PhonesHeaderModel.swift rename to Example/Sources/Table Headers/PhonesHeaderModel.swift diff --git a/Example/Sources/ViewController.swift b/Example/Sources/ViewController.swift index bd1a0a5..0d264e1 100644 --- a/Example/Sources/ViewController.swift +++ b/Example/Sources/ViewController.swift @@ -18,11 +18,11 @@ enum Constants { final class ViewController: UITableViewController { - private var dataSource: DifferentiableCellModelDataSource! + private var dataSource: ActionSensitiveDiffDataSource! private var phonesSection: DifferentiableCellModelSection { - let iPhoneCellModels: [DifferentiableCellModel] = Constants.iPhones.prefix(5).map { DeviceiOSCellModel(name: $0) } - let androidCellModels: [DifferentiableCellModel] = Constants.androids.prefix(5).map { DeviceAndroidCellModel(name: $0) } + let iPhoneCellModels: [DifferentiableCellModel] = Constants.iPhones.prefix(5).map { DeviceiOSCellModel(numberOfTaps: 0, name: $0) } + let androidCellModels: [DifferentiableCellModel] = Constants.androids.prefix(5).map { DeviceAndroidCellModel(numberOfTaps: 0, name: $0) } let cellModels = iPhoneCellModels + androidCellModels let header = PhonesHeaderModel(title: "Cell Phones") @@ -43,10 +43,14 @@ final class ViewController: UITableViewController { override func viewDidLoad() { super.viewDidLoad() - dataSource = DifferentiableCellModelDataSource(tableView, sections: defaultSections) + dataSource = ActionSensitiveDiffDataSource(tableView, sections: defaultSections) dataSource.delegate = self tableView.dataSource = dataSource tableView.delegate = dataSource + + dataSource.deletionHandler = { [weak self] indexPath, _ in + self?.shouldDeleteRow(at: indexPath) + } } @IBAction func didTapReset() { @@ -60,7 +64,7 @@ final class ViewController: UITableViewController { let available = Set(Constants.iPhones).subtracting(Set(visibleItems)) if let item = available.first { - dataSource?.sections[1].cells.insert(DeviceiOSCellModel(name: item), at: 0) + dataSource?.sections[1].cells.insert(DeviceiOSCellModel(numberOfTaps: 0, name: item), at: 0) } } @@ -71,15 +75,29 @@ final class ViewController: UITableViewController { let available = Set(Constants.androids).subtracting(Set(visibleItems)) if let item = available.first { - dataSource?.sections[1].cells.insert(DeviceAndroidCellModel(name: item), at: 0) + dataSource?.sections[1].cells.insert(DeviceAndroidCellModel(numberOfTaps: 0, name: item), at: 0) + } + } + + private func shouldDeleteRow(at indexPath: IndexPath) { + if indexPath.section == 1 { + dataSource?.sections[indexPath.section].cells.remove(at: indexPath.row) } } } extension ViewController: CellModelDataSourceDelegate { func didSelectCellModel(_ cellModel: CellModel, at indexPath: IndexPath) { - if indexPath.section == 1 { - dataSource?.sections[indexPath.section].cells.remove(at: indexPath.row) + if indexPath.section == 1, var sourceModel = dataSource?.sections[indexPath.section].cells[indexPath.row] { + if var iosModel = sourceModel as? DeviceiOSCellModel { + iosModel.numberOfTaps += 1 + sourceModel = iosModel + } + if var android = sourceModel as? DeviceAndroidCellModel { + android.numberOfTaps += 1 + sourceModel = android + } + dataSource?.sections[indexPath.section].cells[indexPath.row] = sourceModel } } } From e8278b0ce994c017f42cbb4cf7694429fc9a0f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikola=CC=81s=CC=8C=20Stuchli=CC=81k?= Date: Thu, 7 Nov 2019 14:31:32 +0100 Subject: [PATCH 12/16] Update gems --- Gemfile.lock | 57 ++++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index b14e269..c62a55e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,21 +7,24 @@ GEM minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - addressable (2.6.0) - public_suffix (>= 2.0.2, < 4.0) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + algoliasearch (1.27.1) + httpclient (~> 2.8, >= 2.8.3) + json (>= 1.5.1) atomos (0.1.3) - babosa (1.0.2) + babosa (1.0.3) claide (1.0.3) - cocoapods (1.7.5) + cocoapods (1.8.4) activesupport (>= 4.0.2, < 5) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.7.5) + cocoapods-core (= 1.8.4) cocoapods-deintegrate (>= 1.0.3, < 2.0) cocoapods-downloader (>= 1.2.2, < 2.0) cocoapods-plugins (>= 1.0.0, < 2.0) cocoapods-search (>= 1.0.0, < 2.0) cocoapods-stats (>= 1.0.0, < 2.0) - cocoapods-trunk (>= 1.3.1, < 2.0) + cocoapods-trunk (>= 1.4.0, < 2.0) cocoapods-try (>= 1.1.0, < 2.0) colored2 (~> 3.1) escape (~> 0.0.4) @@ -30,9 +33,11 @@ GEM molinillo (~> 0.6.6) nap (~> 1.0) ruby-macho (~> 1.4) - xcodeproj (>= 1.10.0, < 2.0) - cocoapods-core (1.7.5) + xcodeproj (>= 1.11.1, < 2.0) + cocoapods-core (1.8.4) activesupport (>= 4.0.2, < 6) + algoliasearch (~> 1.0) + concurrent-ruby (~> 1.1) fuzzy_match (~> 2.0.4) nap (~> 1.0) cocoapods-deintegrate (1.0.4) @@ -41,7 +46,7 @@ GEM nap cocoapods-search (1.0.0) cocoapods-stats (1.1.0) - cocoapods-trunk (1.4.0) + cocoapods-trunk (1.4.1) nap (>= 0.8, < 2.0) netrc (~> 0.11) cocoapods-try (1.1.0) @@ -58,16 +63,16 @@ GEM dotenv (2.7.5) emoji_regex (1.0.1) escape (0.0.4) - excon (0.66.0) - faraday (0.15.4) + excon (0.68.0) + faraday (0.17.0) multipart-post (>= 1.2, < 3) faraday-cookie_jar (0.0.6) faraday (>= 0.7.4) http-cookie (~> 1.0.0) faraday_middleware (0.13.1) faraday (>= 0.7.4, < 1.0) - fastimage (2.1.5) - fastlane (2.129.0) + fastimage (2.1.7) + fastlane (2.134.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.3, < 3.0.0) babosa (>= 1.0.2, < 2.0.0) @@ -77,9 +82,9 @@ GEM dotenv (>= 2.1.1, < 3.0.0) emoji_regex (>= 0.1, < 2.0) excon (>= 0.45.0, < 1.0.0) - faraday (~> 0.9) + faraday (~> 0.17) faraday-cookie_jar (~> 0.0.6) - faraday_middleware (~> 0.9) + faraday_middleware (~> 0.13.1) fastimage (>= 2.1.0, < 3.0.0) gh_inspector (>= 1.1.2, < 2.0.0) google-api-client (>= 0.21.2, < 0.24.0) @@ -92,7 +97,7 @@ GEM multipart-post (~> 2.0.0) plist (>= 3.1.0, < 4.0.0) public_suffix (~> 2.0.0) - rubyzip (>= 1.2.2, < 2.0.0) + rubyzip (>= 1.3.0, < 2.0.0) security (= 0.1.3) simctl (~> 1.6.3) slack-notifier (>= 2.0.0, < 3.0.0) @@ -116,9 +121,9 @@ GEM representable (~> 3.0) retriable (>= 2.0, < 4.0) signet (~> 0.9) - google-cloud-core (1.3.1) + google-cloud-core (1.4.1) google-cloud-env (~> 1.0) - google-cloud-env (1.2.1) + google-cloud-env (1.3.0) faraday (~> 0.11) google-cloud-storage (1.16.0) digest-crc (~> 0.4) @@ -141,13 +146,13 @@ GEM json (2.2.0) jwt (2.1.0) memoist (0.16.0) - mime-types (3.2.2) + mime-types (3.3) mime-types-data (~> 3.2015) - mime-types-data (3.2019.0331) + mime-types-data (3.2019.1009) mini_magick (4.9.5) - minitest (5.11.3) + minitest (5.13.0) molinillo (0.6.6) - multi_json (1.13.1) + multi_json (1.14.1) multi_xml (0.6.0) multipart-post (2.0.0) nanaimo (0.2.6) @@ -164,14 +169,14 @@ GEM retriable (3.1.2) rouge (2.0.7) ruby-macho (1.4.0) - rubyzip (1.2.3) + rubyzip (1.3.0) security (0.1.3) - signet (0.11.0) + signet (0.12.0) addressable (~> 2.3) faraday (~> 0.9) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) - simctl (1.6.5) + simctl (1.6.6) CFPropertyList naturally slack-notifier (2.3.2) @@ -191,7 +196,7 @@ GEM unf_ext (0.0.7.6) unicode-display_width (1.6.0) word_wrap (1.0.0) - xcodeproj (1.12.0) + xcodeproj (1.13.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) From 99641aac797189975d233aa92785265cb2417f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikola=CC=81s=CC=8C=20Stuchli=CC=81k?= Date: Thu, 7 Nov 2019 14:31:55 +0100 Subject: [PATCH 13/16] Bump version --- CellKit.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CellKit.podspec b/CellKit.podspec index 11c5107..6847c04 100644 --- a/CellKit.podspec +++ b/CellKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "CellKit" - s.version = "0.5.1" + s.version = "0.6.0" s.summary = "Table View and Collection View data source wrapper" s.description = <<-DESC Generic abstraction over table/collection data source From 2346a109d7ecafeb9cdd1e47438d455ace1e5176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikola=CC=81s=CC=8C=20Stuchli=CC=81k?= Date: Thu, 7 Nov 2019 14:32:06 +0100 Subject: [PATCH 14/16] Fix linking --- Example/Example.xcodeproj/project.pbxproj | 37 +++++++++++++++++------ 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index 8174cc8..b81efaf 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -7,10 +7,10 @@ objects = { /* Begin PBXBuildFile section */ - BEFF59F22374373900B3A342 /* CellKit in Frameworks */ = {isa = PBXBuildFile; productRef = BEFF59F12374373900B3A342 /* CellKit */; }; - BEFF59F42374373D00B3A342 /* DiffableCellKit in Frameworks */ = {isa = PBXBuildFile; productRef = BEFF59F32374373D00B3A342 /* DiffableCellKit */; }; BEFF59F623743F1000B3A342 /* Launch.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BEFF59F523743F1000B3A342 /* Launch.storyboard */; }; BEFF59FB23744D1C00B3A342 /* ActionSensitiveDiffDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEFF59FA23744D1C00B3A342 /* ActionSensitiveDiffDataSource.swift */; }; + BEFF5A012374530300B3A342 /* CellKit in Frameworks */ = {isa = PBXBuildFile; productRef = BEFF5A002374530300B3A342 /* CellKit */; }; + BEFF5A032374530300B3A342 /* DiffableCellKit in Frameworks */ = {isa = PBXBuildFile; productRef = BEFF5A022374530300B3A342 /* DiffableCellKit */; }; E7BB267522D4B63800A7713E /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7BB266322D4B63800A7713E /* ViewController.swift */; }; E7BB267622D4B63800A7713E /* DeviceAndroidCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7BB266522D4B63800A7713E /* DeviceAndroidCell.swift */; }; E7BB267722D4B63800A7713E /* NibTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E7BB266622D4B63800A7713E /* NibTableViewCell.xib */; }; @@ -77,8 +77,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BEFF59F42374373D00B3A342 /* DiffableCellKit in Frameworks */, - BEFF59F22374373900B3A342 /* CellKit in Frameworks */, + BEFF5A032374530300B3A342 /* DiffableCellKit in Frameworks */, + BEFF5A012374530300B3A342 /* CellKit in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -95,10 +95,10 @@ 52D6D9721BEFF229002C0205 = { isa = PBXGroup; children = ( + BEFF59EF2374371D00B3A342 /* CellKit */, E7BCF6C522DF146B00E71478 /* Tests */, E7BB266222D4B63800A7713E /* Sources */, 52D6D97D1BEFF229002C0205 /* Products */, - BEFF59EF2374371D00B3A342 /* CellKit */, BEFF59F02374373900B3A342 /* Frameworks */, ); sourceTree = ""; @@ -213,11 +213,13 @@ buildRules = ( ); dependencies = ( + BEFF59FD237452FD00B3A342 /* PBXTargetDependency */, + BEFF59FF237452FD00B3A342 /* PBXTargetDependency */, ); name = Example; packageProductDependencies = ( - BEFF59F12374373900B3A342 /* CellKit */, - BEFF59F32374373D00B3A342 /* DiffableCellKit */, + BEFF5A002374530300B3A342 /* CellKit */, + BEFF5A022374530300B3A342 /* DiffableCellKit */, ); productName = Example; productReference = D02135A420CA609D00EE79D1 /* Example.app */; @@ -258,6 +260,7 @@ }; E7BCF6BA22DF145F00E71478 = { CreatedOnToolsVersion = 11.0; + ProvisioningStyle = Automatic; TestTargetID = D02135A320CA609D00EE79D1; }; }; @@ -331,6 +334,14 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + BEFF59FD237452FD00B3A342 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = BEFF59FC237452FD00B3A342 /* CellKit */; + }; + BEFF59FF237452FD00B3A342 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = BEFF59FE237452FD00B3A342 /* DiffableCellKit */; + }; E7BCF6C122DF145F00E71478 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = D02135A320CA609D00EE79D1 /* Example */; @@ -615,11 +626,19 @@ /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ - BEFF59F12374373900B3A342 /* CellKit */ = { + BEFF59FC237452FD00B3A342 /* CellKit */ = { + isa = XCSwiftPackageProductDependency; + productName = CellKit; + }; + BEFF59FE237452FD00B3A342 /* DiffableCellKit */ = { + isa = XCSwiftPackageProductDependency; + productName = DiffableCellKit; + }; + BEFF5A002374530300B3A342 /* CellKit */ = { isa = XCSwiftPackageProductDependency; productName = CellKit; }; - BEFF59F32374373D00B3A342 /* DiffableCellKit */ = { + BEFF5A022374530300B3A342 /* DiffableCellKit */ = { isa = XCSwiftPackageProductDependency; productName = DiffableCellKit; }; From c8ecacdb05f5dba0da3100bff36f6ecf4081a3bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikola=CC=81s=CC=8C=20Stuchli=CC=81k?= Date: Thu, 7 Nov 2019 14:32:17 +0100 Subject: [PATCH 15/16] Apply lint --- Example/Sources/ActionSensitiveDiffDataSource.swift | 2 +- Example/Sources/Table Cells/NibTableViewCell.swift | 4 ++-- Sources/DiffableCellKit/DifferentiableCellModel.swift | 2 -- .../DiffableCellKit/DifferentiableCellModelDataSource.swift | 4 +--- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Example/Sources/ActionSensitiveDiffDataSource.swift b/Example/Sources/ActionSensitiveDiffDataSource.swift index 984db71..06a77af 100644 --- a/Example/Sources/ActionSensitiveDiffDataSource.swift +++ b/Example/Sources/ActionSensitiveDiffDataSource.swift @@ -16,7 +16,7 @@ protocol DeletableCellModel { final class ActionSensitiveDiffDataSource: DifferentiableCellModelDataSource { - var deletionHandler: ((IndexPath, DeletableCellModel)->Void)? + var deletionHandler: ((IndexPath, DeletableCellModel) -> Void)? func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { return (sections[indexPath.section].cells[indexPath.row] as? DeletableCellModel)?.allowDelete == true diff --git a/Example/Sources/Table Cells/NibTableViewCell.swift b/Example/Sources/Table Cells/NibTableViewCell.swift index afe645a..01294eb 100644 --- a/Example/Sources/Table Cells/NibTableViewCell.swift +++ b/Example/Sources/Table Cells/NibTableViewCell.swift @@ -14,7 +14,7 @@ struct NibTableViewCellModel: ReusableCellConvertible, DifferentiableCellModel { var domainIndentifier: Int { return text.hashValue } - + func hasEqualContent(with other: CellModel) -> Bool { guard let other = other as? NibTableViewCellModel else { return false @@ -22,7 +22,7 @@ struct NibTableViewCellModel: ReusableCellConvertible, DifferentiableCellModel { return other.text == self.text } - + typealias Cell = NibTableViewCell let text: String diff --git a/Sources/DiffableCellKit/DifferentiableCellModel.swift b/Sources/DiffableCellKit/DifferentiableCellModel.swift index fae8822..9409a06 100644 --- a/Sources/DiffableCellKit/DifferentiableCellModel.swift +++ b/Sources/DiffableCellKit/DifferentiableCellModel.swift @@ -10,14 +10,12 @@ import DifferenceKit import CellKit #endif - /// Support for determining whether cell model belongs to a certain cell, whether cell should be inserted, removed, updated or moved. public protocol DifferentiableCellModel: CellModel { /// Identifier of a cell model inside it's own domain determined by reusable identifier. Assuming model is already presented inside a view, changing of this value will result in it's removal and insertion into the view. var domainIndentifier: Int { get } - /// This method return true, whenever content of a view model is equal to content of other view model within it's domain. However, argument may contain view model from different domain. If cell model conforms to protocol Equatable, default implementation is provided. /// - Parameter other: other cell model to compare with - DO NOT FORGET to cast this argument to your view model's type func hasEqualContent(with other: CellModel) -> Bool diff --git a/Sources/DiffableCellKit/DifferentiableCellModelDataSource.swift b/Sources/DiffableCellKit/DifferentiableCellModelDataSource.swift index 4d5d45f..a2164c9 100644 --- a/Sources/DiffableCellKit/DifferentiableCellModelDataSource.swift +++ b/Sources/DiffableCellKit/DifferentiableCellModelDataSource.swift @@ -13,7 +13,7 @@ import CellKit #endif open class DifferentiableCellModelDataSource: AbstractDataSource, DataSource { - + private enum Container { case table(UITableView) case collection(UICollectionView) @@ -28,7 +28,6 @@ open class DifferentiableCellModelDataSource: AbstractDataSource, DataSource { } } - private let container: Container private var _sections: [DifferentiableCellModelSection] @@ -47,7 +46,6 @@ open class DifferentiableCellModelDataSource: AbstractDataSource, DataSource { } } - public init(_ tableView: UITableView, sections: [DifferentiableCellModelSection]) { container = .table(tableView) _sections = [] From ec72c6df0b736f16b32982efa4862d8ce0ed2434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikola=CC=81s=CC=8C=20Stuchli=CC=81k?= Date: Fri, 8 Nov 2019 10:19:00 +0100 Subject: [PATCH 16/16] Remove one line --- Sources/DiffableCellKit/DifferentiableSection.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/DiffableCellKit/DifferentiableSection.swift b/Sources/DiffableCellKit/DifferentiableSection.swift index 7fe06f4..f8d14f7 100644 --- a/Sources/DiffableCellKit/DifferentiableSection.swift +++ b/Sources/DiffableCellKit/DifferentiableSection.swift @@ -21,8 +21,7 @@ struct SectionMetadata: ContentEquatable, Differentiable { return identifier } - var headerView: SupplementaryViewModel? - var footerView: SupplementaryViewModel? + var headerView, footerView: SupplementaryViewModel? let identifier: String init(from section: GenericCellModelSection) {