diff --git a/CellKit.podspec b/CellKit.podspec index bd4adda..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 @@ -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 } @@ -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", "~> 1.0" end end diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index b475010..b81efaf 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -7,9 +7,10 @@ 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 */; }; + 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 */; }; @@ -17,13 +18,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 +43,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - E72976192313B07A008B6E2F /* CellKit in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -52,8 +50,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + BEFF59EF2374371D00B3A342 /* CellKit */ = {isa = PBXFileReference; lastKnownFileType = folder; name = CellKit; path = ..; 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; }; - 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 +61,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 +77,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - E729761D2313B089008B6E2F /* DiffableCellKit in Frameworks */, - E729761B2313B089008B6E2F /* CellKit in Frameworks */, + BEFF5A032374530300B3A342 /* DiffableCellKit in Frameworks */, + BEFF5A012374530300B3A342 /* CellKit in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -97,10 +95,11 @@ 52D6D9721BEFF229002C0205 = { isa = PBXGroup; children = ( + BEFF59EF2374371D00B3A342 /* CellKit */, E7BCF6C522DF146B00E71478 /* Tests */, E7BB266222D4B63800A7713E /* Sources */, 52D6D97D1BEFF229002C0205 /* Products */, - E7BB269B22D5D42D00A7713E /* Frameworks */, + BEFF59F02374373900B3A342 /* Frameworks */, ); sourceTree = ""; }; @@ -113,50 +112,73 @@ name = Products; sourceTree = ""; }; + BEFF59F02374373900B3A342 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + 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 = ( - E72976182313B04B008B6E2F /* CellKit */, + E7BB266F22D4B63800A7713E /* Table Headers */, + E7BB266422D4B63800A7713E /* Table Cells */, + BEFF59FA23744D1C00B3A342 /* ActionSensitiveDiffDataSource.swift */, E7BB266322D4B63800A7713E /* ViewController.swift */, - E7BB266422D4B63800A7713E /* Cells */, - E7BB266B22D4B63800A7713E /* LaunchScreen.storyboard */, E7BB266D22D4B63800A7713E /* Main.storyboard */, - E7BB266F22D4B63800A7713E /* Headers */, - E7BB267322D4B63800A7713E /* AppDelegate.swift */, E7BB267422D4B63800A7713E /* Info.plist */, - E7BB268322D4B65600A7713E /* Assets.xcassets */, + 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; - sourceTree = ""; - }; - E7BB269B22D5D42D00A7713E /* Frameworks */ = { - isa = PBXGroup; - children = ( - ); - name = Frameworks; + path = "Table Headers"; sourceTree = ""; }; E7BCF6BC22DF145F00E71478 /* ExampleUITests */ = { @@ -191,11 +213,13 @@ buildRules = ( ); dependencies = ( + BEFF59FD237452FD00B3A342 /* PBXTargetDependency */, + BEFF59FF237452FD00B3A342 /* PBXTargetDependency */, ); name = Example; packageProductDependencies = ( - E729761A2313B089008B6E2F /* CellKit */, - E729761C2313B089008B6E2F /* DiffableCellKit */, + BEFF5A002374530300B3A342 /* CellKit */, + BEFF5A022374530300B3A342 /* DiffableCellKit */, ); productName = Example; productReference = D02135A420CA609D00EE79D1 /* Example.app */; @@ -236,6 +260,7 @@ }; E7BCF6BA22DF145F00E71478 = { CreatedOnToolsVersion = 11.0; + ProvisioningStyle = Automatic; TestTargetID = D02135A320CA609D00EE79D1; }; }; @@ -265,9 +290,8 @@ buildActionMask = 2147483647; files = ( E7BB267D22D4B63800A7713E /* Main.storyboard in Resources */, + BEFF59F623743F1000B3A342 /* Launch.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; @@ -286,6 +310,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 */, @@ -309,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 */; @@ -317,14 +350,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 +477,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 +500,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 +531,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 +626,19 @@ /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ - E729761A2313B089008B6E2F /* CellKit */ = { + BEFF59FC237452FD00B3A342 /* CellKit */ = { + isa = XCSwiftPackageProductDependency; + productName = CellKit; + }; + BEFF59FE237452FD00B3A342 /* DiffableCellKit */ = { + isa = XCSwiftPackageProductDependency; + productName = DiffableCellKit; + }; + BEFF5A002374530300B3A342 /* CellKit */ = { isa = XCSwiftPackageProductDependency; productName = CellKit; }; - E729761C2313B089008B6E2F /* DiffableCellKit */ = { + BEFF5A022374530300B3A342 /* DiffableCellKit */ = { isa = XCSwiftPackageProductDependency; productName = DiffableCellKit; }; diff --git a/Example/Sources/ActionSensitiveDiffDataSource.swift b/Example/Sources/ActionSensitiveDiffDataSource.swift new file mode 100644 index 0000000..06a77af --- /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/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/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/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/Supporting/Launch.storyboard b/Example/Sources/Supporting/Launch.storyboard new file mode 100644 index 0000000..e8f835c --- /dev/null +++ b/Example/Sources/Supporting/Launch.storyboard @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 50% rename from Example/Sources/Cells/DeviceAndroidCellModel.swift rename to Example/Sources/Table Cells/AndroidCell/DeviceAndroidCellModel.swift index 34f2f6a..35d9e8b 100644 --- a/Example/Sources/Cells/DeviceAndroidCellModel.swift +++ b/Example/Sources/Table Cells/AndroidCell/DeviceAndroidCellModel.swift @@ -10,12 +10,26 @@ import UIKit import CellKit import DiffableCellKit -struct DeviceAndroidCellModel: CellConvertible, EquatableCellModel, Equatable { +struct DeviceAndroidCellModel: CellConvertible, DifferentiableCellModel, DeletableCellModel { + + 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 && 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 50% rename from Example/Sources/Cells/DeviceiOSCellModel.swift rename to Example/Sources/Table Cells/IOSCell/DeviceiOSCellModel.swift index 993e10f..6e5e1df 100644 --- a/Example/Sources/Cells/DeviceiOSCellModel.swift +++ b/Example/Sources/Table Cells/IOSCell/DeviceiOSCellModel.swift @@ -10,12 +10,26 @@ import UIKit import CellKit import DiffableCellKit -struct DeviceiOSCellModel: CellConvertible, EquatableCellModel, Equatable { +struct DeviceiOSCellModel: CellConvertible, DifferentiableCellModel, DeletableCellModel { + + 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 && 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 60% rename from Example/Sources/Cells/NibTableViewCell.swift rename to Example/Sources/Table Cells/NibTableViewCell.swift index aa8e3fa..01294eb 100644 --- a/Example/Sources/Cells/NibTableViewCell.swift +++ b/Example/Sources/Table 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/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 eb30983..0d264e1 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: ActionSensitiveDiffDataSource! - 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(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") - 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,10 +43,14 @@ final class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - dataSource = EquatableCellModelDataSource(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() { @@ -57,9 +62,9 @@ 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) + dataSource?.sections[1].cells.insert(DeviceiOSCellModel(numberOfTaps: 0, name: item), at: 0) } } @@ -68,17 +73,31 @@ 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) + 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 } } } 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) diff --git a/Package.swift b/Package.swift index 69b86e0..3a1602e 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/DifferentiableCellModel.swift b/Sources/DiffableCellKit/DifferentiableCellModel.swift new file mode 100644 index 0000000..9409a06 --- /dev/null +++ b/Sources/DiffableCellKit/DifferentiableCellModel.swift @@ -0,0 +1,51 @@ +// +// DifferentiableCellModel.swift +// +// +// Created by Matěj Kašpar Jirásek on 10/07/2019. +// + +import DifferenceKit +#if SWIFT_PACKAGE +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 +} + +extension DifferentiableCellModel { + func asDifferentaibleWrapper() -> DifferentiableCellModelWrapper { + return DifferentiableCellModelWrapper(cellModel: 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 + } +} + +struct DifferentiableCellModelWrapper { + let cellModel: DifferentiableCellModel +} + +extension DifferentiableCellModelWrapper: Equatable, Differentiable { + static func == (lhs: DifferentiableCellModelWrapper, rhs: DifferentiableCellModelWrapper) -> Bool { + return lhs.cellModel.hasEqualContent(with: rhs.cellModel) + } + + var differenceIdentifier: String { + return "\(cellModel.reuseIdentifier)<.>\(cellModel.domainIndentifier)" + } +} diff --git a/Sources/DiffableCellKit/DifferentiableCellModelDataSource.swift b/Sources/DiffableCellKit/DifferentiableCellModelDataSource.swift new file mode 100644 index 0000000..a2164c9 --- /dev/null +++ b/Sources/DiffableCellKit/DifferentiableCellModelDataSource.swift @@ -0,0 +1,88 @@ +// +// DifferentiableCellModelDataSource.swift +// Example +// +// Created by Petr Zvoníček on 08.06.18. +// Copyright © 2018 FUNTASTY Digital, s.r.o. All rights reserved. +// + +import UIKit +import DifferenceKit +#if SWIFT_PACKAGE +import CellKit +#endif + +open class DifferentiableCellModelDataSource: AbstractDataSource, DataSource { + + 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 _sections + } + set { + let oldSection = _sections.map { $0.arraySection } + let newSection = newValue.map { $0.arraySection } + 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 + } + } + } + + public init(_ tableView: UITableView, sections: [DifferentiableCellModelSection]) { + container = .table(tableView) + _sections = [] + super.init() + + self.sections = sections + } + + public init(_ collectionView: UICollectionView, sections: [DifferentiableCellModelSection]) { + container = .collection(collectionView) + _sections = [] + super.init() + + self.sections = sections + } + + 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] + } +} diff --git a/Sources/DiffableCellKit/DifferentiableSection.swift b/Sources/DiffableCellKit/DifferentiableSection.swift new file mode 100644 index 0000000..f8d14f7 --- /dev/null +++ b/Sources/DiffableCellKit/DifferentiableSection.swift @@ -0,0 +1,40 @@ +// +// DifferentiableSection.swift +// +// +// Created by Mikoláš Stuchlík on 11/09/2019. +// + +import DifferenceKit +#if SWIFT_PACKAGE +import CellKit +#endif + +public typealias DifferentiableCellModelSection = GenericCellModelSection + +struct SectionMetadata: ContentEquatable, Differentiable { + func isContentEqual(to source: SectionMetadata) -> Bool { + return identifier == identifier + } + + var differenceIdentifier: String { + return identifier + } + + var headerView, footerView: SupplementaryViewModel? + let identifier: String + + init(from section: GenericCellModelSection) { + headerView = section.headerView + footerView = section.footerView + identifier = section.identifier + } +} + +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 61197bb..0000000 --- a/Sources/DiffableCellKit/EquatableCellModel.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// File.swift -// -// -// Created by Matěj Kašpar Jirásek on 10/07/2019. -// - -#if SWIFT_PACKAGE -import CellKit -#endif - -public protocol EquatableCellModel: CellModel { - func isEqual(to other: CellModel) -> Bool -} - -extension EquatableCellModel { - func asEquatable() -> EquatableCellModelWrapper { - return EquatableCellModelWrapper(self) - } -} - -extension EquatableCellModel where Self: Equatable { - public func isEqual(to other: CellModel) -> Bool { - guard let otherCellModel = other as? Self else { - return false - } - return self == otherCellModel - } -} - -struct EquatableCellModelWrapper { - let cellModel: EquatableCellModel - - init(_ cellModel: EquatableCellModel) { - self.cellModel = cellModel - } -} - -extension EquatableCellModelWrapper: Equatable { - static func == (lhs: EquatableCellModelWrapper, rhs: EquatableCellModelWrapper) -> Bool { - return lhs.cellModel.isEqual(to: rhs.cellModel) - } -} diff --git a/Sources/DiffableCellKit/EquatableCellModelDataSource.swift b/Sources/DiffableCellKit/EquatableCellModelDataSource.swift deleted file mode 100644 index d67d660..0000000 --- a/Sources/DiffableCellKit/EquatableCellModelDataSource.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// CellModelManager.swift -// Example -// -// Created by Petr Zvoníček on 08.06.18. -// Copyright © 2018 FUNTASTY Digital, s.r.o. All rights reserved. -// - -import UIKit -import Dwifft -#if SWIFT_PACKAGE -import CellKit -#endif - -public typealias EquatableCellModelSection = GenericCellModelSection - -open class EquatableCellModelDataSource: AbstractDataSource, DataSource { - - public var sections: [EquatableCellModelSection] { - get { - return diffCalculator.sectionedValues.sectionsAndValues.map { $0.0 } - } - set { - diffCalculator.sectionedValues = SectionedValues(newValue.map { ($0, $0.cells.map { $0.asEquatable() }) }) - } - } - - let diffCalculator: AbstractDiffCalculator - - public init(_ tableView: UITableView, sections: [EquatableCellModelSection]) { - self.diffCalculator = TableViewDiffCalculator(tableView: tableView) - super.init() - - self.sections = sections - } - - public init(_ collectionView: UICollectionView, sections: [EquatableCellModelSection]) { - self.diffCalculator = CollectionViewDiffCalculator(collectionView: collectionView) - 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 - } -}