Skip to content

Commit 2a253cb

Browse files
committed
Observable collections cleanup
1 parent bb086e7 commit 2a253cb

25 files changed

+863
-1023
lines changed

Bond-App/AppDelegate.swift

+1-90
Original file line numberDiff line numberDiff line change
@@ -17,98 +17,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
1717

1818
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
1919
window = UIWindow(frame: UIScreen.main.bounds)
20-
window?.rootViewController = ViewController()
20+
window?.rootViewController = UIViewController()
2121
window?.makeKeyAndVisible()
2222
return true
2323
}
2424
}
2525

26-
class CustomBinder: TableViewBinderDataSource<TreeChangeset<Array2D<String, Int>>> {
27-
28-
@objc func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
29-
return changeset?.dataSource[[section]].value.section
30-
}
31-
32-
// override func applyChageset(_ changeset: TreeChangeset<Array2D<String, Int>>) {
33-
// tableView?.reloadData()
34-
// }
35-
}
36-
37-
class ViewController: UIViewController {
38-
39-
40-
let tableView = UITableView()
41-
let mutableArray = MutableObservableArray2D<String, Int>()
42-
43-
override func viewDidLoad() {
44-
super.viewDidLoad()
45-
view.addSubview(tableView)
46-
tableView.estimatedRowHeight = 50
47-
randomOperation()
48-
49-
mutableArray.bind(to: self) { s, d in
50-
print(d.diffs, d.collection)
51-
}
52-
53-
mutableArray.bind(to: tableView, cellType: UITableViewCell.self, using: CustomBinder()) { (cell, data) in
54-
cell.textLabel?.text = "\(data)"
55-
}
56-
}
57-
58-
59-
func randomOperation() {
60-
mutableArray.apply(.randomOperation(collection: mutableArray.value.collection))
61-
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
62-
self.randomOperation()
63-
}
64-
}
65-
66-
override func viewDidLayoutSubviews() {
67-
super.viewDidLayoutSubviews()
68-
tableView.frame = view.bounds
69-
}
70-
}
71-
72-
extension TreeChangeset.Operation where Collection == Array2D<String, Int> {
73-
74-
static func randomOperation(collection: Collection) -> TreeChangeset<Collection>.Operation {
75-
let data = [0, 0].randomElement() == 0 ? Array2DElement(item: Int.random(in: 11..<100)) : Array2DElement(section: "\(Int.random(in: 11..<100))")
76-
let element = TreeNode(data)
77-
let indices = collection.indices
78-
guard indices.count > 3 else {
79-
return .insert(TreeNode(Array2DElement(section: "Sec \(Int.random(in: 11..<100))"), [TreeNode(Array2DElement(item: 0))]), at: [0])
80-
}
81-
switch [0, 0, 1, 3, 4].randomElement() {
82-
case 0:
83-
var at = indices.randomElement()!
84-
if Bool.random() && at.count == 1 {
85-
at = at.appending(0)
86-
}
87-
if at.count == 2 {
88-
return .insert(TreeNode(Array2DElement(item: Int.random(in: 11..<100))), at: at)
89-
} else {
90-
return .insert(TreeNode(Array2DElement(section: "\(Int.random(in: 11..<100))")), at: at)
91-
}
92-
case 1:
93-
let at = indices.randomElement()!
94-
return .delete(at: at)
95-
case 2:
96-
let at = indices.randomElement()!
97-
return .update(at: at, newElement: element)
98-
case 3:
99-
guard let from = indices.filter({ $0.count == 2 }).randomElement() else { return randomOperation(collection: collection) }
100-
var collection = collection
101-
collection.remove(at: from)
102-
let to = collection.indices.filter { $0.count == 2}.randomElement() ?? from // to endindex
103-
return .move(from: from, to: to)
104-
case 4:
105-
guard let from = indices.filter({ $0.count == 1 }).randomElement() else { return randomOperation(collection: collection) }
106-
var collection = collection
107-
collection.remove(at: from)
108-
let to = collection.indices.filter { $0.count == 1 }.randomElement() ?? from // to endindex
109-
return .move(from: from, to: to)
110-
default:
111-
fatalError()
112-
}
113-
}
114-
}

Bond.xcodeproj/project.pbxproj

+136-114
Large diffs are not rendered by default.

Sources/Bond/Data Structures/Array.swift renamed to Sources/Bond/Data Structures/Collection.swift

+27
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,30 @@ extension Array {
4747
insert(element, at: index)
4848
}
4949
}
50+
51+
52+
extension Collection {
53+
54+
func indices(where isIncluded: (Element) -> Bool) -> [Index] {
55+
return indices.filter { isIncluded(self[$0]) }
56+
}
57+
}
58+
59+
extension RangeReplaceableCollection {
60+
61+
public mutating func move(from fromIndex: Index, to toIndex: Index) {
62+
let item = remove(at: fromIndex)
63+
insert(item, at: toIndex)
64+
}
65+
}
66+
67+
extension RangeReplaceableCollection where Index: Strideable {
68+
69+
public mutating func move(from fromIndices: [Index], to toIndex: Index) {
70+
let items = fromIndices.map { self[$0] }
71+
for index in fromIndices.sorted().reversed() {
72+
remove(at: index)
73+
}
74+
insert(contentsOf: items, at: toIndex)
75+
}
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
//
2+
// The MIT License (MIT)
3+
//
4+
// Copyright (c) 2018 DeclarativeHub/Bond
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in
14+
// all copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
// THE SOFTWARE.
23+
//
24+
25+
import Foundation
26+
import ReactiveKit
27+
import Differ
28+
29+
//extension ArrayBasedDiff where Index == IndexPath {
30+
//
31+
// public init(from diff: ExtendedDiff, rootIndex: IndexPath) {
32+
// self.init()
33+
// for element in diff.elements {
34+
// switch element {
35+
// case .insert(let at):
36+
// inserts.append(rootIndex.appending(at))
37+
// case .delete(let at):
38+
// deletes.append(rootIndex.appending(at))
39+
// case .move(let from, let to):
40+
// moves.append((from: rootIndex.appending(from), to: rootIndex.appending(to)))
41+
// }
42+
// }
43+
// }
44+
//}
45+
//
46+
//extension ArrayBasedTreeNode where ChildNode: ArrayBasedTreeNode {
47+
//
48+
// public func treeDiff(_ other: Self, rootIndex: IndexPath = [], areRootsEqual: @escaping (Value, Value) -> Bool, areChildrenEqual: @escaping (ChildNode.Value, ChildNode.Value) -> Bool) -> TreeChangeset<Self>.Diff {
49+
//
50+
// // Check roots
51+
// if rootIndex.isEmpty && !areRootsEqual(value, other.value) {
52+
// return TreeChangeset<Self>.Diff(updates: [rootIndex])
53+
// }
54+
//
55+
// let isEqual: (ChildNode, ChildNode) -> Bool = { a, b in areChildrenEqual(a.value, b.value) }
56+
// let traces = children.outputDiffPathTraces(to: other.children, isEqual: isEqual)
57+
// let levelDiff = Differ.Diff(traces: traces)
58+
// let levelExtendedDiff = children.extendedDiff(from: levelDiff, other: other.children, isEqual: isEqual)
59+
//
60+
// var diff = TreeChangeset<Self>.Diff(from: levelExtendedDiff, rootIndex: rootIndex)
61+
//
62+
// let matchingLevelTraces = traces.filter { trace in
63+
// return trace.from.x + 1 == trace.to.x && trace.from.y + 1 == trace.to.y // Differ matchPoint
64+
// }
65+
//
66+
// for trace in matchingLevelTraces {
67+
// let sourceChild = children[trace.from.x]
68+
// let destinationChild = other.children[trace.from.y]
69+
// let diffRootIndex = rootIndex + [trace.from.y]
70+
// let childDiff = sourceChild.treeDiff(
71+
// destinationChild,
72+
// rootIndex: diffRootIndex,
73+
// areRootsEqual: areChildrenEqual,
74+
// areChildrenEqual: areChildrenEqual
75+
// )
76+
// let childPatch = childDiff.generatePatch(to: sourceChild).map { $0.asAnyArrayBasedOperation }
77+
// diff = ArrayBasedDiff<IndexPath>(from: diff.generatePatch(to: self).map { $0.asAnyArrayBasedOperation } + childPatch)
78+
// }
79+
//
80+
// return diff
81+
// }
82+
//}
83+
//
84+
//extension ArrayBasedTreeNode where ChildNode: ArrayBasedTreeNode, ChildNode.Value: Equatable, Value: Equatable {
85+
//
86+
// /// Diff the receiver against the given tree.
87+
// public func treeDiff(_ other: Self) -> TreeChangeset<Self>.Diff {
88+
// return treeDiff(other, areRootsEqual: { $0 == $1 }, areChildrenEqual: { $0 == $1 })
89+
// }
90+
//}
91+
//
92+
//extension ArrayBasedTreeNode where ChildNode: ArrayBasedTreeNode,ChildNode.Value: Equatable, Value == Void {
93+
//
94+
// /// Diff the receiver against the given tree.
95+
// public func treeDiff(_ other: Self) -> TreeChangeset<Self>.Diff {
96+
// return treeDiff(other, areRootsEqual: { _, _ in true }, areChildrenEqual: { $0 == $1 })
97+
// }
98+
//}
99+
//
100+
//extension SignalProtocol where Element: ArrayBasedTreeNode, Element.ChildNode: ArrayBasedTreeNode {
101+
//
102+
// /// Diff each next element (tree) against the previous one and emit a diff event.
103+
// public func diff(areRootsEqual: @escaping (Element.Value, Element.Value) -> Bool, areChildrenEqual: @escaping (Element.ChildNode.Value, Element.ChildNode.Value) -> Bool) -> Signal<TreeChangeset<Element>, Error> {
104+
// return diff(generateDiff: { $0.treeDiff($1, areRootsEqual: areRootsEqual, areChildrenEqual: areChildrenEqual) })
105+
// }
106+
//}
107+
//
108+
//extension SignalProtocol where Element: ArrayBasedTreeNode, Element.ChildNode: ArrayBasedTreeNode, Element.Value == Element.ChildNode.Value {
109+
//
110+
// /// Diff each next element (tree) against the previous one and emit a diff event.
111+
// public func diff(_ areEqual: @escaping (Element.Value, Element.Value) -> Bool) -> Signal<TreeChangeset<Element>, Error> {
112+
// return diff(generateDiff: { $0.treeDiff($1, areRootsEqual: areEqual, areChildrenEqual: areEqual) })
113+
// }
114+
//}
115+
//
116+
//extension SignalProtocol where Element: ArrayBasedTreeNode, Element.ChildNode: ArrayBasedTreeNode, Element.ChildNode.Value: Equatable, Element.Value: Equatable {
117+
//
118+
// /// Diff each next element (tree) against the previous one and emit a diff event.
119+
// public func diff() -> Signal<TreeChangeset<Element>, Error> {
120+
// return diff(generateDiff: { $0.treeDiff($1) })
121+
// }
122+
//}
123+
//
124+
//extension SignalProtocol where Element: ArrayBasedTreeNode, Element.ChildNode: ArrayBasedTreeNode, Element.ChildNode.Value: Equatable, Element.Value == Void {
125+
//
126+
// /// Diff each next element (tree) against the previous one and emit a diff event.
127+
// public func diff() -> Signal<TreeChangeset<Element>, Error> {
128+
// return diff(generateDiff: { $0.treeDiff($1) })
129+
// }
130+
//}

Sources/Bond/Observable Collections/TreeChangeset.Diff+Patch.swift renamed to Sources/Bond/Observable Collections/ArrayBasedDiff+IndexPath+Patch.swift

+12-12
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@
2424

2525
import Foundation
2626

27-
extension TreeChangeset.Diff {
27+
extension ArrayBasedDiff where Index == IndexPath {
2828

29-
private struct Edit {
29+
private struct Edit<Element> {
3030

3131
var deletionIndex: IndexPath?
3232
var insertionIndex: IndexPath?
33-
var element: Collection.ChildNode?
33+
var element: Element?
3434

35-
var asOperation: TreeChangeset.Operation {
35+
var asOperation: ArrayBasedOperation<Element, Index> {
3636
if let from = deletionIndex, let to = insertionIndex {
3737
return .move(from: from, to: to)
3838
} else if let deletionIndex = deletionIndex {
@@ -45,14 +45,14 @@ extension TreeChangeset.Diff {
4545
}
4646
}
4747

48-
func generatePatch(to collection: Collection) -> [TreeChangeset.Operation] {
48+
public func generatePatch<C: TreeNodeProtocol>(to collection: C) -> [ArrayBasedOperation<C.ChildNode, C.Index>] where C.Index == IndexPath {
4949

50-
let inserts = self.inserts.map { Edit(deletionIndex: nil, insertionIndex: $0, element: collection[$0]) }
51-
let deletes = self.deletes.map { Edit(deletionIndex: $0, insertionIndex: nil, element: nil) }
52-
let moves = self.moves.map { Edit(deletionIndex: $0.from, insertionIndex: $0.to, element: nil) }
50+
let inserts = self.inserts.map { Edit<C.ChildNode>(deletionIndex: nil, insertionIndex: $0, element: collection[$0]) }
51+
let deletes = self.deletes.map { Edit<C.ChildNode>(deletionIndex: $0, insertionIndex: nil, element: nil) }
52+
let moves = self.moves.map { Edit<C.ChildNode>(deletionIndex: $0.from, insertionIndex: $0.to, element: nil) }
5353

54-
func makeInsertionTree(_ script: [Edit]) -> TreeNode<Int> {
55-
func insert(_ edit: Edit, value: Int, into tree: TreeNode<Int>) -> TreeNode<Int> {
54+
func makeInsertionTree(_ script: [Edit<C.ChildNode>]) -> TreeNode<Int> {
55+
func insert(_ edit: Edit<C.ChildNode>, value: Int, into tree: TreeNode<Int>) -> TreeNode<Int> {
5656
var tree = tree
5757
if let insertionIndex = edit.insertionIndex, let index = tree.children.firstIndex(where: { script[$0.value].insertionIndex?.isAncestor(of: insertionIndex) ?? false }) {
5858
tree.children[index] = insert(edit, value: value, into: tree.children[index])
@@ -76,8 +76,8 @@ extension TreeChangeset.Diff {
7676
return tree
7777
}
7878

79-
func makeDeletionTree(_ script: [Edit]) -> TreeNode<Int> {
80-
func insert(_ edit: Edit, value: Int, into tree: TreeNode<Int>) -> TreeNode<Int> {
79+
func makeDeletionTree(_ script: [Edit<C.ChildNode>]) -> TreeNode<Int> {
80+
func insert(_ edit: Edit<C.ChildNode>, value: Int, into tree: TreeNode<Int>) -> TreeNode<Int> {
8181
var tree = tree
8282
if let deletionIndex = edit.deletionIndex, let index = tree.children.firstIndex(where: { script[$0.value].deletionIndex?.isAncestor(of: deletionIndex) ?? false }) {
8383
tree.children[index] = insert(edit, value: value, into: tree.children[index])

0 commit comments

Comments
 (0)