Skip to content

Commit

Permalink
Merge pull request #16 from zenangst/feature/customizable-diffing
Browse files Browse the repository at this point in the history
Refactor implementation to offer more customization
  • Loading branch information
zenangst authored Mar 3, 2019
2 parents baca25f + 78ce95a commit 903f828
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 8 deletions.
8 changes: 8 additions & 0 deletions Differific.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
BDBF80E8209252440002CE93 /* UICollectionViewExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDBF80E5209252440002CE93 /* UICollectionViewExtensionsTests.swift */; };
BDBF80EA20927B080002CE93 /* NSCollectionViewExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDBF80E920927B080002CE93 /* NSCollectionViewExtensionTests.swift */; };
BDBF80EC20927B360002CE93 /* NSTableViewExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDBF80EB20927B360002CE93 /* NSTableViewExtensionsTests.swift */; };
BDED0DC8222C57A800D7B46E /* Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDED0DC7222C57A800D7B46E /* Diffable.swift */; };
BDED0DC9222C57A800D7B46E /* Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDED0DC7222C57A800D7B46E /* Diffable.swift */; };
BDED0DCA222C57A800D7B46E /* Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDED0DC7222C57A800D7B46E /* Diffable.swift */; };
D284B1051F79038B00D94AF3 /* Differific.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D284B0FC1F79038B00D94AF3 /* Differific.framework */; };
D284B1381F7906DB00D94AF3 /* Info-iOS-Tests.plist in Resources */ = {isa = PBXBuildFile; fileRef = D284B12F1F7906DA00D94AF3 /* Info-iOS-Tests.plist */; };
D284B1391F7906DB00D94AF3 /* Info-tvOS-Tests.plist in Resources */ = {isa = PBXBuildFile; fileRef = D284B1301F7906DA00D94AF3 /* Info-tvOS-Tests.plist */; };
Expand Down Expand Up @@ -99,6 +102,7 @@
BDBF80E5209252440002CE93 /* UICollectionViewExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UICollectionViewExtensionsTests.swift; sourceTree = "<group>"; };
BDBF80E920927B080002CE93 /* NSCollectionViewExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSCollectionViewExtensionTests.swift; sourceTree = "<group>"; };
BDBF80EB20927B360002CE93 /* NSTableViewExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSTableViewExtensionsTests.swift; sourceTree = "<group>"; };
BDED0DC7222C57A800D7B46E /* Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Diffable.swift; sourceTree = "<group>"; };
D284B0FC1F79038B00D94AF3 /* Differific.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Differific.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D284B1041F79038B00D94AF3 /* Differific-tvOS-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Differific-tvOS-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
D284B1181F79039F00D94AF3 /* Differific.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Differific.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -283,6 +287,7 @@
BD690D4F20DC143100E56E30 /* ArrayEntry.swift */,
BDBF80C420923FD00002CE93 /* Change.swift */,
BD690D4720DC13F700E56E30 /* Counter.swift */,
BDED0DC7222C57A800D7B46E /* Diffable.swift */,
BDBF80C320923FD00002CE93 /* DiffManager.swift */,
BDBF80C520923FD00002CE93 /* IndexPathManager.swift */,
BD690D4B20DC141200E56E30 /* TableEntry.swift */,
Expand Down Expand Up @@ -587,6 +592,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BDED0DCA222C57A800D7B46E /* Diffable.swift in Sources */,
BDBF80CF20923FD00002CE93 /* IndexPathManager.swift in Sources */,
BDBF80D220923FD00002CE93 /* Algorithm.swift in Sources */,
BDBF80BC20923FB90002CE93 /* UICollectionView+Extensions.swift in Sources */,
Expand Down Expand Up @@ -621,6 +627,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BDED0DC8222C57A800D7B46E /* Diffable.swift in Sources */,
BDBF80CD20923FD00002CE93 /* IndexPathManager.swift in Sources */,
BDBF80D020923FD00002CE93 /* Algorithm.swift in Sources */,
BDBF80BB20923FB90002CE93 /* UICollectionView+Extensions.swift in Sources */,
Expand Down Expand Up @@ -648,6 +655,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BDED0DC9222C57A800D7B46E /* Diffable.swift in Sources */,
BDBF80CE20923FD00002CE93 /* IndexPathManager.swift in Sources */,
BDBF80D120923FD00002CE93 /* Algorithm.swift in Sources */,
BDBF80D820923FFA0002CE93 /* NSTableView+Extensions.swift in Sources */,
Expand Down
14 changes: 8 additions & 6 deletions Source/Shared/Algorithm.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

class Algorithm {
public static func diff<T: Hashable>(old: [T], new: [T]) -> [Change<T>] {
public static func diff<T: Hashable>(old: [T], new: [T], compare: (T,T) -> Bool) -> [Change<T>] {
if new.isEmpty {
var changes = [Change<T>]()
changes.reserveCapacity(old.count)
Expand Down Expand Up @@ -35,28 +35,30 @@ class Algorithm {

// 1 Pass
for element in new[0...].lazy {
let diffValue = (element as? Diffable)?.diffValue ?? element.hashValue
let entry: TableEntry
if let tableEntry = table[element.hashValue] {
if let tableEntry = table[diffValue] {
entry = tableEntry
} else {
entry = TableEntry()
}

table[element.hashValue] = entry
table[diffValue] = entry
entry.newCounter += 1
newArray.append(ArrayEntry(tableEntry: entry))
}

// 2 Pass
for element in old[0...].lazy {
let diffValue = (element as? Diffable)?.diffValue ?? element.hashValue
let entry: TableEntry
if let tableEntry = table[element.hashValue] {
if let tableEntry = table[diffValue] {
entry = tableEntry
} else {
entry = TableEntry()
}

table[element.hashValue] = entry
table[diffValue] = entry
entry.oldCounter += 1
entry.indexesInOld.append(offset)
oldArray.append(ArrayEntry(tableEntry: entry))
Expand Down Expand Up @@ -135,7 +137,7 @@ class Algorithm {
runningOffset += 1
} else {
let oldIndex = element.indexInOther
if old[oldIndex] != new[offset] {
if !compare(old[oldIndex], new[offset]) {
changes.append(Change(.update,
item: old[oldIndex],
index: oldIndex,
Expand Down
5 changes: 3 additions & 2 deletions Source/Shared/DiffManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import Foundation

public class DiffManager {
public init() {}
public func diff<T: Hashable>(_ old: [T], _ new: [T]) -> [Change<T>] {
return Algorithm.diff(old: old, new: new)
public func diff<T: Hashable>(_ old: [T], _ new: [T],
compare: (T,T) -> Bool = { $0 == $1 }) -> [Change<T>] {
return Algorithm.diff(old: old, new: new, compare: compare)
}
}
5 changes: 5 additions & 0 deletions Source/Shared/Diffable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

public protocol Diffable {
var diffValue: Int { get }
}
48 changes: 48 additions & 0 deletions Tests/Shared/DiffManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ import XCTest
import Differific

class DiffManagerTests: XCTestCase {
struct MockDiffable: Hashable, Diffable {
let id: Int
var diffValue: Int {
return id
}
}

struct MockObject: Hashable {
let id: Int
let name: String
Expand Down Expand Up @@ -148,6 +155,47 @@ class DiffManagerTests: XCTestCase {
XCTAssertEqual(result.count, 5)
}

func testComparator() {
let old = [
MockObject(id: 0, name: "Foo"),
MockObject(id: 1, name: "Bar"),
MockObject(id: 2, name: "Baz")
]
let new = [
MockObject(id: 1, name: "Foo0"),
MockObject(id: 0, name: "Bar1"),
MockObject(id: 3, name: "Baz2")
]

let diffManager = DiffManager()
let changes = diffManager.diff(old, new, compare: { $0.id == $1.id })

XCTAssertEqual(changes[0].kind, .delete)
XCTAssertEqual(changes[1].kind, .move)
XCTAssertEqual(changes[2].kind, .insert)
}

func testDiffable() {
let old = [
MockDiffable(id: 0),
MockDiffable(id: 1),
MockDiffable(id: 2)
]

let new = [
MockDiffable(id: 1),
MockDiffable(id: 0),
MockDiffable(id: 3)
]

let diffManager = DiffManager()
let changes = diffManager.diff(old, new)

XCTAssertEqual(changes[0].kind, .delete)
XCTAssertEqual(changes[1].kind, .move)
XCTAssertEqual(changes[2].kind, .insert)
}

func testPerformance() {
let diffManager = DiffManager()
var old = [Int]()
Expand Down
30 changes: 30 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
ignore:
- "Applications/Xcode.app/.*"
- "bin"
- "Example"
- "Tests"
- "Images"
- "Info"
coverage:
status:
project:
default: off
macOS:
flags: macOS
tvOS:
flags: tvOS
iOS:
flags: iOS
flags:
macOS:
paths:
- Source/macOS
- Source/Shared
tvOS:
paths:
- Source/iOS+tvOS
- Source/Shared
iOS:
paths:
- Source/iOS+tvOS
- Source/Shared

0 comments on commit 903f828

Please sign in to comment.