Skip to content

Commit e4b6d09

Browse files
committed
Reimplemented editing table view example to use pure transformation rather then mutate original datasource
1 parent 47411d1 commit e4b6d09

File tree

2 files changed

+105
-48
lines changed

2 files changed

+105
-48
lines changed

Example/EditingExampleViewController.swift

Lines changed: 105 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,97 @@
99
import UIKit
1010
import RxDataSources
1111
import RxSwift
12-
import RxSwift
12+
import RxCocoa
13+
14+
func + <T>(lhs: [T], rhs: T) -> [T] {
15+
var copy = lhs
16+
copy.append(rhs)
17+
return copy
18+
}
19+
20+
enum TableViewEditingCommand<T> {
21+
case AppendItem(item: T, section: Int)
22+
case MoveItem(sourceIndex: NSIndexPath, destinationIndex: NSIndexPath)
23+
case DeleteItem(NSIndexPath)
24+
}
1325

14-
extension Array {
15-
subscript (safe index: Int) -> Element? {
16-
return indices ~= index
17-
? self[index]
18-
: nil
26+
struct SectionedTableViewState<T: AnimatableSectionModelType> {
27+
private var sections: [T]
28+
29+
init(sections: [T]) {
30+
self.sections = sections
31+
}
32+
33+
func executeCommand(command: TableViewEditingCommand<T.Item>) -> SectionedTableViewState {
34+
switch command {
35+
case .AppendItem(let appendEvent):
36+
var sections = self.sections
37+
let items = sections[appendEvent.section].items + appendEvent.item
38+
sections[appendEvent.section] = T(original: sections[appendEvent.section], items: items)
39+
return SectionedTableViewState(sections: sections)
40+
case .DeleteItem(let indexPath):
41+
var sections = self.sections
42+
var items = sections[indexPath.section].items
43+
items.removeAtIndex(indexPath.row)
44+
sections[indexPath.section] = T(original: sections[indexPath.section], items: items)
45+
return SectionedTableViewState(sections: sections)
46+
case .MoveItem(let moveEvent):
47+
var sections = self.sections
48+
var sourceItems = sections[moveEvent.sourceIndex.section].items
49+
var destinationItems = sections[moveEvent.destinationIndex.section].items
50+
51+
if moveEvent.sourceIndex.section == moveEvent.destinationIndex.section {
52+
destinationItems.insert(destinationItems.removeAtIndex(moveEvent.sourceIndex.row),
53+
atIndex: moveEvent.destinationIndex.row)
54+
let destinationSection = T(original: sections[moveEvent.destinationIndex.section], items: destinationItems)
55+
sections[moveEvent.sourceIndex.section] = destinationSection
56+
57+
return SectionedTableViewState(sections: sections)
58+
} else {
59+
let item = sourceItems.removeAtIndex(moveEvent.sourceIndex.row)
60+
destinationItems.insert(item, atIndex: moveEvent.destinationIndex.row)
61+
let sourceSection = T(original: sections[moveEvent.sourceIndex.section], items: sourceItems)
62+
let destinationSection = T(original: sections[moveEvent.destinationIndex.section], items: destinationItems)
63+
sections[moveEvent.sourceIndex.section] = sourceSection
64+
sections[moveEvent.destinationIndex.section] = destinationSection
65+
66+
return SectionedTableViewState(sections: sections)
67+
}
68+
}
69+
}
70+
}
71+
72+
class TableViewEditingCommandsViewModel<T: AnimatableSectionModelType> {
73+
74+
let sectionsChange: Observable<[T]>
75+
76+
init(uiTriggers:(itemAdded: Observable<(item: T.Item, section: Int)>,
77+
itemDeleted: Observable<NSIndexPath>,
78+
itemMoved: Observable<ItemMovedEvent>),
79+
initialData: SectionedTableViewState<T>) {
80+
81+
let addComand = uiTriggers.itemAdded
82+
.map {
83+
return TableViewEditingCommand<T.Item>.AppendItem(item: $0.item, section: $0.section)
84+
}
85+
let deleteCommand = uiTriggers.itemDeleted
86+
.map {
87+
return TableViewEditingCommand<T.Item>.DeleteItem($0)
88+
}
89+
let movedCommand = uiTriggers.itemMoved
90+
.map { (sourceIndex, destinationIndex) -> TableViewEditingCommand<T.Item> in
91+
return TableViewEditingCommand<T.Item>.MoveItem(sourceIndex: sourceIndex, destinationIndex: destinationIndex)
92+
}
93+
94+
sectionsChange = Observable.of(addComand, deleteCommand, movedCommand)
95+
.merge()
96+
.scan(initialData) {
97+
return $0.executeCommand($1)
98+
}
99+
.map {
100+
$0.sections
101+
}
102+
.shareReplay(1)
19103
}
20104
}
21105

@@ -30,37 +114,24 @@ class EditingExampleViewController: UIViewController {
30114
super.viewDidLoad()
31115

32116
let dataSource = RxTableViewSectionedAnimatedDataSource<NumberSection>()
33-
34-
let addition = addButton.rx_tap.asObservable()
35-
.scan(0) { (acum, _) -> Int in
36-
return acum + 1
37-
}
38-
.map { [unowned dataSource] i -> [NumberSection] in
39-
var section = dataSource.sectionModels[safe:0]
40-
section?.appendItem(IntItem(number: i, date: NSDate()))
41-
42-
return [section ?? NumberSection(header: "",
43-
numbers: [IntItem(number: i, date: NSDate())],
44-
updated: NSDate())]
117+
let sections: [NumberSection] = [NumberSection(header: "Section 1", numbers: [], updated: NSDate()),
118+
NumberSection(header: "Section 2", numbers: [], updated: NSDate()),
119+
NumberSection(header: "Section 3", numbers: [], updated: NSDate())]
120+
let initialState = SectionedTableViewState<NumberSection>(sections: sections)
121+
let itemAdded = addButton.rx_tap
122+
.scan(0, accumulator: { $0.0 + 1})
123+
.map { number -> (item: IntItem, section: Int) in
124+
let randSection = Int(arc4random_uniform(UInt32(sections.count)))
125+
return (IntItem(number: number, date: NSDate()), randSection)
45126
}
127+
let itemDeleted = tableView.rx_itemDeleted.asObservable()
128+
let itemMoved = tableView.rx_itemMoved.asObservable()
46129

47-
let deletion = tableView.rx_itemDeleted.asObservable()
48-
.map { [unowned dataSource] indexPath -> [NumberSection] in
49-
var section = dataSource.sectionModels[0]
50-
section.removeItemAtIndex(indexPath.row)
51-
return [section]
52-
}
130+
let viewModel = TableViewEditingCommandsViewModel<NumberSection>(uiTriggers: (itemAdded: itemAdded,
131+
itemDeleted: itemDeleted,
132+
itemMoved: itemMoved), initialData: initialState)
53133

54-
let move = tableView.rx_itemMoved.asObservable()
55-
.map { moveEvent -> [NumberSection] in
56-
var section = dataSource.sectionModels[0]
57-
section.moveItemAtIndex(moveEvent.sourceIndex.row,
58-
toIndex: moveEvent.destinationIndex.row)
59-
return [section]
60-
}
61-
62-
Observable.of(addition, deletion, move)
63-
.merge()
134+
viewModel.sectionsChange
64135
.bindTo(tableView.rx_itemsAnimatedWithDataSource(dataSource))
65136
.addDisposableTo(disposeBag)
66137

Example/NumberSection.swift

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,6 @@ struct NumberSection {
2323
self.numbers = numbers
2424
self.updated = updated
2525
}
26-
27-
mutating func appendItem(item: IntItem) {
28-
numbers.append(item)
29-
}
30-
31-
mutating func removeItemAtIndex(index: Int) {
32-
numbers.removeAtIndex(index)
33-
}
34-
35-
mutating func moveItemAtIndex(index: Int , toIndex newIndex: Int) {
36-
numbers.insert(numbers.removeAtIndex(index), atIndex: newIndex)
37-
}
38-
39-
// mutating func repl
4026
}
4127

4228

0 commit comments

Comments
 (0)