Skip to content

Commit

Permalink
Observable collections cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
srdanrasic committed Sep 30, 2018
1 parent bb086e7 commit 2a253cb
Show file tree
Hide file tree
Showing 25 changed files with 863 additions and 1,023 deletions.
91 changes: 1 addition & 90 deletions Bond-App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,98 +17,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = ViewController()
window?.rootViewController = UIViewController()
window?.makeKeyAndVisible()
return true
}
}

class CustomBinder: TableViewBinderDataSource<TreeChangeset<Array2D<String, Int>>> {

@objc func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return changeset?.dataSource[[section]].value.section
}

// override func applyChageset(_ changeset: TreeChangeset<Array2D<String, Int>>) {
// tableView?.reloadData()
// }
}

class ViewController: UIViewController {


let tableView = UITableView()
let mutableArray = MutableObservableArray2D<String, Int>()

override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)
tableView.estimatedRowHeight = 50
randomOperation()

mutableArray.bind(to: self) { s, d in
print(d.diffs, d.collection)
}

mutableArray.bind(to: tableView, cellType: UITableViewCell.self, using: CustomBinder()) { (cell, data) in
cell.textLabel?.text = "\(data)"
}
}


func randomOperation() {
mutableArray.apply(.randomOperation(collection: mutableArray.value.collection))
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.randomOperation()
}
}

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.frame = view.bounds
}
}

extension TreeChangeset.Operation where Collection == Array2D<String, Int> {

static func randomOperation(collection: Collection) -> TreeChangeset<Collection>.Operation {
let data = [0, 0].randomElement() == 0 ? Array2DElement(item: Int.random(in: 11..<100)) : Array2DElement(section: "\(Int.random(in: 11..<100))")
let element = TreeNode(data)
let indices = collection.indices
guard indices.count > 3 else {
return .insert(TreeNode(Array2DElement(section: "Sec \(Int.random(in: 11..<100))"), [TreeNode(Array2DElement(item: 0))]), at: [0])
}
switch [0, 0, 1, 3, 4].randomElement() {
case 0:
var at = indices.randomElement()!
if Bool.random() && at.count == 1 {
at = at.appending(0)
}
if at.count == 2 {
return .insert(TreeNode(Array2DElement(item: Int.random(in: 11..<100))), at: at)
} else {
return .insert(TreeNode(Array2DElement(section: "\(Int.random(in: 11..<100))")), at: at)
}
case 1:
let at = indices.randomElement()!
return .delete(at: at)
case 2:
let at = indices.randomElement()!
return .update(at: at, newElement: element)
case 3:
guard let from = indices.filter({ $0.count == 2 }).randomElement() else { return randomOperation(collection: collection) }
var collection = collection
collection.remove(at: from)
let to = collection.indices.filter { $0.count == 2}.randomElement() ?? from // to endindex
return .move(from: from, to: to)
case 4:
guard let from = indices.filter({ $0.count == 1 }).randomElement() else { return randomOperation(collection: collection) }
var collection = collection
collection.remove(at: from)
let to = collection.indices.filter { $0.count == 1 }.randomElement() ?? from // to endindex
return .move(from: from, to: to)
default:
fatalError()
}
}
}
250 changes: 136 additions & 114 deletions Bond.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,30 @@ extension Array {
insert(element, at: index)
}
}


extension Collection {

func indices(where isIncluded: (Element) -> Bool) -> [Index] {
return indices.filter { isIncluded(self[$0]) }
}
}

extension RangeReplaceableCollection {

public mutating func move(from fromIndex: Index, to toIndex: Index) {
let item = remove(at: fromIndex)
insert(item, at: toIndex)
}
}

extension RangeReplaceableCollection where Index: Strideable {

public mutating func move(from fromIndices: [Index], to toIndex: Index) {
let items = fromIndices.map { self[$0] }
for index in fromIndices.sorted().reversed() {
remove(at: index)
}
insert(contentsOf: items, at: toIndex)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2018 DeclarativeHub/Bond
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

import Foundation
import ReactiveKit
import Differ

//extension ArrayBasedDiff where Index == IndexPath {
//
// public init(from diff: ExtendedDiff, rootIndex: IndexPath) {
// self.init()
// for element in diff.elements {
// switch element {
// case .insert(let at):
// inserts.append(rootIndex.appending(at))
// case .delete(let at):
// deletes.append(rootIndex.appending(at))
// case .move(let from, let to):
// moves.append((from: rootIndex.appending(from), to: rootIndex.appending(to)))
// }
// }
// }
//}
//
//extension ArrayBasedTreeNode where ChildNode: ArrayBasedTreeNode {
//
// public func treeDiff(_ other: Self, rootIndex: IndexPath = [], areRootsEqual: @escaping (Value, Value) -> Bool, areChildrenEqual: @escaping (ChildNode.Value, ChildNode.Value) -> Bool) -> TreeChangeset<Self>.Diff {
//
// // Check roots
// if rootIndex.isEmpty && !areRootsEqual(value, other.value) {
// return TreeChangeset<Self>.Diff(updates: [rootIndex])
// }
//
// let isEqual: (ChildNode, ChildNode) -> Bool = { a, b in areChildrenEqual(a.value, b.value) }
// let traces = children.outputDiffPathTraces(to: other.children, isEqual: isEqual)
// let levelDiff = Differ.Diff(traces: traces)
// let levelExtendedDiff = children.extendedDiff(from: levelDiff, other: other.children, isEqual: isEqual)
//
// var diff = TreeChangeset<Self>.Diff(from: levelExtendedDiff, rootIndex: rootIndex)
//
// let matchingLevelTraces = traces.filter { trace in
// return trace.from.x + 1 == trace.to.x && trace.from.y + 1 == trace.to.y // Differ matchPoint
// }
//
// for trace in matchingLevelTraces {
// let sourceChild = children[trace.from.x]
// let destinationChild = other.children[trace.from.y]
// let diffRootIndex = rootIndex + [trace.from.y]
// let childDiff = sourceChild.treeDiff(
// destinationChild,
// rootIndex: diffRootIndex,
// areRootsEqual: areChildrenEqual,
// areChildrenEqual: areChildrenEqual
// )
// let childPatch = childDiff.generatePatch(to: sourceChild).map { $0.asAnyArrayBasedOperation }
// diff = ArrayBasedDiff<IndexPath>(from: diff.generatePatch(to: self).map { $0.asAnyArrayBasedOperation } + childPatch)
// }
//
// return diff
// }
//}
//
//extension ArrayBasedTreeNode where ChildNode: ArrayBasedTreeNode, ChildNode.Value: Equatable, Value: Equatable {
//
// /// Diff the receiver against the given tree.
// public func treeDiff(_ other: Self) -> TreeChangeset<Self>.Diff {
// return treeDiff(other, areRootsEqual: { $0 == $1 }, areChildrenEqual: { $0 == $1 })
// }
//}
//
//extension ArrayBasedTreeNode where ChildNode: ArrayBasedTreeNode,ChildNode.Value: Equatable, Value == Void {
//
// /// Diff the receiver against the given tree.
// public func treeDiff(_ other: Self) -> TreeChangeset<Self>.Diff {
// return treeDiff(other, areRootsEqual: { _, _ in true }, areChildrenEqual: { $0 == $1 })
// }
//}
//
//extension SignalProtocol where Element: ArrayBasedTreeNode, Element.ChildNode: ArrayBasedTreeNode {
//
// /// Diff each next element (tree) against the previous one and emit a diff event.
// public func diff(areRootsEqual: @escaping (Element.Value, Element.Value) -> Bool, areChildrenEqual: @escaping (Element.ChildNode.Value, Element.ChildNode.Value) -> Bool) -> Signal<TreeChangeset<Element>, Error> {
// return diff(generateDiff: { $0.treeDiff($1, areRootsEqual: areRootsEqual, areChildrenEqual: areChildrenEqual) })
// }
//}
//
//extension SignalProtocol where Element: ArrayBasedTreeNode, Element.ChildNode: ArrayBasedTreeNode, Element.Value == Element.ChildNode.Value {
//
// /// Diff each next element (tree) against the previous one and emit a diff event.
// public func diff(_ areEqual: @escaping (Element.Value, Element.Value) -> Bool) -> Signal<TreeChangeset<Element>, Error> {
// return diff(generateDiff: { $0.treeDiff($1, areRootsEqual: areEqual, areChildrenEqual: areEqual) })
// }
//}
//
//extension SignalProtocol where Element: ArrayBasedTreeNode, Element.ChildNode: ArrayBasedTreeNode, Element.ChildNode.Value: Equatable, Element.Value: Equatable {
//
// /// Diff each next element (tree) against the previous one and emit a diff event.
// public func diff() -> Signal<TreeChangeset<Element>, Error> {
// return diff(generateDiff: { $0.treeDiff($1) })
// }
//}
//
//extension SignalProtocol where Element: ArrayBasedTreeNode, Element.ChildNode: ArrayBasedTreeNode, Element.ChildNode.Value: Equatable, Element.Value == Void {
//
// /// Diff each next element (tree) against the previous one and emit a diff event.
// public func diff() -> Signal<TreeChangeset<Element>, Error> {
// return diff(generateDiff: { $0.treeDiff($1) })
// }
//}
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@

import Foundation

extension TreeChangeset.Diff {
extension ArrayBasedDiff where Index == IndexPath {

private struct Edit {
private struct Edit<Element> {

var deletionIndex: IndexPath?
var insertionIndex: IndexPath?
var element: Collection.ChildNode?
var element: Element?

var asOperation: TreeChangeset.Operation {
var asOperation: ArrayBasedOperation<Element, Index> {
if let from = deletionIndex, let to = insertionIndex {
return .move(from: from, to: to)
} else if let deletionIndex = deletionIndex {
Expand All @@ -45,14 +45,14 @@ extension TreeChangeset.Diff {
}
}

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

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

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

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

0 comments on commit 2a253cb

Please sign in to comment.