Synchronizes RxDataSources.

This commit is contained in:
Krunoslav Zaher 2016-03-13 14:24:46 +01:00
parent b8c7e520b8
commit 80edd849ff
34 changed files with 1484 additions and 922 deletions

View File

@ -1,66 +0,0 @@
//
// Changeset.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 5/30/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Foundation
import CoreData
#if !RX_NO_MODULE
import RxSwift
import RxCocoa
#endif
struct ItemPath : CustomDebugStringConvertible {
let sectionIndex: Int
let itemIndex: Int
var debugDescription : String {
return "(\(sectionIndex), \(itemIndex))"
}
}
public struct Changeset<S: SectionModelType> : CustomDebugStringConvertible {
typealias I = S.Item
var reloadData: Bool = false
var finalSections: [S] = []
var insertedSections: [Int] = []
var deletedSections: [Int] = []
var movedSections: [(from: Int, to: Int)] = []
var updatedSections: [Int] = []
var insertedItems: [ItemPath] = []
var deletedItems: [ItemPath] = []
var movedItems: [(from: ItemPath, to: ItemPath)] = []
var updatedItems: [ItemPath] = []
public static func initialValue(sections: [S]) -> Changeset<S> {
var initialValue = Changeset<S>()
initialValue.insertedSections = Array(0 ..< sections.count)
initialValue.finalSections = sections
initialValue.reloadData = true
return initialValue
}
public var debugDescription : String {
let serializedSections = "[\n" + finalSections.map { "\($0)" }.joinWithSeparator(",\n") + "\n]\n"
return " >> Final sections"
+ " \n\(serializedSections)"
+ (insertedSections.count > 0 || deletedSections.count > 0 || movedSections.count > 0 || updatedSections.count > 0 ? "\nSections:" : "")
+ (insertedSections.count > 0 ? "\ninsertedSections:\n\t\(insertedSections)" : "")
+ (deletedSections.count > 0 ? "\ndeletedSections:\n\t\(deletedSections)" : "")
+ (movedSections.count > 0 ? "\nmovedSections:\n\t\(movedSections)" : "")
+ (updatedSections.count > 0 ? "\nupdatesSections:\n\t\(updatedSections)" : "")
+ (insertedItems.count > 0 || deletedItems.count > 0 || movedItems.count > 0 || updatedItems.count > 0 ? "\nItems:" : "")
+ (insertedItems.count > 0 ? "\ninsertedItems:\n\t\(insertedItems)" : "")
+ (deletedItems.count > 0 ? "\ndeletedItems:\n\t\(deletedItems)" : "")
+ (movedItems.count > 0 ? "\nmovedItems:\n\t\(movedItems)" : "")
+ (updatedItems.count > 0 ? "\nupdatedItems:\n\t\(updatedItems)" : "")
}
}

View File

@ -1,38 +0,0 @@
//
// RxCollectionViewSectionedAnimatedDataSource.swift
// RxExample
//
// Created by Krunoslav Zaher on 7/2/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Foundation
import UIKit
#if !RX_NO_MODULE
import RxSwift
import RxCocoa
#endif
class RxCollectionViewSectionedAnimatedDataSource<S: SectionModelType> : RxCollectionViewSectionedDataSource<S>
, RxCollectionViewDataSourceType {
typealias Element = [Changeset<S>]
// For some inexplicable reason, when doing animated updates first time
// it crashes. Still need to figure out that one.
var set = false
func collectionView(collectionView: UICollectionView, observedEvent: Event<Element>) {
UIBindingObserver(UIElement: self) { ds, element in
for c in element {
if !ds.set {
ds.setSections(c.finalSections)
collectionView.reloadData()
ds.set = true
return
}
ds.setSections(c.finalSections)
collectionView.performBatchUpdates(c)
}
}.on(observedEvent)
}
}

View File

@ -1,503 +0,0 @@
//
// Differentiator.swift
// RxExample
//
// Created by Krunoslav Zaher on 6/27/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Foundation
public enum DifferentiatorError
: ErrorType
, CustomDebugStringConvertible {
case DuplicateItem(item: Any)
}
extension DifferentiatorError {
public var debugDescription: String {
switch self {
case let .DuplicateItem(item):
return "Duplicate item \(item)"
}
}
}
enum EditEvent : CustomDebugStringConvertible {
case Inserted // can't be found in old sections
case Deleted // Was in old, not in new, in it's place is something "not new" :(, otherwise it's Updated
case Moved // same item, but was on different index, and needs explicit move
case MovedAutomatically // don't need to specify any changes for those rows
case Untouched
}
extension EditEvent {
var debugDescription: String {
switch self {
case .Inserted:
return "Inserted"
case .Deleted:
return "Deleted"
case .Moved:
return "Moved"
case .MovedAutomatically:
return "MovedAutomatically"
case .Untouched:
return "Untouched"
}
}
}
struct SectionAdditionalInfo : CustomDebugStringConvertible {
var event: EditEvent
var indexAfterDelete: Int?
}
extension SectionAdditionalInfo {
var debugDescription: String {
return "\(event), \(indexAfterDelete)"
}
}
struct ItemAdditionalInfo : CustomDebugStringConvertible {
var event: EditEvent
var indexAfterDelete: Int?
}
extension ItemAdditionalInfo {
var debugDescription: String {
return "\(event) \(indexAfterDelete)"
}
}
func indexSections<S: SectionModelType where S: Hashable, S.Item: Hashable>(sections: [S]) throws -> [S : Int] {
var indexedSections: [S : Int] = [:]
for (i, section) in sections.enumerate() {
guard indexedSections[section] == nil else {
#if DEBUG
precondition(indexedSections[section] == nil, "Section \(section) has already been indexed at \(indexedSections[section]!)")
#endif
throw DifferentiatorError.DuplicateItem(item: section)
}
indexedSections[section] = i
}
return indexedSections
}
func indexSectionItems<S: SectionModelType where S: Hashable, S.Item: Hashable>(sections: [S]) throws -> [S.Item : (Int, Int)] {
var totalItems = 0
for i in 0 ..< sections.count {
totalItems += sections[i].items.count
}
// let's make sure it's enough
var indexedItems: [S.Item : (Int, Int)] = Dictionary(minimumCapacity: totalItems * 3)
for i in 0 ..< sections.count {
for (j, item) in sections[i].items.enumerate() {
guard indexedItems[item] == nil else {
#if DEBUG
precondition(indexedItems[item] == nil, "Item \(item) has already been indexed at \(indexedItems[item]!)" )
#endif
throw DifferentiatorError.DuplicateItem(item: item)
}
indexedItems[item] = (i, j)
}
}
return indexedItems
}
/*
I've uncovered this case during random stress testing of logic.
This is the hardest generic update case that causes two passes, first delete, and then move/insert
[
NumberSection(model: "1", items: [1111]),
NumberSection(model: "2", items: [2222]),
]
[
NumberSection(model: "2", items: [0]),
NumberSection(model: "1", items: []),
]
If update is in the form
* Move section from 2 to 1
* Delete Items at paths 0 - 0, 1 - 0
* Insert Items at paths 0 - 0
or
* Move section from 2 to 1
* Delete Items at paths 0 - 0
* Reload Items at paths 1 - 0
or
* Move section from 2 to 1
* Delete Items at paths 0 - 0
* Reload Items at paths 0 - 0
it crashes table view.
No matter what change is performed, it fails for me.
If anyone knows how to make this work for one Changeset, PR is welcome.
*/
// If you are considering working out your own algorithm, these are tricky
// transition cases that you can use.
// case 1
/*
from = [
NumberSection(model: "section 4", items: [10, 11, 12]),
NumberSection(model: "section 9", items: [25, 26, 27]),
]
to = [
HashableSectionModel(model: "section 9", items: [11, 26, 27]),
HashableSectionModel(model: "section 4", items: [10, 12])
]
*/
// case 2
/*
from = [
HashableSectionModel(model: "section 10", items: [26]),
HashableSectionModel(model: "section 7", items: [5, 29]),
HashableSectionModel(model: "section 1", items: [14]),
HashableSectionModel(model: "section 5", items: [16]),
HashableSectionModel(model: "section 4", items: []),
HashableSectionModel(model: "section 8", items: [3, 15, 19, 23]),
HashableSectionModel(model: "section 3", items: [20])
]
to = [
HashableSectionModel(model: "section 10", items: [26]),
HashableSectionModel(model: "section 1", items: [14]),
HashableSectionModel(model: "section 9", items: [3]),
HashableSectionModel(model: "section 5", items: [16, 8]),
HashableSectionModel(model: "section 8", items: [15, 19, 23]),
HashableSectionModel(model: "section 3", items: [20]),
HashableSectionModel(model: "Section 2", items: [7])
]
*/
// case 3
/*
from = [
HashableSectionModel(model: "section 4", items: [5]),
HashableSectionModel(model: "section 6", items: [20, 14]),
HashableSectionModel(model: "section 9", items: []),
HashableSectionModel(model: "section 2", items: [2, 26]),
HashableSectionModel(model: "section 8", items: [23]),
HashableSectionModel(model: "section 10", items: [8, 18, 13]),
HashableSectionModel(model: "section 1", items: [28, 25, 6, 11, 10, 29, 24, 7, 19])
]
to = [
HashableSectionModel(model: "section 4", items: [5]),
HashableSectionModel(model: "section 6", items: [20, 14]),
HashableSectionModel(model: "section 9", items: [16]),
HashableSectionModel(model: "section 7", items: [17, 15, 4]),
HashableSectionModel(model: "section 2", items: [2, 26, 23]),
HashableSectionModel(model: "section 8", items: []),
HashableSectionModel(model: "section 10", items: [8, 18, 13]),
HashableSectionModel(model: "section 1", items: [28, 25, 6, 11, 10, 29, 24, 7, 19])
]
*/
// Generates differential changes suitable for sectioned view consumption.
// It will not only detect changes between two states, but it will also try to compress those changes into
// almost minimal set of changes.
//
// I know, I know, it's ugly :( Totally agree, but this is the only general way I could find that works 100%, and
// avoids UITableView quirks.
//
// Please take into consideration that I was also convinced about 20 times that I've found a simple general
// solution, but then UITableView falls apart under stress testing :(
//
// Sincerely, if somebody else would present me this 250 lines of code, I would call him a mad man. I would think
// that there has to be a simpler solution. Well, after 3 days, I'm not convinced any more :)
//
// Maybe it can be made somewhat simpler, but don't think it can be made much simpler.
//
// The algorithm could take anywhere from 1 to 3 table view transactions to finish the updates.
//
// * stage 1 - remove deleted sections and items
// * stage 2 - move sections into place
// * stage 3 - fix moved and new items
//
// There maybe exists a better division, but time will tell.
//
func differencesForSectionedView<S: SectionModelType where S: Hashable, S.Item: Hashable>(
initialSections: [S],
finalSections: [S]
)
throws -> [Changeset<S>] {
typealias I = S.Item
var deletes = Changeset<S>()
var newAndMovedSections = Changeset<S>()
var newAndMovedItems = Changeset<S>()
var initialSectionInfos = [SectionAdditionalInfo](count: initialSections.count, repeatedValue: SectionAdditionalInfo(event: .Untouched, indexAfterDelete: nil))
var finalSectionInfos = [SectionAdditionalInfo](count: finalSections.count, repeatedValue: SectionAdditionalInfo(event: .Untouched, indexAfterDelete: nil))
var initialSectionIndexes: [S : Int] = [:]
var finalSectionIndexes: [S : Int] = [:]
let defaultItemInfo = ItemAdditionalInfo(event: .Untouched, indexAfterDelete: nil)
var initialItemInfos = initialSections.map { s in
return [ItemAdditionalInfo](count: s.items.count, repeatedValue: defaultItemInfo)
}
var finalItemInfos = finalSections.map { s in
return [ItemAdditionalInfo](count: s.items.count, repeatedValue: defaultItemInfo)
}
initialSectionIndexes = try indexSections(initialSections)
finalSectionIndexes = try indexSections(finalSections)
var initialItemIndexes: [I: (Int, Int)] = try indexSectionItems(initialSections)
var finalItemIndexes: [I: (Int, Int)] = try indexSectionItems(finalSections)
// mark deleted sections {
// 1rst stage
var sectionIndexAfterDelete = 0
for (i, initialSection) in initialSections.enumerate() {
if finalSectionIndexes[initialSection] == nil {
initialSectionInfos[i].event = .Deleted
deletes.deletedSections.append(i)
}
else {
initialSectionInfos[i].indexAfterDelete = sectionIndexAfterDelete
sectionIndexAfterDelete += 1
}
}
deletes.deletedSections = deletes.deletedSections.reverse()
// }
var untouchedOldIndex: Int? = 0
let findNextUntouchedOldIndex = { (initialSearchIndex: Int?) -> Int? in
var i = initialSearchIndex
while i != nil && i < initialSections.count {
if initialSectionInfos[i!].event == .Untouched {
return i
}
i = i! + 1
}
return nil
}
// inserted and moved sections {
// this should fix all sections and move them into correct places
// 2nd stage
for (i, finalSection) in finalSections.enumerate() {
untouchedOldIndex = findNextUntouchedOldIndex(untouchedOldIndex)
// oh, it did exist
if let oldSectionIndex = initialSectionIndexes[finalSection] {
let moveType = oldSectionIndex != untouchedOldIndex ? EditEvent.Moved : EditEvent.MovedAutomatically
finalSectionInfos[i].event = moveType
initialSectionInfos[oldSectionIndex].event = moveType
if moveType == .Moved {
let moveCommand = (from: initialSectionInfos[oldSectionIndex].indexAfterDelete!, to: i)
newAndMovedSections.movedSections.append(moveCommand)
}
}
else {
finalSectionInfos[i].event = .Inserted
newAndMovedSections.insertedSections.append(i)
}
}
// }
// mark deleted items {
// 1rst stage again (I know, I know ...)
for (i, initialSection) in initialSections.enumerate() {
let event = initialSectionInfos[i].event
// Deleted section will take care of deleting child items.
// In case of moving an item from deleted section, tableview will
// crash anyway, so this is not limiting anything.
if event == .Deleted {
continue
}
var indexAfterDelete = 0
for (j, initialItem) in initialSection.items.enumerate() {
if let finalItemIndex = finalItemIndexes[initialItem] {
let targetSectionEvent = finalSectionInfos[finalItemIndex.0].event
// In case there is move of item from existing section into new section
// that is also considered a "delete"
if targetSectionEvent == .Inserted {
initialItemInfos[i][j].event = .Deleted
deletes.deletedItems.append(ItemPath(sectionIndex: i, itemIndex: j))
continue
}
initialItemInfos[i][j].indexAfterDelete = indexAfterDelete
indexAfterDelete += 1
}
else {
initialItemInfos[i][j].event = .Deleted
deletes.deletedItems.append(ItemPath(sectionIndex: i, itemIndex: j))
}
}
}
// }
deletes.deletedItems = deletes.deletedItems.reverse()
// mark new and moved items {
// 3rd stage
for (i, _) in finalSections.enumerate() {
let finalSection = finalSections[i]
let originalSection: Int? = initialSectionIndexes[finalSection]
var untouchedOldIndex: Int? = 0
let findNextUntouchedOldIndex = { (initialSearchIndex: Int?) -> Int? in
var i2 = initialSearchIndex
while originalSection != nil && i2 != nil && i2! < initialItemInfos[originalSection!].count {
if initialItemInfos[originalSection!][i2!].event == .Untouched {
return i2
}
i2 = i2! + 1
}
return nil
}
let sectionEvent = finalSectionInfos[i].event
// new and deleted sections cause reload automatically
if sectionEvent != .Moved && sectionEvent != .MovedAutomatically {
continue
}
for (j, finalItem) in finalSection.items.enumerate() {
let currentItemEvent = finalItemInfos[i][j].event
precondition(currentItemEvent == .Untouched)
untouchedOldIndex = findNextUntouchedOldIndex(untouchedOldIndex)
// ok, so it was moved from somewhere
if let originalIndex = initialItemIndexes[finalItem] {
// In case trying to move from deleted section, abort, otherwise it will crash table view
if initialSectionInfos[originalIndex.0].event == .Deleted {
finalItemInfos[i][j].event = .Inserted
newAndMovedItems.insertedItems.append(ItemPath(sectionIndex: i, itemIndex: j))
}
// original section can't be inserted
else if initialSectionInfos[originalIndex.0].event == .Inserted {
fatalError("New section in initial sections, that is wrong")
}
// what's left is moved section
else {
precondition(initialSectionInfos[originalIndex.0].event == .Moved || initialSectionInfos[originalIndex.0].event == .MovedAutomatically)
let eventType =
originalIndex.0 == (originalSection ?? -1)
&& originalIndex.1 == (untouchedOldIndex ?? -1)
? EditEvent.MovedAutomatically : EditEvent.Moved
// print("\(finalItem) \(eventType) \(originalIndex), \(originalSection) \(untouchedOldIndex)")
initialItemInfos[originalIndex.0][originalIndex.1].event = eventType
finalItemInfos[i][j].event = eventType
if eventType == .Moved {
let finalSectionIndex = finalSectionIndexes[initialSections[originalIndex.0]]!
let moveFromItemWithIndex = initialItemInfos[originalIndex.0][originalIndex.1].indexAfterDelete!
let moveCommand = (
from: ItemPath(sectionIndex: finalSectionIndex, itemIndex: moveFromItemWithIndex),
to: ItemPath(sectionIndex: i, itemIndex: j)
)
newAndMovedItems.movedItems.append(moveCommand)
}
}
}
// if it wasn't moved from anywhere, it's inserted
else {
finalItemInfos[i][j].event = .Inserted
newAndMovedItems.insertedItems.append(ItemPath(sectionIndex: i, itemIndex: j))
}
}
}
// }
var result: [Changeset<S>] = []
if deletes.deletedItems.count > 0 || deletes.deletedSections.count > 0 {
deletes.finalSections = []
for (i, s) in initialSections.enumerate() {
if initialSectionInfos[i].event == .Deleted {
continue
}
var items: [I] = []
for (j, item) in s.items.enumerate() {
if initialItemInfos[i][j].event != .Deleted {
items.append(item)
}
}
deletes.finalSections.append(S(original: s, items: items))
}
result.append(deletes)
}
if newAndMovedSections.insertedSections.count > 0 || newAndMovedSections.movedSections.count > 0 || newAndMovedSections.updatedSections.count != 0 {
// sections should be in place, but items should be original without deleted ones
newAndMovedSections.finalSections = []
for (i, s) in finalSections.enumerate() {
let event = finalSectionInfos[i].event
if event == .Inserted {
// it's already set up
newAndMovedSections.finalSections.append(s)
}
else if event == .Moved || event == .MovedAutomatically {
let originalSectionIndex = initialSectionIndexes[s]!
let originalSection = initialSections[originalSectionIndex]
var items: [I] = []
for (j, item) in originalSection.items.enumerate() {
if initialItemInfos[originalSectionIndex][j].event != .Deleted {
items.append(item)
}
}
newAndMovedSections.finalSections.append(S(original: s, items: items))
}
else {
fatalError("This is weird, this shouldn't happen")
}
}
result.append(newAndMovedSections)
}
newAndMovedItems.finalSections = finalSections
result.append(newAndMovedItems)
return result
}

View File

@ -1,10 +0,0 @@
//
// RxDataSourceStarterKit.swift
// RxExample
//
// Created by Krunoslav Zaher on 8/29/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Foundation

View File

@ -1,60 +0,0 @@
//
// SectionModel.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 6/16/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Foundation
public struct SectionModel<Section, ItemType> : SectionModelType, CustomStringConvertible {
public typealias Item = ItemType
public var model: Section
public var items: [Item]
public init(model: Section, items: [Item]) {
self.model = model
self.items = items
}
public init(original: SectionModel, items: [Item]) {
self.model = original.model
self.items = items
}
public var description: String {
return "\(self.model) > \(items)"
}
}
public struct HashableSectionModel<Section: Hashable, ItemType: Hashable> : Hashable, SectionModelType, CustomStringConvertible {
public typealias Item = ItemType
public var model: Section
public var items: [Item]
public init(model: Section, items: [Item]) {
self.model = model
self.items = items
}
public init(original: HashableSectionModel, items: [Item]) {
self.model = original.model
self.items = items
}
public var description: String {
return "HashableSectionModel(model: \"\(self.model)\", items: \(items))"
}
public var hashValue: Int {
return self.model.hashValue
}
}
public func == <S, I>(lhs: HashableSectionModel<S, I>, rhs: HashableSectionModel<S, I>) -> Bool {
return lhs.model == rhs.model
}

View File

@ -12,10 +12,10 @@ import Foundation
import RxCocoa import RxCocoa
#endif #endif
extension ObservableConvertibleType where E: SequenceType, E.Generator.Element : protocol<SectionModelType, Hashable>, E.Generator.Element.Item: Hashable { extension ObservableConvertibleType where E: SequenceType, E.Generator.Element : AnimatableSectionModelType {
typealias Section = E.Generator.Element typealias Section = E.Generator.Element
func differentiateForSectionedView() public func differentiateForSectionedView()
-> Observable<[Changeset<Section>]> { -> Observable<[Changeset<Section>]> {
return self.asObservable().multicast({ return self.asObservable().multicast({
@ -31,9 +31,9 @@ extension ObservableConvertibleType where E: SequenceType, E.Generator.Element :
do { do {
return try differencesForSectionedView(Array(oldSections), finalSections: Array(newSections)) return try differencesForSectionedView(Array(oldSections), finalSections: Array(newSections))
} }
// in case of error, print it to terminal only // in case of error, print it to terminal only because this is binding to UI Step
catch let e { catch let e {
print(e) rxDebugFatalError(e)
return [Changeset.initialValue(Array(newSections))] return [Changeset.initialValue(Array(newSections))]
} }
} }

View File

@ -0,0 +1,45 @@
//
// RxCollectionViewSectionedAnimatedDataSource.swift
// RxExample
//
// Created by Krunoslav Zaher on 7/2/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Foundation
import UIKit
#if !RX_NO_MODULE
import RxSwift
import RxCocoa
#endif
public class RxCollectionViewSectionedAnimatedDataSource<S: SectionModelType>
: CollectionViewSectionedDataSource<S>
, RxCollectionViewDataSourceType {
public typealias Element = [Changeset<S>]
public var animationConfiguration: AnimationConfiguration? = nil
// For some inexplicable reason, when doing animated updates first time
// it crashes. Still need to figure out that one.
var set = false
public override init() {
super.init()
}
public func collectionView(collectionView: UICollectionView, observedEvent: Event<Element>) {
UIBindingObserver(UIElement: self) { dataSource, element in
for c in element {
if !dataSource.set {
dataSource.setSections(c.finalSections)
collectionView.reloadData()
dataSource.set = true
return
}
dataSource.setSections(c.finalSections)
collectionView.performBatchUpdates(c, animationConfiguration: self.animationConfiguration)
}
}.on(observedEvent)
}
}

View File

@ -13,11 +13,17 @@ import RxSwift
import RxCocoa import RxCocoa
#endif #endif
class RxCollectionViewSectionedReloadDataSource<S: SectionModelType> : RxCollectionViewSectionedDataSource<S> public class RxCollectionViewSectionedReloadDataSource<S: SectionModelType>
, RxCollectionViewDataSourceType { : CollectionViewSectionedDataSource<S>
typealias Element = [S] , RxCollectionViewDataSourceType {
func collectionView(collectionView: UICollectionView, observedEvent: Event<Element>) { public typealias Element = [S]
public override init() {
super.init()
}
public func collectionView(collectionView: UICollectionView, observedEvent: Event<Element>) {
UIBindingObserver(UIElement: self) { dataSource, element in UIBindingObserver(UIElement: self) { dataSource, element in
dataSource.setSections(element) dataSource.setSections(element)
collectionView.reloadData() collectionView.reloadData()

View File

@ -13,23 +13,26 @@ import RxSwift
import RxCocoa import RxCocoa
#endif #endif
/** public class RxTableViewSectionedAnimatedDataSource<S: SectionModelType>
Code for reactive data sources is packed in [RxDataSources](https://github.com/RxSwiftCommunity/RxDataSources) project. : RxTableViewSectionedDataSource<S>
*/ , RxTableViewDataSourceType {
class RxTableViewSectionedAnimatedDataSource<S: SectionModelType> : RxTableViewSectionedDataSource<S>
, RxTableViewDataSourceType {
typealias Element = [Changeset<S>]
func tableView(tableView: UITableView, observedEvent: Event<Element>) { public typealias Element = [Changeset<S>]
public var animationConfiguration: AnimationConfiguration? = nil
public override init() {
super.init()
}
public func tableView(tableView: UITableView, observedEvent: Event<Element>) {
UIBindingObserver(UIElement: self) { dataSource, element in UIBindingObserver(UIElement: self) { dataSource, element in
for c in element { for c in element {
//print("Animating ==============================\n\(c)\n===============================\n")
dataSource.setSections(c.finalSections) dataSource.setSections(c.finalSections)
if c.reloadData { if c.reloadData {
tableView.reloadData() tableView.reloadData()
} }
else { else {
tableView.performBatchUpdates(c) tableView.performBatchUpdates(c, animationConfiguration: self.animationConfiguration)
} }
} }
}.on(observedEvent) }.on(observedEvent)

View File

@ -13,14 +13,16 @@ import RxSwift
import RxCocoa import RxCocoa
#endif #endif
/** public class RxTableViewSectionedReloadDataSource<S: SectionModelType>
Code for reactive data sources is packed in [RxDataSources](https://github.com/RxSwiftCommunity/RxDataSources) project. : RxTableViewSectionedDataSource<S>
*/ , RxTableViewDataSourceType {
class RxTableViewSectionedReloadDataSource<S: SectionModelType> : RxTableViewSectionedDataSource<S> public typealias Element = [S]
, RxTableViewDataSourceType {
typealias Element = [S] public override init() {
super.init()
func tableView(tableView: UITableView, observedEvent: Event<Element>) { }
public func tableView(tableView: UITableView, observedEvent: Event<Element>) {
UIBindingObserver(UIElement: self) { dataSource, element in UIBindingObserver(UIElement: self) { dataSource, element in
dataSource.setSections(element) dataSource.setSections(element)
tableView.reloadData() tableView.reloadData()

View File

@ -16,14 +16,11 @@ import RxCocoa
extension UITableView { extension UITableView {
public func rx_itemsAnimatedWithDataSource< public func rx_itemsAnimatedWithDataSource<
DataSource: protocol<RxTableViewDataSourceType, UITableViewDataSource>, DataSource: protocol<RxTableViewDataSourceType, UITableViewDataSource>,
S: SequenceType,
O: ObservableConvertibleType, O: ObservableConvertibleType,
Section: protocol<SectionModelType, Hashable> Section: AnimatableSectionModelType
where where
DataSource.Element == [Changeset<Section>], DataSource.Element == [Changeset<Section>],
O.E == S, O.E == [Section]
S.Generator.Element == Section,
Section.Item: Hashable
> >
(dataSource: DataSource) (dataSource: DataSource)
(source: O) (source: O)
@ -36,14 +33,11 @@ extension UITableView {
extension UICollectionView { extension UICollectionView {
public func rx_itemsAnimatedWithDataSource< public func rx_itemsAnimatedWithDataSource<
DataSource: protocol<RxCollectionViewDataSourceType, UICollectionViewDataSource>, DataSource: protocol<RxCollectionViewDataSourceType, UICollectionViewDataSource>,
S: SequenceType,
O: ObservableConvertibleType, O: ObservableConvertibleType,
Section: protocol<SectionModelType, Hashable> Section: AnimatableSectionModelType
where where
DataSource.Element == [Changeset<Section>], DataSource.Element == [Changeset<Section>],
O.E == S, O.E == [Section]
S.Generator.Element == Section,
Section.Item: Hashable
> >
(dataSource: DataSource) (dataSource: DataSource)
(source: O) (source: O)

View File

@ -0,0 +1,47 @@
//
// AnimatableSectionModel.swift
// RxDataSources
//
// Created by Krunoslav Zaher on 1/10/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Foundation
public struct AnimatableSectionModel<Section: Hashable, ItemType: Hashable>
: Hashable
, AnimatableSectionModelType
, CustomStringConvertible {
public typealias Item = IdentitifiableValue<ItemType>
public typealias Identity = Section
public var model: Section
public var items: [Item]
public var identity: Section {
return model
}
public init(model: Section, items: [ItemType]) {
self.model = model
self.items = items.map(IdentitifiableValue.init)
}
public init(original: AnimatableSectionModel, items: [Item]) {
self.model = original.model
self.items = items
}
public var description: String {
return "HashableSectionModel(model: \"\(self.model)\", items: \(items))"
}
public var hashValue: Int {
return self.model.hashValue
}
}
public func == <S, I>(lhs: AnimatableSectionModel<S, I>, rhs: AnimatableSectionModel<S, I>) -> Bool {
return lhs.model == rhs.model
}

View File

@ -0,0 +1,15 @@
//
// AnimatableSectionModelType+ItemPath.swift
// RxDataSources
//
// Created by Krunoslav Zaher on 1/9/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Foundation
extension Array where Element: AnimatableSectionModelType {
subscript(index: ItemPath) -> Element.Item {
return self[index.sectionIndex].items[index.itemIndex]
}
}

View File

@ -0,0 +1,17 @@
//
// AnimatableSectionModelType.swift
// RxDataSources
//
// Created by Krunoslav Zaher on 1/6/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Foundation
public protocol AnimatableSectionModelType
: SectionModelType
, IdentifiableType {
typealias Item : IdentifiableType, Equatable
init(original: Self, items: [Item])
}

View File

@ -0,0 +1,27 @@
//
// AnimationConfiguration.swift
// RxDataSources
//
// Created by Esteban Torres on 5/2/16.
// Copyright © 2016 kzaher. All rights reserved.
//
import Foundation
import UIKit
/**
Exposes custom animation styles for insertion, deletion and reloading behavior.
*/
public struct AnimationConfiguration {
let insertAnimation: UITableViewRowAnimation
let reloadAnimation: UITableViewRowAnimation
let deleteAnimation: UITableViewRowAnimation
public init(insertAnimation: UITableViewRowAnimation = .Automatic,
reloadAnimation: UITableViewRowAnimation = .Automatic,
deleteAnimation: UITableViewRowAnimation = .Automatic) {
self.insertAnimation = insertAnimation
self.reloadAnimation = reloadAnimation
self.deleteAnimation = deleteAnimation
}
}

View File

@ -0,0 +1,94 @@
//
// Changeset.swift
// RxDataSources
//
// Created by Krunoslav Zaher on 5/30/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Foundation
import CoreData
#if !RX_NO_MODULE
import RxSwift
import RxCocoa
#endif
public struct Changeset<S: SectionModelType> {
public typealias I = S.Item
public let reloadData: Bool
public let finalSections: [S]
public let insertedSections: [Int]
public let deletedSections: [Int]
public let movedSections: [(from: Int, to: Int)]
public let updatedSections: [Int]
public let insertedItems: [ItemPath]
public let deletedItems: [ItemPath]
public let movedItems: [(from: ItemPath, to: ItemPath)]
public let updatedItems: [ItemPath]
init(reloadData: Bool = false,
finalSections: [S] = [],
insertedSections: [Int] = [],
deletedSections: [Int] = [],
movedSections: [(from: Int, to: Int)] = [],
updatedSections: [Int] = [],
insertedItems: [ItemPath] = [],
deletedItems: [ItemPath] = [],
movedItems: [(from: ItemPath, to: ItemPath)] = [],
updatedItems: [ItemPath] = []
) {
self.reloadData = reloadData
self.finalSections = finalSections
self.insertedSections = insertedSections
self.deletedSections = deletedSections
self.movedSections = movedSections
self.updatedSections = updatedSections
self.insertedItems = insertedItems
self.deletedItems = deletedItems
self.movedItems = movedItems
self.updatedItems = updatedItems
}
public static func initialValue(sections: [S]) -> Changeset<S> {
return Changeset<S>(
insertedSections: Array(0 ..< sections.count) as [Int],
finalSections: sections,
reloadData: true
)
}
}
extension ItemPath
: CustomDebugStringConvertible {
public var debugDescription : String {
return "(\(sectionIndex), \(itemIndex))"
}
}
extension Changeset
: CustomDebugStringConvertible {
public var debugDescription : String {
let serializedSections = "[\n" + finalSections.map { "\($0)" }.joinWithSeparator(",\n") + "\n]\n"
return " >> Final sections"
+ " \n\(serializedSections)"
+ (insertedSections.count > 0 || deletedSections.count > 0 || movedSections.count > 0 || updatedSections.count > 0 ? "\nSections:" : "")
+ (insertedSections.count > 0 ? "\ninsertedSections:\n\t\(insertedSections)" : "")
+ (deletedSections.count > 0 ? "\ndeletedSections:\n\t\(deletedSections)" : "")
+ (movedSections.count > 0 ? "\nmovedSections:\n\t\(movedSections)" : "")
+ (updatedSections.count > 0 ? "\nupdatesSections:\n\t\(updatedSections)" : "")
+ (insertedItems.count > 0 || deletedItems.count > 0 || movedItems.count > 0 || updatedItems.count > 0 ? "\nItems:" : "")
+ (insertedItems.count > 0 ? "\ninsertedItems:\n\t\(insertedItems)" : "")
+ (deletedItems.count > 0 ? "\ndeletedItems:\n\t\(deletedItems)" : "")
+ (movedItems.count > 0 ? "\nmovedItems:\n\t\(movedItems)" : "")
+ (updatedItems.count > 0 ? "\nupdatedItems:\n\t\(updatedItems)" : "")
}
}

View File

@ -1,6 +1,6 @@
// //
// RxCollectionViewSectionedDataSource.swift // CollectionViewSectionedDataSource.swift
// RxExample // RxDataSources
// //
// Created by Krunoslav Zaher on 7/2/15. // Created by Krunoslav Zaher on 7/2/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved. // Copyright © 2015 Krunoslav Zaher. All rights reserved.
@ -8,13 +8,11 @@
import Foundation import Foundation
import UIKit import UIKit
#if !RX_NO_MODULE
import RxSwift
import RxCocoa import RxCocoa
#endif
public class _RxCollectionViewSectionedDataSource : NSObject public class _CollectionViewSectionedDataSource
, UICollectionViewDataSource { : NSObject
, UICollectionViewDataSource {
func _numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { func _numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 0 return 0
@ -47,17 +45,31 @@ public class _RxCollectionViewSectionedDataSource : NSObject
public func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView { public func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
return _collectionView(collectionView, viewForSupplementaryElementOfKind: kind, atIndexPath: indexPath) return _collectionView(collectionView, viewForSupplementaryElementOfKind: kind, atIndexPath: indexPath)
} }
func _collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return false
}
public func collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return _collectionView(collectionView, canMoveItemAtIndexPath: indexPath)
}
func _collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
}
public func collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
_collectionView(collectionView, moveItemAtIndexPath: sourceIndexPath, toIndexPath: destinationIndexPath)
}
} }
public class RxCollectionViewSectionedDataSource<S: SectionModelType> : _RxCollectionViewSectionedDataSource { public class CollectionViewSectionedDataSource<S: SectionModelType>
: _CollectionViewSectionedDataSource
, SectionedViewDataSourceType {
public typealias I = S.Item public typealias I = S.Item
public typealias Section = S public typealias Section = S
public typealias CellFactory = (UICollectionView, NSIndexPath, I) -> UICollectionViewCell public typealias CellFactory = (CollectionViewSectionedDataSource<S>, UICollectionView, NSIndexPath, I) -> UICollectionViewCell
public typealias SupplementaryViewFactory = (UICollectionView, String, NSIndexPath) -> UICollectionReusableView public typealias SupplementaryViewFactory = (CollectionViewSectionedDataSource<S>, UICollectionView, String, NSIndexPath) -> UICollectionReusableView
public typealias IncrementalUpdateObserver = AnyObserver<Changeset<S>>
public typealias IncrementalUpdateDisposeKey = Bag<IncrementalUpdateObserver>.KeyType
// This structure exists because model can be mutable // This structure exists because model can be mutable
// In that case current state value should be preserved. // In that case current state value should be preserved.
@ -72,11 +84,15 @@ public class RxCollectionViewSectionedDataSource<S: SectionModelType> : _RxColle
public func sectionAtIndex(section: Int) -> S { public func sectionAtIndex(section: Int) -> S {
return self._sectionModels[section].model return self._sectionModels[section].model
} }
public func itemAtIndexPath(indexPath: NSIndexPath) -> I { public func itemAtIndexPath(indexPath: NSIndexPath) -> I {
return self._sectionModels[indexPath.section].items[indexPath.item] return self._sectionModels[indexPath.section].items[indexPath.item]
} }
public func modelAtIndexPath(indexPath: NSIndexPath) throws -> Any {
return itemAtIndexPath(indexPath)
}
public func setSections(sections: [S]) { public func setSections(sections: [S]) {
self._sectionModels = sections.map { SectionModelSnapshot(model: $0, items: $0.items) } self._sectionModels = sections.map { SectionModelSnapshot(model: $0, items: $0.items) }
} }
@ -84,9 +100,12 @@ public class RxCollectionViewSectionedDataSource<S: SectionModelType> : _RxColle
public var cellFactory: CellFactory! = nil public var cellFactory: CellFactory! = nil
public var supplementaryViewFactory: SupplementaryViewFactory public var supplementaryViewFactory: SupplementaryViewFactory
public var moveItem: ((CollectionViewSectionedDataSource<S>, sourceIndexPath:NSIndexPath, destinationIndexPath:NSIndexPath) -> Void)?
public var canMoveItemAtIndexPath: ((CollectionViewSectionedDataSource<S>, indexPath:NSIndexPath) -> Bool)?
public override init() { public override init() {
self.cellFactory = { _, _, _ in return (nil as UICollectionViewCell?)! } self.cellFactory = {_, _, _, _ in return (nil as UICollectionViewCell?)! }
self.supplementaryViewFactory = { _, _, _ in (nil as UICollectionReusableView?)! } self.supplementaryViewFactory = {_, _, _, _ in (nil as UICollectionReusableView?)! }
super.init() super.init()
@ -96,13 +115,13 @@ public class RxCollectionViewSectionedDataSource<S: SectionModelType> : _RxColle
return (nil as UICollectionViewCell!)! return (nil as UICollectionViewCell!)!
} }
self.supplementaryViewFactory = { [weak self] _, _, _ in self.supplementaryViewFactory = { [weak self] _ in
precondition(false, "There is a minor problem. `supplementaryViewFactory` property on \(self!) was not set.") precondition(false, "There is a minor problem. `supplementaryViewFactory` property on \(self!) was not set.")
return (nil as UICollectionReusableView?)! return (nil as UICollectionReusableView?)!
} }
} }
// UITableViewDataSource // UICollectionViewDataSource
override func _numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { override func _numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return _sectionModels.count return _sectionModels.count
@ -115,10 +134,21 @@ public class RxCollectionViewSectionedDataSource<S: SectionModelType> : _RxColle
override func _collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { override func _collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
precondition(indexPath.item < _sectionModels[indexPath.section].items.count) precondition(indexPath.item < _sectionModels[indexPath.section].items.count)
return cellFactory(collectionView, indexPath, itemAtIndexPath(indexPath)) return cellFactory(self, collectionView, indexPath, itemAtIndexPath(indexPath))
} }
override func _collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView { override func _collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
return supplementaryViewFactory(collectionView, kind, indexPath) return supplementaryViewFactory(self, collectionView, kind, indexPath)
} }
override func _collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return canMoveItemAtIndexPath?(self, indexPath: indexPath) ??
super._collectionView(collectionView, canMoveItemAtIndexPath: indexPath)
}
override func _collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
return moveItem?(self, sourceIndexPath:sourceIndexPath, destinationIndexPath: destinationIndexPath) ??
super._collectionView(collectionView, moveItemAtIndexPath: sourceIndexPath, toIndexPath: destinationIndexPath)
}
} }

View File

@ -0,0 +1,35 @@
//
// DataSources.swift
// RxDataSources
//
// Created by Krunoslav Zaher on 1/8/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Foundation
enum RxDataSourceError : ErrorType {
case UnwrappingOptional
case PreconditionFailed(message: String)
}
func rxPrecondition(condition: Bool, @autoclosure _ message: () -> String) throws -> () {
if condition {
return
}
rxDebugFatalError("Precondition failed")
throw RxDataSourceError.PreconditionFailed(message: message())
}
func rxDebugFatalError(error: ErrorType) {
rxDebugFatalError("\(error)")
}
func rxDebugFatalError(message: String) {
#if DEBUG
fatalError(message)
#else
print(message)
#endif
}

View File

@ -0,0 +1,685 @@
//
// Differentiator.swift
// RxDataSources
//
// Created by Krunoslav Zaher on 6/27/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Foundation
public enum DifferentiatorError
: ErrorType
, CustomDebugStringConvertible {
case DuplicateItem(item: Any)
case DuplicateSection(section: Any)
}
extension DifferentiatorError {
public var debugDescription: String {
switch self {
case let .DuplicateItem(item):
return "Duplicate item \(item)"
case let .DuplicateSection(section):
return "Duplicate section \(section)"
}
}
}
enum EditEvent : CustomDebugStringConvertible {
case Inserted // can't be found in old sections
case InsertedAutomatically // Item inside section being inserted
case Deleted // Was in old, not in new, in it's place is something "not new" :(, otherwise it's Updated
case DeletedAutomatically // Item inside section that is being deleted
case Moved // same item, but was on different index, and needs explicit move
case MovedAutomatically // don't need to specify any changes for those rows
case Untouched
}
extension EditEvent {
var debugDescription: String {
get {
switch self {
case .Inserted:
return "Inserted"
case .InsertedAutomatically:
return "InsertedAutomatically"
case .Deleted:
return "Deleted"
case .DeletedAutomatically:
return "DeletedAutomatically"
case .Moved:
return "Moved"
case .MovedAutomatically:
return "MovedAutomatically"
case .Untouched:
return "Untouched"
}
}
}
}
struct SectionAssociatedData {
var event: EditEvent
var indexAfterDelete: Int?
var moveIndex: Int?
}
extension SectionAssociatedData : CustomDebugStringConvertible {
var debugDescription: String {
get {
return "\(event), \(indexAfterDelete)"
}
}
}
extension SectionAssociatedData {
static var initial: SectionAssociatedData {
return SectionAssociatedData(event: .Untouched, indexAfterDelete: nil, moveIndex: nil)
}
}
struct ItemAssociatedData {
var event: EditEvent
var indexAfterDelete: Int?
var moveIndex: ItemPath?
}
extension ItemAssociatedData : CustomDebugStringConvertible {
var debugDescription: String {
get {
return "\(event) \(indexAfterDelete)"
}
}
}
extension ItemAssociatedData {
static var initial : ItemAssociatedData {
return ItemAssociatedData(event: .Untouched, indexAfterDelete: nil, moveIndex: nil)
}
}
func indexSections<S: AnimatableSectionModelType>(sections: [S]) throws -> [S.Identity : Int] {
var indexedSections: [S.Identity : Int] = [:]
for (i, section) in sections.enumerate() {
guard indexedSections[section.identity] == nil else {
#if DEBUG
precondition(indexedSections[section.identity] == nil, "Section \(section) has already been indexed at \(indexedSections[section]!)")
#endif
throw DifferentiatorError.DuplicateItem(item: section)
}
indexedSections[section.identity] = i
}
return indexedSections
}
func indexSectionItems<S: AnimatableSectionModelType>(sections: [S]) throws -> [S.Item.Identity : (Int, Int)] {
var totalItems = 0
for i in 0 ..< sections.count {
totalItems += sections[i].items.count
}
// let's make sure it's enough
var indexedItems: [S.Item.Identity : (Int, Int)] = Dictionary(minimumCapacity: totalItems * 3)
for i in 0 ..< sections.count {
for (j, item) in sections[i].items.enumerate() {
guard indexedItems[item.identity] == nil else {
#if DEBUG
precondition(indexedItems[item.identity] == nil, "Item \(item) has already been indexed at \(indexedItems[item]!)" )
#endif
throw DifferentiatorError.DuplicateItem(item: item)
}
indexedItems[item.identity] = (i, j)
}
}
return indexedItems
}
/*
I've uncovered this case during random stress testing of logic.
This is the hardest generic update case that causes two passes, first delete, and then move/insert
[
NumberSection(model: "1", items: [1111]),
NumberSection(model: "2", items: [2222]),
]
[
NumberSection(model: "2", items: [0]),
NumberSection(model: "1", items: []),
]
If update is in the form
* Move section from 2 to 1
* Delete Items at paths 0 - 0, 1 - 0
* Insert Items at paths 0 - 0
or
* Move section from 2 to 1
* Delete Items at paths 0 - 0
* Reload Items at paths 1 - 0
or
* Move section from 2 to 1
* Delete Items at paths 0 - 0
* Reload Items at paths 0 - 0
it crashes table view.
No matter what change is performed, it fails for me.
If anyone knows how to make this work for one Changeset, PR is welcome.
*/
// If you are considering working out your own algorithm, these are tricky
// transition cases that you can use.
// case 1
/*
from = [
NumberSection(model: "section 4", items: [10, 11, 12]),
NumberSection(model: "section 9", items: [25, 26, 27]),
]
to = [
HashableSectionModel(model: "section 9", items: [11, 26, 27]),
HashableSectionModel(model: "section 4", items: [10, 12])
]
*/
// case 2
/*
from = [
HashableSectionModel(model: "section 10", items: [26]),
HashableSectionModel(model: "section 7", items: [5, 29]),
HashableSectionModel(model: "section 1", items: [14]),
HashableSectionModel(model: "section 5", items: [16]),
HashableSectionModel(model: "section 4", items: []),
HashableSectionModel(model: "section 8", items: [3, 15, 19, 23]),
HashableSectionModel(model: "section 3", items: [20])
]
to = [
HashableSectionModel(model: "section 10", items: [26]),
HashableSectionModel(model: "section 1", items: [14]),
HashableSectionModel(model: "section 9", items: [3]),
HashableSectionModel(model: "section 5", items: [16, 8]),
HashableSectionModel(model: "section 8", items: [15, 19, 23]),
HashableSectionModel(model: "section 3", items: [20]),
HashableSectionModel(model: "Section 2", items: [7])
]
*/
// case 3
/*
from = [
HashableSectionModel(model: "section 4", items: [5]),
HashableSectionModel(model: "section 6", items: [20, 14]),
HashableSectionModel(model: "section 9", items: []),
HashableSectionModel(model: "section 2", items: [2, 26]),
HashableSectionModel(model: "section 8", items: [23]),
HashableSectionModel(model: "section 10", items: [8, 18, 13]),
HashableSectionModel(model: "section 1", items: [28, 25, 6, 11, 10, 29, 24, 7, 19])
]
to = [
HashableSectionModel(model: "section 4", items: [5]),
HashableSectionModel(model: "section 6", items: [20, 14]),
HashableSectionModel(model: "section 9", items: [16]),
HashableSectionModel(model: "section 7", items: [17, 15, 4]),
HashableSectionModel(model: "section 2", items: [2, 26, 23]),
HashableSectionModel(model: "section 8", items: []),
HashableSectionModel(model: "section 10", items: [8, 18, 13]),
HashableSectionModel(model: "section 1", items: [28, 25, 6, 11, 10, 29, 24, 7, 19])
]
*/
// Generates differential changes suitable for sectioned view consumption.
// It will not only detect changes between two states, but it will also try to compress those changes into
// almost minimal set of changes.
//
// I know, I know, it's ugly :( Totally agree, but this is the only general way I could find that works 100%, and
// avoids UITableView quirks.
//
// Please take into consideration that I was also convinced about 20 times that I've found a simple general
// solution, but then UITableView falls apart under stress testing :(
//
// Sincerely, if somebody else would present me this 250 lines of code, I would call him a mad man. I would think
// that there has to be a simpler solution. Well, after 3 days, I'm not convinced any more :)
//
// Maybe it can be made somewhat simpler, but don't think it can be made much simpler.
//
// The algorithm could take anywhere from 1 to 3 table view transactions to finish the updates.
//
// * stage 1 - remove deleted sections and items
// * stage 2 - move sections into place
// * stage 3 - fix moved and new items
//
// There maybe exists a better division, but time will tell.
//
public func differencesForSectionedView<S: AnimatableSectionModelType>(
initialSections: [S],
finalSections: [S]
)
throws -> [Changeset<S>] {
typealias I = S.Item
var result: [Changeset<S>] = []
var sectionCommands = try CommandGenerator<S>.generatorForInitialSections(initialSections, finalSections: finalSections)
result.appendContentsOf(try sectionCommands.generateDeleteSections())
result.appendContentsOf(try sectionCommands.generateInsertAndMoveSections())
result.appendContentsOf(try sectionCommands.generateNewAndMovedItems())
return result
}
struct CommandGenerator<S: AnimatableSectionModelType> {
let initialSections: [S]
let finalSections: [S]
let initialSectionData: [SectionAssociatedData]
let finalSectionData: [SectionAssociatedData]
let initialItemData: [[ItemAssociatedData]]
let finalItemData: [[ItemAssociatedData]]
static func generatorForInitialSections(
initialSections: [S],
finalSections: [S]
) throws -> CommandGenerator<S> {
let (initialSectionData, finalSectionData) = try calculateSectionMovementsForInitialSections(initialSections, finalSections: finalSections)
let (initialItemData, finalItemData) = try calculateItemMovementsForInitialSections(initialSections,
finalSections: finalSections,
initialSectionData: initialSectionData,
finalSectionData: finalSectionData
)
return CommandGenerator<S>(
initialSections: initialSections,
finalSections: finalSections,
initialSectionData: initialSectionData,
finalSectionData: finalSectionData,
initialItemData: initialItemData,
finalItemData: finalItemData
)
}
static func calculateItemMovementsForInitialSections(initialSections: [S], finalSections: [S],
initialSectionData: [SectionAssociatedData], finalSectionData: [SectionAssociatedData]) throws -> ([[ItemAssociatedData]], [[ItemAssociatedData]]) {
var initialItemData = initialSections.map { s in
return [ItemAssociatedData](count: s.items.count, repeatedValue: ItemAssociatedData.initial)
}
var finalItemData = finalSections.map { s in
return [ItemAssociatedData](count: s.items.count, repeatedValue: ItemAssociatedData.initial)
}
let initialItemIndexes = try indexSectionItems(initialSections)
for i in 0 ..< finalSections.count {
for (j, item) in finalSections[i].items.enumerate() {
guard let initialItemIndex = initialItemIndexes[item.identity] else {
continue
}
if initialItemData[initialItemIndex.0][initialItemIndex.1].moveIndex != nil {
throw DifferentiatorError.DuplicateItem(item: item)
}
initialItemData[initialItemIndex.0][initialItemIndex.1].moveIndex = ItemPath(sectionIndex: i, itemIndex: j)
finalItemData[i][j].moveIndex = ItemPath(sectionIndex: initialItemIndex.0, itemIndex: initialItemIndex.1)
}
}
let findNextUntouchedOldIndex = { (initialSectionIndex: Int, initialSearchIndex: Int?) -> Int? in
guard var i2 = initialSearchIndex else {
return nil
}
while i2 < initialSections[initialSectionIndex].items.count {
if initialItemData[initialSectionIndex][i2].event == .Untouched {
return i2
}
i2 = i2 + 1
}
return nil
}
// first mark deleted items
for i in 0 ..< initialSections.count {
guard let _ = initialSectionData[i].moveIndex else {
continue
}
var indexAfterDelete = 0
for j in 0 ..< initialSections[i].items.count {
guard let finalIndexPath = initialItemData[i][j].moveIndex else {
initialItemData[i][j].event = .Deleted
continue
}
// from this point below, section has to be move type because it's initial and not deleted
// because there is no move to inserted section
if finalSectionData[finalIndexPath.sectionIndex].event == .Inserted {
initialItemData[i][j].event = .Deleted
continue
}
initialItemData[i][j].indexAfterDelete = indexAfterDelete
indexAfterDelete += 1
}
}
// mark moved or moved automatically
for i in 0 ..< finalSections.count {
guard let originalSectionIndex = finalSectionData[i].moveIndex else {
continue
}
var untouchedIndex: Int? = 0
for j in 0 ..< finalSections[i].items.count {
untouchedIndex = findNextUntouchedOldIndex(originalSectionIndex, untouchedIndex)
guard let originalIndex = finalItemData[i][j].moveIndex else {
finalItemData[i][j].event = .Inserted
continue
}
// In case trying to move from deleted section, abort, otherwise it will crash table view
if initialSectionData[originalIndex.sectionIndex].event == .Deleted {
finalItemData[i][j].event = .Inserted
continue
}
// original section can't be inserted
else if initialSectionData[originalIndex.sectionIndex].event == .Inserted {
try rxPrecondition(false, "New section in initial sections, that is wrong")
}
let initialSectionEvent = initialSectionData[originalIndex.sectionIndex].event
try rxPrecondition(initialSectionEvent == .Moved || initialSectionEvent == .MovedAutomatically, "Section not moved")
let eventType = originalIndex == ItemPath(sectionIndex: originalSectionIndex, itemIndex: untouchedIndex ?? -1)
? EditEvent.MovedAutomatically : EditEvent.Moved
initialItemData[originalIndex.sectionIndex][originalIndex.itemIndex].event = eventType
finalItemData[i][j].event = eventType
}
}
return (initialItemData, finalItemData)
}
static func calculateSectionMovementsForInitialSections(initialSections: [S], finalSections: [S]) throws -> ([SectionAssociatedData], [SectionAssociatedData]) {
let initialSectionIndexes = try indexSections(initialSections)
var initialSectionData = [SectionAssociatedData](count: initialSections.count, repeatedValue: SectionAssociatedData.initial)
var finalSectionData = [SectionAssociatedData](count: finalSections.count, repeatedValue: SectionAssociatedData.initial)
for (i, section) in finalSections.enumerate() {
guard let initialSectionIndex = initialSectionIndexes[section.identity] else {
continue
}
if initialSectionData[initialSectionIndex].moveIndex != nil {
throw DifferentiatorError.DuplicateSection(section: section)
}
initialSectionData[initialSectionIndex].moveIndex = i
finalSectionData[i].moveIndex = initialSectionIndex
}
var sectionIndexAfterDelete = 0
// deleted sections
for i in 0 ..< initialSectionData.count {
if initialSectionData[i].moveIndex == nil {
initialSectionData[i].event = .Deleted
continue
}
initialSectionData[i].indexAfterDelete = sectionIndexAfterDelete
sectionIndexAfterDelete += 1
}
// moved sections
var untouchedOldIndex: Int? = 0
let findNextUntouchedOldIndex = { (initialSearchIndex: Int?) -> Int? in
guard var i = initialSearchIndex else {
return nil
}
while i < initialSections.count {
if initialSectionData[i].event == .Untouched {
return i
}
i = i + 1
}
return nil
}
// inserted and moved sections {
// this should fix all sections and move them into correct places
// 2nd stage
for i in 0 ..< finalSections.count {
untouchedOldIndex = findNextUntouchedOldIndex(untouchedOldIndex)
// oh, it did exist
if let oldSectionIndex = finalSectionData[i].moveIndex {
let moveType = oldSectionIndex != untouchedOldIndex ? EditEvent.Moved : EditEvent.MovedAutomatically
finalSectionData[i].event = moveType
initialSectionData[oldSectionIndex].event = moveType
}
else {
finalSectionData[i].event = .Inserted
}
}
// inserted sections
for (i, section) in finalSectionData.enumerate() {
if section.moveIndex == nil {
finalSectionData[i].event == .Inserted
}
}
return (initialSectionData, finalSectionData)
}
mutating func generateDeleteSections() throws -> [Changeset<S>] {
var deletedSections = [Int]()
var deletedItems = [ItemPath]()
var updatedItems = [ItemPath]()
var afterDeleteState = [S]()
// mark deleted items {
// 1rst stage again (I know, I know ...)
for (i, initialSection) in initialSections.enumerate() {
let event = initialSectionData[i].event
// Deleted section will take care of deleting child items.
// In case of moving an item from deleted section, tableview will
// crash anyway, so this is not limiting anything.
if event == .Deleted {
deletedSections.append(i)
continue
}
var afterDeleteItems: [S.Item] = []
for j in 0 ..< initialSection.items.count {
let event = initialItemData[i][j].event
switch event {
case .Deleted:
deletedItems.append(ItemPath(sectionIndex: i, itemIndex: j))
case .Moved, .MovedAutomatically:
let finalItemIndex = try initialItemData[i][j].moveIndex.unwrap()
let finalItem = finalSections[finalItemIndex]
if finalItem != initialSections[i].items[j] {
updatedItems.append(ItemPath(sectionIndex: i, itemIndex: j))
}
afterDeleteItems.append(finalItem)
default:
try rxPrecondition(false, "Unhandled case")
}
}
afterDeleteState.append(S(original: initialSection, items: afterDeleteItems))
}
// }
if deletedItems.count == 0 && deletedSections.count == 0 && updatedItems.count == 0 {
return []
}
return [Changeset(
finalSections: afterDeleteState,
deletedSections: deletedSections,
deletedItems: deletedItems,
updatedItems: updatedItems
)]
}
func generateInsertAndMoveSections() throws -> [Changeset<S>] {
var movedSections = [(from: Int, to: Int)]()
var insertedSections = [Int]()
for i in 0 ..< initialSections.count {
switch initialSectionData[i].event {
case .Deleted:
break
case .Moved:
movedSections.append((from: try initialSectionData[i].indexAfterDelete.unwrap(), to: try initialSectionData[i].moveIndex.unwrap()))
case .MovedAutomatically:
break
default:
try rxPrecondition(false, "Unhandled case in initial sections")
}
}
for i in 0 ..< finalSections.count {
switch finalSectionData[i].event {
case .Inserted:
insertedSections.append(i)
default:
break
}
}
if insertedSections.count == 0 && movedSections.count == 0 {
return []
}
// sections should be in place, but items should be original without deleted ones
let sectionsAfterChange: [S] = try self.finalSections.enumerate().map { i, s -> S in
let event = self.finalSectionData[i].event
if event == .Inserted {
// it's already set up
return s
}
else if event == .Moved || event == .MovedAutomatically {
let originalSectionIndex = try finalSectionData[i].moveIndex.unwrap()
let originalSection = initialSections[originalSectionIndex]
var items: [S.Item] = []
for (j, _) in originalSection.items.enumerate() {
let initialData = self.initialItemData[originalSectionIndex][j]
guard initialData.event != .Deleted else {
continue
}
guard let finalIndex = initialData.moveIndex else {
try rxPrecondition(false, "Item was moved, but no final location.")
continue
}
items.append(self.finalSections[finalIndex.sectionIndex].items[finalIndex.itemIndex])
}
return S(original: s, items: items)
}
else {
try rxPrecondition(false, "This is weird, this shouldn't happen")
return s
}
}
return [Changeset(
finalSections: sectionsAfterChange,
insertedSections: insertedSections,
movedSections: movedSections
)]
}
mutating func generateNewAndMovedItems() throws -> [Changeset<S>] {
var insertedItems = [ItemPath]()
var movedItems = [(from: ItemPath, to: ItemPath)]()
// mark new and moved items {
// 3rd stage
for i in 0 ..< finalSections.count {
let finalSection = finalSections[i]
let sectionEvent = finalSectionData[i].event
// new and deleted sections cause reload automatically
if sectionEvent != .Moved && sectionEvent != .MovedAutomatically {
continue
}
for j in 0 ..< finalSection.items.count {
let currentItemEvent = finalItemData[i][j].event
try rxPrecondition(currentItemEvent != .Untouched, "Current event is not untouched")
let event = finalItemData[i][j].event
switch event {
case .Inserted:
insertedItems.append(ItemPath(sectionIndex: i, itemIndex: j))
case .Moved:
let originalIndex = try finalItemData[i][j].moveIndex.unwrap()
let finalSectionIndex = try initialSectionData[originalIndex.sectionIndex].moveIndex.unwrap()
let moveFromItemWithIndex = try initialItemData[originalIndex.sectionIndex][originalIndex.itemIndex].indexAfterDelete.unwrap()
let moveCommand = (
from: ItemPath(sectionIndex: finalSectionIndex, itemIndex: moveFromItemWithIndex),
to: ItemPath(sectionIndex: i, itemIndex: j)
)
movedItems.append(moveCommand)
default:
break
}
}
}
// }
if insertedItems.count == 0 && movedItems.count == 0 {
return []
}
return [Changeset(
finalSections: finalSections,
insertedItems: insertedItems,
movedItems: movedItems
)]
}
}

View File

@ -0,0 +1,15 @@
//
// IdentifiableType.swift
// RxDataSources
//
// Created by Krunoslav Zaher on 1/6/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Foundation
public protocol IdentifiableType {
typealias Identity: Hashable
var identity : Identity { get }
}

View File

@ -0,0 +1,35 @@
//
// IdentifiableValue.swift
// RxDataSources
//
// Created by Krunoslav Zaher on 1/7/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Foundation
public struct IdentitifiableValue<Value: Hashable>
: IdentifiableType
, Equatable
, CustomStringConvertible
, CustomDebugStringConvertible {
public typealias Identity = Value
public let value: Value
public var identity : Identity {
return value
}
public var description: String {
return "\(value)"
}
public var debugDescription: String {
return "\(value)"
}
}
public func == <V: Hashable>(lhs: IdentitifiableValue<V>, rhs: IdentitifiableValue<V>) -> Bool {
return lhs.value == rhs.value
}

View File

@ -0,0 +1,22 @@
//
// ItemPath.swift
// RxDataSources
//
// Created by Krunoslav Zaher on 1/9/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Foundation
public struct ItemPath {
public let sectionIndex: Int
public let itemIndex: Int
}
extension ItemPath : Equatable {
}
public func == (lhs: ItemPath, rhs: ItemPath) -> Bool {
return lhs.sectionIndex == rhs.sectionIndex && lhs.itemIndex == rhs.itemIndex
}

View File

@ -0,0 +1,21 @@
//
// Optional+Extensions.swift
// RxDataSources
//
// Created by Krunoslav Zaher on 1/8/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Foundation
extension Optional {
func unwrap() throws -> Wrapped {
if let unwrapped = self {
return unwrapped
}
else {
rxDebugFatalError("Error during unwrapping optional")
throw RxDataSourceError.UnwrappingOptional
}
}
}

View File

@ -0,0 +1,33 @@
//
// SectionModel.swift
// RxDataSources
//
// Created by Krunoslav Zaher on 6/16/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import Foundation
public struct SectionModel<Section, ItemType>
: SectionModelType
, CustomStringConvertible {
public typealias Identity = Section
public typealias Item = ItemType
public var model: Section
public var identity: Section {
return model
}
public var items: [Item]
public init(model: Section, items: [Item]) {
self.model = model
self.items = items
}
public var description: String {
return "\(self.model) > \(items)"
}
}

View File

@ -1,6 +1,6 @@
// //
// SectionModelType.swift // SectionModelType.swift
// RxExample // RxDataSources
// //
// Created by Krunoslav Zaher on 6/28/15. // Created by Krunoslav Zaher on 6/28/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved. // Copyright © 2015 Krunoslav Zaher. All rights reserved.
@ -10,8 +10,6 @@ import Foundation
public protocol SectionModelType { public protocol SectionModelType {
typealias Item typealias Item
var items: [Item] { get } var items: [Item] { get }
init(original: Self, items: [Item])
} }

View File

@ -1,6 +1,6 @@
// //
// RxTableViewSectionedDataSource.swift // TableViewSectionedDataSource.swift
// RxCocoa // RxDataSources
// //
// Created by Krunoslav Zaher on 6/15/15. // Created by Krunoslav Zaher on 6/15/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved. // Copyright © 2015 Krunoslav Zaher. All rights reserved.
@ -8,14 +8,12 @@
import Foundation import Foundation
import UIKit import UIKit
#if !RX_NO_MODULE
import RxSwift
import RxCocoa import RxCocoa
#endif
// objc monkey business // objc monkey business
public class _RxTableViewSectionedDataSource : NSObject public class _TableViewSectionedDataSource
, UITableViewDataSource { : NSObject
, UITableViewDataSource {
func _numberOfSectionsInTableView(tableView: UITableView) -> Int { func _numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1 return 1
@ -56,13 +54,48 @@ public class _RxTableViewSectionedDataSource : NSObject
public func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? { public func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return _tableView(tableView, titleForFooterInSection: section) return _tableView(tableView, titleForFooterInSection: section)
} }
func _tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return false
}
public func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return _tableView(tableView, canEditRowAtIndexPath: indexPath)
}
func _tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return false
}
public func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return _tableView(tableView, canMoveRowAtIndexPath: indexPath)
}
func _sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
return nil
}
public func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
return _sectionIndexTitlesForTableView(tableView)
}
func _tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
return 0
}
public func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
return _tableView(tableView, sectionForSectionIndexTitle: title, atIndex: index)
}
} }
public class RxTableViewSectionedDataSource<S: SectionModelType> : _RxTableViewSectionedDataSource { public class RxTableViewSectionedDataSource<S: SectionModelType>
: _TableViewSectionedDataSource
, SectionedViewDataSourceType {
public typealias I = S.Item public typealias I = S.Item
public typealias Section = S public typealias Section = S
public typealias CellFactory = (UITableView, NSIndexPath, I) -> UITableViewCell public typealias CellFactory = (RxTableViewSectionedDataSource<S>, UITableView, NSIndexPath, I) -> UITableViewCell
// This structure exists because model can be mutable // This structure exists because model can be mutable
// In that case current state value should be preserved. // In that case current state value should be preserved.
@ -74,6 +107,10 @@ public class RxTableViewSectionedDataSource<S: SectionModelType> : _RxTableViewS
private var _sectionModels: [SectionModelSnapshot] = [] private var _sectionModels: [SectionModelSnapshot] = []
public var sectionModels: [S] {
return _sectionModels.map { $0.model }
}
public func sectionAtIndex(section: Int) -> S { public func sectionAtIndex(section: Int) -> S {
return self._sectionModels[section].model return self._sectionModels[section].model
} }
@ -82,20 +119,31 @@ public class RxTableViewSectionedDataSource<S: SectionModelType> : _RxTableViewS
return self._sectionModels[indexPath.section].items[indexPath.item] return self._sectionModels[indexPath.section].items[indexPath.item]
} }
public func modelAtIndexPath(indexPath: NSIndexPath) throws -> Any {
return itemAtIndexPath(indexPath)
}
public func setSections(sections: [S]) { public func setSections(sections: [S]) {
self._sectionModels = sections.map { SectionModelSnapshot(model: $0, items: $0.items) } self._sectionModels = sections.map { SectionModelSnapshot(model: $0, items: $0.items) }
} }
public var configureCell: CellFactory! = nil
public var cellFactory: CellFactory! = nil public var titleForHeaderInSection: ((RxTableViewSectionedDataSource<S>, section: Int) -> String?)?
public var titleForFooterInSection: ((RxTableViewSectionedDataSource<S>, section: Int) -> String?)?
public var titleForHeaderInSection: ((section: Int) -> String)? public var canEditRowAtIndexPath: ((RxTableViewSectionedDataSource<S>, indexPath: NSIndexPath) -> Bool)?
public var titleForFooterInSection: ((section: Int) -> String)? public var canMoveRowAtIndexPath: ((RxTableViewSectionedDataSource<S>, indexPath: NSIndexPath) -> Bool)?
public var sectionIndexTitles: ((RxTableViewSectionedDataSource<S>) -> [String]?)?
public var sectionForSectionIndexTitle:((RxTableViewSectionedDataSource<S>, title: String, index: Int) -> Int)?
public var rowAnimation: UITableViewRowAnimation = .Automatic public var rowAnimation: UITableViewRowAnimation = .Automatic
public override init() { public override init() {
super.init() super.init()
self.cellFactory = { [weak self] _ in self.configureCell = { [weak self] _ in
if let strongSelf = self { if let strongSelf = self {
precondition(false, "There is a minor problem. `cellFactory` property on \(strongSelf) was not set. Please set it manually, or use one of the `rx_bindTo` methods.") precondition(false, "There is a minor problem. `cellFactory` property on \(strongSelf) was not set. Please set it manually, or use one of the `rx_bindTo` methods.")
} }
@ -117,15 +165,34 @@ public class RxTableViewSectionedDataSource<S: SectionModelType> : _RxTableViewS
override func _tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { override func _tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
precondition(indexPath.item < _sectionModels[indexPath.section].items.count) precondition(indexPath.item < _sectionModels[indexPath.section].items.count)
return cellFactory(tableView, indexPath, itemAtIndexPath(indexPath)) return configureCell(self, tableView, indexPath, itemAtIndexPath(indexPath))
} }
override func _tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { override func _tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return titleForHeaderInSection?(section: section) return titleForHeaderInSection?(self, section: section)
} }
override func _tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? { override func _tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return titleForFooterInSection?(section: section) return titleForFooterInSection?(self, section: section)
} }
} override func _tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return canEditRowAtIndexPath?(self, indexPath: indexPath) ??
super._tableView(tableView, canMoveRowAtIndexPath: indexPath)
}
override func _tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return canMoveRowAtIndexPath?(self, indexPath: indexPath) ??
super._tableView(tableView, canMoveRowAtIndexPath: indexPath)
}
override func _sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
return sectionIndexTitles?(self) ?? super._sectionIndexTitlesForTableView(tableView)
}
override func _tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
return sectionForSectionIndexTitle?(self, title: title, index: index) ??
super._tableView(tableView, sectionForSectionIndexTitle: title, atIndex: index)
}
}

View File

@ -1,6 +1,6 @@
// //
// SectionedViewType.swift // UI+SectionedViewType.swift
// RxExample // RxDataSources
// //
// Created by Krunoslav Zaher on 6/27/15. // Created by Krunoslav Zaher on 6/27/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved. // Copyright © 2015 Krunoslav Zaher. All rights reserved.
@ -18,79 +18,80 @@ func indexSet(values: [Int]) -> NSIndexSet {
} }
extension UITableView : SectionedViewType { extension UITableView : SectionedViewType {
func insertItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) {
public func insertItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) {
self.insertRowsAtIndexPaths(paths, withRowAnimation: animationStyle) self.insertRowsAtIndexPaths(paths, withRowAnimation: animationStyle)
} }
func deleteItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) { public func deleteItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) {
self.deleteRowsAtIndexPaths(paths, withRowAnimation: animationStyle) self.deleteRowsAtIndexPaths(paths, withRowAnimation: animationStyle)
} }
func moveItemAtIndexPath(from: NSIndexPath, to: NSIndexPath) { public func moveItemAtIndexPath(from: NSIndexPath, to: NSIndexPath) {
self.moveRowAtIndexPath(from, toIndexPath: to) self.moveRowAtIndexPath(from, toIndexPath: to)
} }
func reloadItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) { public func reloadItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) {
self.reloadRowsAtIndexPaths(paths, withRowAnimation: animationStyle) self.reloadRowsAtIndexPaths(paths, withRowAnimation: animationStyle)
} }
func insertSections(sections: [Int], animationStyle: UITableViewRowAnimation) { public func insertSections(sections: [Int], animationStyle: UITableViewRowAnimation) {
self.insertSections(indexSet(sections), withRowAnimation: animationStyle) self.insertSections(indexSet(sections), withRowAnimation: animationStyle)
} }
func deleteSections(sections: [Int], animationStyle: UITableViewRowAnimation) { public func deleteSections(sections: [Int], animationStyle: UITableViewRowAnimation) {
self.deleteSections(indexSet(sections), withRowAnimation: animationStyle) self.deleteSections(indexSet(sections), withRowAnimation: animationStyle)
} }
func moveSection(from: Int, to: Int) { public func moveSection(from: Int, to: Int) {
self.moveSection(from, toSection: to) self.moveSection(from, toSection: to)
} }
func reloadSections(sections: [Int], animationStyle: UITableViewRowAnimation) { public func reloadSections(sections: [Int], animationStyle: UITableViewRowAnimation) {
self.reloadSections(indexSet(sections), withRowAnimation: animationStyle) self.reloadSections(indexSet(sections), withRowAnimation: animationStyle)
} }
func performBatchUpdates<S: SectionModelType>(changes: Changeset<S>) { public func performBatchUpdates<S: SectionModelType>(changes: Changeset<S>, animationConfiguration: AnimationConfiguration?=nil) {
self.beginUpdates() self.beginUpdates()
_performBatchUpdates(self, changes: changes) _performBatchUpdates(self, changes: changes, animationConfiguration: animationConfiguration)
self.endUpdates() self.endUpdates()
} }
} }
extension UICollectionView : SectionedViewType { extension UICollectionView : SectionedViewType {
func insertItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) { public func insertItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) {
self.insertItemsAtIndexPaths(paths) self.insertItemsAtIndexPaths(paths)
} }
func deleteItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) { public func deleteItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) {
self.deleteItemsAtIndexPaths(paths) self.deleteItemsAtIndexPaths(paths)
} }
func moveItemAtIndexPath(from: NSIndexPath, to: NSIndexPath) { public func moveItemAtIndexPath(from: NSIndexPath, to: NSIndexPath) {
self.moveItemAtIndexPath(from, toIndexPath: to) self.moveItemAtIndexPath(from, toIndexPath: to)
} }
func reloadItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) { public func reloadItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) {
self.reloadItemsAtIndexPaths(paths) self.reloadItemsAtIndexPaths(paths)
} }
func insertSections(sections: [Int], animationStyle: UITableViewRowAnimation) { public func insertSections(sections: [Int], animationStyle: UITableViewRowAnimation) {
self.insertSections(indexSet(sections)) self.insertSections(indexSet(sections))
} }
func deleteSections(sections: [Int], animationStyle: UITableViewRowAnimation) { public func deleteSections(sections: [Int], animationStyle: UITableViewRowAnimation) {
self.deleteSections(indexSet(sections)) self.deleteSections(indexSet(sections))
} }
func moveSection(from: Int, to: Int) { public func moveSection(from: Int, to: Int) {
self.moveSection(from, toSection: to) self.moveSection(from, toSection: to)
} }
func reloadSections(sections: [Int], animationStyle: UITableViewRowAnimation) { public func reloadSections(sections: [Int], animationStyle: UITableViewRowAnimation) {
self.reloadSections(indexSet(sections)) self.reloadSections(indexSet(sections))
} }
func performBatchUpdates<S: SectionModelType>(changes: Changeset<S>) { public func performBatchUpdates<S: SectionModelType>(changes: Changeset<S>, animationConfiguration:AnimationConfiguration?=nil) {
self.performBatchUpdates({ () -> Void in self.performBatchUpdates({ () -> Void in
_performBatchUpdates(self, changes: changes) _performBatchUpdates(self, changes: changes)
}, completion: { (completed: Bool) -> Void in }, completion: { (completed: Bool) -> Void in
@ -98,7 +99,7 @@ extension UICollectionView : SectionedViewType {
} }
} }
protocol SectionedViewType { public protocol SectionedViewType {
func insertItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) func insertItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation)
func deleteItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) func deleteItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation)
func moveItemAtIndexPath(from: NSIndexPath, to: NSIndexPath) func moveItemAtIndexPath(from: NSIndexPath, to: NSIndexPath)
@ -109,42 +110,33 @@ protocol SectionedViewType {
func moveSection(from: Int, to: Int) func moveSection(from: Int, to: Int)
func reloadSections(sections: [Int], animationStyle: UITableViewRowAnimation) func reloadSections(sections: [Int], animationStyle: UITableViewRowAnimation)
func performBatchUpdates<S>(changes: Changeset<S>) func performBatchUpdates<S>(changes: Changeset<S>, animationConfiguration: AnimationConfiguration?)
} }
func setFor<E, K>(items: [E], transform: E -> K) -> [K : K] { func _performBatchUpdates<V: SectionedViewType, S: SectionModelType>(view: V, changes: Changeset<S>, animationConfiguration :AnimationConfiguration?=nil) {
var res = [K : K]()
for i in items {
let k = transform(i)
res[k] = k
}
return res
}
func _performBatchUpdates<V: SectionedViewType, S: SectionModelType>(view: V, changes: Changeset<S>) {
typealias I = S.Item typealias I = S.Item
let rowAnimation = UITableViewRowAnimation.Automatic
let animationConfiguration = animationConfiguration ?? AnimationConfiguration()
view.deleteSections(changes.deletedSections, animationStyle: rowAnimation) view.deleteSections(changes.deletedSections, animationStyle: animationConfiguration.deleteAnimation)
view.reloadSections(changes.updatedSections, animationStyle: rowAnimation) // Updated sections doesn't mean reload entire section, somebody needs to update the section view manually
view.insertSections(changes.insertedSections, animationStyle: rowAnimation) // otherwise all cells will be reloaded for nothing.
//view.reloadSections(changes.updatedSections, animationStyle: rowAnimation)
view.insertSections(changes.insertedSections, animationStyle: animationConfiguration.insertAnimation)
for (from, to) in changes.movedSections { for (from, to) in changes.movedSections {
view.moveSection(from, to: to) view.moveSection(from, to: to)
} }
view.deleteItemsAtIndexPaths( view.deleteItemsAtIndexPaths(
changes.deletedItems.map { NSIndexPath(forItem: $0.itemIndex, inSection: $0.sectionIndex) }, changes.deletedItems.map { NSIndexPath(forItem: $0.itemIndex, inSection: $0.sectionIndex) },
animationStyle: rowAnimation animationStyle: animationConfiguration.deleteAnimation
) )
view.insertItemsAtIndexPaths( view.insertItemsAtIndexPaths(
changes.insertedItems.map { NSIndexPath(forItem: $0.itemIndex, inSection: $0.sectionIndex) }, changes.insertedItems.map { NSIndexPath(forItem: $0.itemIndex, inSection: $0.sectionIndex) },
animationStyle: rowAnimation animationStyle: animationConfiguration.insertAnimation
) )
view.reloadItemsAtIndexPaths( view.reloadItemsAtIndexPaths(
changes.updatedItems.map { NSIndexPath(forItem: $0.itemIndex, inSection: $0.sectionIndex) }, changes.updatedItems.map { NSIndexPath(forItem: $0.itemIndex, inSection: $0.sectionIndex) },
animationStyle: rowAnimation animationStyle: animationConfiguration.reloadAnimation
) )
for (from, to) in changes.movedItems { for (from, to) in changes.movedItems {

View File

@ -1,5 +1,5 @@
RxSwift: DataSource Starter Kit RxSwift: DataSources
=============================== ====================
This directory contains example implementations of reactive data sources. This directory contains example implementations of reactive data sources.

View File

@ -324,34 +324,6 @@
C89634081B95BE50002AE38C /* RxBlocking.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468EF1B8A8BD000BF917B /* RxBlocking.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; C89634081B95BE50002AE38C /* RxBlocking.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468EF1B8A8BD000BF917B /* RxBlocking.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
C89634091B95BE50002AE38C /* RxCocoa.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468ED1B8A8BCC00BF917B /* RxCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; C89634091B95BE50002AE38C /* RxCocoa.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468ED1B8A8BCC00BF917B /* RxCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
C896340A1B95BE51002AE38C /* RxSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468EB1B8A8BC900BF917B /* RxSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; C896340A1B95BE51002AE38C /* RxSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468EB1B8A8BC900BF917B /* RxSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
C8984C311C36A579001E4272 /* Changeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C211C36A579001E4272 /* Changeset.swift */; };
C8984C321C36A579001E4272 /* Changeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C211C36A579001E4272 /* Changeset.swift */; };
C8984C331C36A579001E4272 /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C231C36A579001E4272 /* RxCollectionViewSectionedAnimatedDataSource.swift */; };
C8984C341C36A579001E4272 /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C231C36A579001E4272 /* RxCollectionViewSectionedAnimatedDataSource.swift */; };
C8984C351C36A579001E4272 /* RxCollectionViewSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C241C36A579001E4272 /* RxCollectionViewSectionedDataSource.swift */; };
C8984C361C36A579001E4272 /* RxCollectionViewSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C241C36A579001E4272 /* RxCollectionViewSectionedDataSource.swift */; };
C8984C371C36A579001E4272 /* RxCollectionViewSectionedReloadDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C251C36A579001E4272 /* RxCollectionViewSectionedReloadDataSource.swift */; };
C8984C381C36A579001E4272 /* RxCollectionViewSectionedReloadDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C251C36A579001E4272 /* RxCollectionViewSectionedReloadDataSource.swift */; };
C8984C391C36A579001E4272 /* RxTableViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C261C36A579001E4272 /* RxTableViewSectionedAnimatedDataSource.swift */; };
C8984C3A1C36A579001E4272 /* RxTableViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C261C36A579001E4272 /* RxTableViewSectionedAnimatedDataSource.swift */; };
C8984C3B1C36A579001E4272 /* RxTableViewSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C271C36A579001E4272 /* RxTableViewSectionedDataSource.swift */; };
C8984C3C1C36A579001E4272 /* RxTableViewSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C271C36A579001E4272 /* RxTableViewSectionedDataSource.swift */; };
C8984C3D1C36A579001E4272 /* RxTableViewSectionedReloadDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C281C36A579001E4272 /* RxTableViewSectionedReloadDataSource.swift */; };
C8984C3E1C36A579001E4272 /* RxTableViewSectionedReloadDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C281C36A579001E4272 /* RxTableViewSectionedReloadDataSource.swift */; };
C8984C3F1C36A579001E4272 /* Differentiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C291C36A579001E4272 /* Differentiator.swift */; };
C8984C401C36A579001E4272 /* Differentiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C291C36A579001E4272 /* Differentiator.swift */; };
C8984C411C36A579001E4272 /* ObservableConvertibleType+Differentiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C2A1C36A579001E4272 /* ObservableConvertibleType+Differentiator.swift */; };
C8984C421C36A579001E4272 /* ObservableConvertibleType+Differentiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C2A1C36A579001E4272 /* ObservableConvertibleType+Differentiator.swift */; };
C8984C451C36A579001E4272 /* RxDataSourceStarterKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C2C1C36A579001E4272 /* RxDataSourceStarterKit.swift */; };
C8984C461C36A579001E4272 /* RxDataSourceStarterKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C2C1C36A579001E4272 /* RxDataSourceStarterKit.swift */; };
C8984C471C36A579001E4272 /* SectionedViewType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C2D1C36A579001E4272 /* SectionedViewType.swift */; };
C8984C481C36A579001E4272 /* SectionedViewType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C2D1C36A579001E4272 /* SectionedViewType.swift */; };
C8984C491C36A579001E4272 /* SectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C2E1C36A579001E4272 /* SectionModel.swift */; };
C8984C4A1C36A579001E4272 /* SectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C2E1C36A579001E4272 /* SectionModel.swift */; };
C8984C4B1C36A579001E4272 /* SectionModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C2F1C36A579001E4272 /* SectionModelType.swift */; };
C8984C4C1C36A579001E4272 /* SectionModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C2F1C36A579001E4272 /* SectionModelType.swift */; };
C8984C4D1C36A579001E4272 /* UISectionedViewType+RxAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C301C36A579001E4272 /* UISectionedViewType+RxAnimatedDataSource.swift */; };
C8984C4E1C36A579001E4272 /* UISectionedViewType+RxAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984C301C36A579001E4272 /* UISectionedViewType+RxAnimatedDataSource.swift */; };
C8984CD11C36BC3E001E4272 /* NumberCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984CCE1C36BC3E001E4272 /* NumberCell.swift */; }; C8984CD11C36BC3E001E4272 /* NumberCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984CCE1C36BC3E001E4272 /* NumberCell.swift */; };
C8984CD21C36BC3E001E4272 /* NumberCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984CCE1C36BC3E001E4272 /* NumberCell.swift */; }; C8984CD21C36BC3E001E4272 /* NumberCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984CCE1C36BC3E001E4272 /* NumberCell.swift */; };
C8984CD31C36BC3E001E4272 /* NumberSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984CCF1C36BC3E001E4272 /* NumberSectionView.swift */; }; C8984CD31C36BC3E001E4272 /* NumberSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8984CCF1C36BC3E001E4272 /* NumberSectionView.swift */; };
@ -373,6 +345,50 @@
C8A468F11B8A8C2600BF917B /* RxBlocking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468EF1B8A8BD000BF917B /* RxBlocking.framework */; }; C8A468F11B8A8C2600BF917B /* RxBlocking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468EF1B8A8BD000BF917B /* RxBlocking.framework */; };
C8A468F21B8A8C2600BF917B /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468ED1B8A8BCC00BF917B /* RxCocoa.framework */; }; C8A468F21B8A8C2600BF917B /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468ED1B8A8BCC00BF917B /* RxCocoa.framework */; };
C8A468F31B8A8C2600BF917B /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468EB1B8A8BC900BF917B /* RxSwift.framework */; }; C8A468F31B8A8C2600BF917B /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468EB1B8A8BC900BF917B /* RxSwift.framework */; };
C8B290BF1C959D2900E923D0 /* AnimatableSectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290A71C959D2900E923D0 /* AnimatableSectionModel.swift */; };
C8B290C01C959D2900E923D0 /* AnimatableSectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290A71C959D2900E923D0 /* AnimatableSectionModel.swift */; };
C8B290C11C959D2900E923D0 /* AnimatableSectionModelType+ItemPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290A81C959D2900E923D0 /* AnimatableSectionModelType+ItemPath.swift */; };
C8B290C21C959D2900E923D0 /* AnimatableSectionModelType+ItemPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290A81C959D2900E923D0 /* AnimatableSectionModelType+ItemPath.swift */; };
C8B290C31C959D2900E923D0 /* AnimatableSectionModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290A91C959D2900E923D0 /* AnimatableSectionModelType.swift */; };
C8B290C41C959D2900E923D0 /* AnimatableSectionModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290A91C959D2900E923D0 /* AnimatableSectionModelType.swift */; };
C8B290C51C959D2900E923D0 /* AnimationConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290AA1C959D2900E923D0 /* AnimationConfiguration.swift */; };
C8B290C61C959D2900E923D0 /* AnimationConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290AA1C959D2900E923D0 /* AnimationConfiguration.swift */; };
C8B290C71C959D2900E923D0 /* Changeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290AB1C959D2900E923D0 /* Changeset.swift */; };
C8B290C81C959D2900E923D0 /* Changeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290AB1C959D2900E923D0 /* Changeset.swift */; };
C8B290C91C959D2900E923D0 /* CollectionViewSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290AC1C959D2900E923D0 /* CollectionViewSectionedDataSource.swift */; };
C8B290CA1C959D2900E923D0 /* CollectionViewSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290AC1C959D2900E923D0 /* CollectionViewSectionedDataSource.swift */; };
C8B290CB1C959D2900E923D0 /* DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290AD1C959D2900E923D0 /* DataSources.swift */; };
C8B290CC1C959D2900E923D0 /* DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290AD1C959D2900E923D0 /* DataSources.swift */; };
C8B290CD1C959D2900E923D0 /* Differentiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290AE1C959D2900E923D0 /* Differentiator.swift */; };
C8B290CE1C959D2900E923D0 /* Differentiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290AE1C959D2900E923D0 /* Differentiator.swift */; };
C8B290CF1C959D2900E923D0 /* IdentifiableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290AF1C959D2900E923D0 /* IdentifiableType.swift */; };
C8B290D01C959D2900E923D0 /* IdentifiableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290AF1C959D2900E923D0 /* IdentifiableType.swift */; };
C8B290D11C959D2900E923D0 /* IdentifiableValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B01C959D2900E923D0 /* IdentifiableValue.swift */; };
C8B290D21C959D2900E923D0 /* IdentifiableValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B01C959D2900E923D0 /* IdentifiableValue.swift */; };
C8B290D31C959D2900E923D0 /* ItemPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B11C959D2900E923D0 /* ItemPath.swift */; };
C8B290D41C959D2900E923D0 /* ItemPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B11C959D2900E923D0 /* ItemPath.swift */; };
C8B290D51C959D2900E923D0 /* Optional+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B21C959D2900E923D0 /* Optional+Extensions.swift */; };
C8B290D61C959D2900E923D0 /* Optional+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B21C959D2900E923D0 /* Optional+Extensions.swift */; };
C8B290D71C959D2900E923D0 /* SectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B31C959D2900E923D0 /* SectionModel.swift */; };
C8B290D81C959D2900E923D0 /* SectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B31C959D2900E923D0 /* SectionModel.swift */; };
C8B290D91C959D2900E923D0 /* SectionModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B41C959D2900E923D0 /* SectionModelType.swift */; };
C8B290DA1C959D2900E923D0 /* SectionModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B41C959D2900E923D0 /* SectionModelType.swift */; };
C8B290DB1C959D2900E923D0 /* TableViewSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B51C959D2900E923D0 /* TableViewSectionedDataSource.swift */; };
C8B290DC1C959D2900E923D0 /* TableViewSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B51C959D2900E923D0 /* TableViewSectionedDataSource.swift */; };
C8B290DD1C959D2900E923D0 /* UI+SectionedViewType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B61C959D2900E923D0 /* UI+SectionedViewType.swift */; };
C8B290DE1C959D2900E923D0 /* UI+SectionedViewType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B61C959D2900E923D0 /* UI+SectionedViewType.swift */; };
C8B290DF1C959D2900E923D0 /* ObservableConvertibleType+Differentiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B81C959D2900E923D0 /* ObservableConvertibleType+Differentiator.swift */; };
C8B290E01C959D2900E923D0 /* ObservableConvertibleType+Differentiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B81C959D2900E923D0 /* ObservableConvertibleType+Differentiator.swift */; };
C8B290E11C959D2900E923D0 /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B91C959D2900E923D0 /* RxCollectionViewSectionedAnimatedDataSource.swift */; };
C8B290E21C959D2900E923D0 /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B91C959D2900E923D0 /* RxCollectionViewSectionedAnimatedDataSource.swift */; };
C8B290E31C959D2900E923D0 /* RxCollectionViewSectionedReloadDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290BA1C959D2900E923D0 /* RxCollectionViewSectionedReloadDataSource.swift */; };
C8B290E41C959D2900E923D0 /* RxCollectionViewSectionedReloadDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290BA1C959D2900E923D0 /* RxCollectionViewSectionedReloadDataSource.swift */; };
C8B290E51C959D2900E923D0 /* RxTableViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290BB1C959D2900E923D0 /* RxTableViewSectionedAnimatedDataSource.swift */; };
C8B290E61C959D2900E923D0 /* RxTableViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290BB1C959D2900E923D0 /* RxTableViewSectionedAnimatedDataSource.swift */; };
C8B290E71C959D2900E923D0 /* RxTableViewSectionedReloadDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290BC1C959D2900E923D0 /* RxTableViewSectionedReloadDataSource.swift */; };
C8B290E81C959D2900E923D0 /* RxTableViewSectionedReloadDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290BC1C959D2900E923D0 /* RxTableViewSectionedReloadDataSource.swift */; };
C8B290E91C959D2900E923D0 /* UISectionedViewType+RxAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290BD1C959D2900E923D0 /* UISectionedViewType+RxAnimatedDataSource.swift */; };
C8B290EA1C959D2900E923D0 /* UISectionedViewType+RxAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290BD1C959D2900E923D0 /* UISectionedViewType+RxAnimatedDataSource.swift */; };
C8BCD3CE1C14756F005F1280 /* ShareReplay1WhileConnected.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8BCD3CD1C14756F005F1280 /* ShareReplay1WhileConnected.swift */; }; C8BCD3CE1C14756F005F1280 /* ShareReplay1WhileConnected.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8BCD3CD1C14756F005F1280 /* ShareReplay1WhileConnected.swift */; };
C8BCD3DF1C1480E9005F1280 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8BCD3DE1C1480E9005F1280 /* Operators.swift */; }; C8BCD3DF1C1480E9005F1280 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8BCD3DE1C1480E9005F1280 /* Operators.swift */; };
C8BCD3E01C1480E9005F1280 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8BCD3DE1C1480E9005F1280 /* Operators.swift */; }; C8BCD3E01C1480E9005F1280 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8BCD3DE1C1480E9005F1280 /* Operators.swift */; };
@ -865,21 +881,6 @@
C89465581BC6C2BC0055219D /* UITextField+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextField+Rx.swift"; sourceTree = "<group>"; }; C89465581BC6C2BC0055219D /* UITextField+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextField+Rx.swift"; sourceTree = "<group>"; };
C89465591BC6C2BC0055219D /* UITextView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextView+Rx.swift"; sourceTree = "<group>"; }; C89465591BC6C2BC0055219D /* UITextView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextView+Rx.swift"; sourceTree = "<group>"; };
C89465601BC6C2BC0055219D /* RxCocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxCocoa.h; sourceTree = "<group>"; }; C89465601BC6C2BC0055219D /* RxCocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxCocoa.h; sourceTree = "<group>"; };
C8984C211C36A579001E4272 /* Changeset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Changeset.swift; sourceTree = "<group>"; };
C8984C231C36A579001E4272 /* RxCollectionViewSectionedAnimatedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewSectionedAnimatedDataSource.swift; sourceTree = "<group>"; };
C8984C241C36A579001E4272 /* RxCollectionViewSectionedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewSectionedDataSource.swift; sourceTree = "<group>"; };
C8984C251C36A579001E4272 /* RxCollectionViewSectionedReloadDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewSectionedReloadDataSource.swift; sourceTree = "<group>"; };
C8984C261C36A579001E4272 /* RxTableViewSectionedAnimatedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewSectionedAnimatedDataSource.swift; sourceTree = "<group>"; };
C8984C271C36A579001E4272 /* RxTableViewSectionedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewSectionedDataSource.swift; sourceTree = "<group>"; };
C8984C281C36A579001E4272 /* RxTableViewSectionedReloadDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewSectionedReloadDataSource.swift; sourceTree = "<group>"; };
C8984C291C36A579001E4272 /* Differentiator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Differentiator.swift; sourceTree = "<group>"; };
C8984C2A1C36A579001E4272 /* ObservableConvertibleType+Differentiator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObservableConvertibleType+Differentiator.swift"; sourceTree = "<group>"; };
C8984C2B1C36A579001E4272 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
C8984C2C1C36A579001E4272 /* RxDataSourceStarterKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxDataSourceStarterKit.swift; sourceTree = "<group>"; };
C8984C2D1C36A579001E4272 /* SectionedViewType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionedViewType.swift; sourceTree = "<group>"; };
C8984C2E1C36A579001E4272 /* SectionModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionModel.swift; sourceTree = "<group>"; };
C8984C2F1C36A579001E4272 /* SectionModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionModelType.swift; sourceTree = "<group>"; };
C8984C301C36A579001E4272 /* UISectionedViewType+RxAnimatedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UISectionedViewType+RxAnimatedDataSource.swift"; sourceTree = "<group>"; };
C8984CCE1C36BC3E001E4272 /* NumberCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberCell.swift; sourceTree = "<group>"; }; C8984CCE1C36BC3E001E4272 /* NumberCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberCell.swift; sourceTree = "<group>"; };
C8984CCF1C36BC3E001E4272 /* NumberSectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberSectionView.swift; sourceTree = "<group>"; }; C8984CCF1C36BC3E001E4272 /* NumberSectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberSectionView.swift; sourceTree = "<group>"; };
C8984CD01C36BC3E001E4272 /* PartialUpdatesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PartialUpdatesViewController.swift; sourceTree = "<group>"; }; C8984CD01C36BC3E001E4272 /* PartialUpdatesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PartialUpdatesViewController.swift; sourceTree = "<group>"; };
@ -897,6 +898,29 @@
C8A468EF1B8A8BD000BF917B /* RxBlocking.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = RxBlocking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8A468EF1B8A8BD000BF917B /* RxBlocking.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = RxBlocking.framework; sourceTree = BUILT_PRODUCTS_DIR; };
C8B145041BD2E45200267DCE /* ImmediateScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImmediateScheduler.swift; sourceTree = "<group>"; }; C8B145041BD2E45200267DCE /* ImmediateScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImmediateScheduler.swift; sourceTree = "<group>"; };
C8B145051BD2E45200267DCE /* ConcurrentMainScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConcurrentMainScheduler.swift; sourceTree = "<group>"; }; C8B145051BD2E45200267DCE /* ConcurrentMainScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConcurrentMainScheduler.swift; sourceTree = "<group>"; };
C8B290A71C959D2900E923D0 /* AnimatableSectionModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimatableSectionModel.swift; sourceTree = "<group>"; };
C8B290A81C959D2900E923D0 /* AnimatableSectionModelType+ItemPath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AnimatableSectionModelType+ItemPath.swift"; sourceTree = "<group>"; };
C8B290A91C959D2900E923D0 /* AnimatableSectionModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimatableSectionModelType.swift; sourceTree = "<group>"; };
C8B290AA1C959D2900E923D0 /* AnimationConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimationConfiguration.swift; sourceTree = "<group>"; };
C8B290AB1C959D2900E923D0 /* Changeset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Changeset.swift; sourceTree = "<group>"; };
C8B290AC1C959D2900E923D0 /* CollectionViewSectionedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewSectionedDataSource.swift; sourceTree = "<group>"; };
C8B290AD1C959D2900E923D0 /* DataSources.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataSources.swift; sourceTree = "<group>"; };
C8B290AE1C959D2900E923D0 /* Differentiator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Differentiator.swift; sourceTree = "<group>"; };
C8B290AF1C959D2900E923D0 /* IdentifiableType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IdentifiableType.swift; sourceTree = "<group>"; };
C8B290B01C959D2900E923D0 /* IdentifiableValue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IdentifiableValue.swift; sourceTree = "<group>"; };
C8B290B11C959D2900E923D0 /* ItemPath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemPath.swift; sourceTree = "<group>"; };
C8B290B21C959D2900E923D0 /* Optional+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Optional+Extensions.swift"; sourceTree = "<group>"; };
C8B290B31C959D2900E923D0 /* SectionModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionModel.swift; sourceTree = "<group>"; };
C8B290B41C959D2900E923D0 /* SectionModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionModelType.swift; sourceTree = "<group>"; };
C8B290B51C959D2900E923D0 /* TableViewSectionedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewSectionedDataSource.swift; sourceTree = "<group>"; };
C8B290B61C959D2900E923D0 /* UI+SectionedViewType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UI+SectionedViewType.swift"; sourceTree = "<group>"; };
C8B290B81C959D2900E923D0 /* ObservableConvertibleType+Differentiator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObservableConvertibleType+Differentiator.swift"; sourceTree = "<group>"; };
C8B290B91C959D2900E923D0 /* RxCollectionViewSectionedAnimatedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewSectionedAnimatedDataSource.swift; sourceTree = "<group>"; };
C8B290BA1C959D2900E923D0 /* RxCollectionViewSectionedReloadDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewSectionedReloadDataSource.swift; sourceTree = "<group>"; };
C8B290BB1C959D2900E923D0 /* RxTableViewSectionedAnimatedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewSectionedAnimatedDataSource.swift; sourceTree = "<group>"; };
C8B290BC1C959D2900E923D0 /* RxTableViewSectionedReloadDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewSectionedReloadDataSource.swift; sourceTree = "<group>"; };
C8B290BD1C959D2900E923D0 /* UISectionedViewType+RxAnimatedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UISectionedViewType+RxAnimatedDataSource.swift"; sourceTree = "<group>"; };
C8B290BE1C959D2900E923D0 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
C8BCD3CD1C14756F005F1280 /* ShareReplay1WhileConnected.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareReplay1WhileConnected.swift; sourceTree = "<group>"; }; C8BCD3CD1C14756F005F1280 /* ShareReplay1WhileConnected.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareReplay1WhileConnected.swift; sourceTree = "<group>"; };
C8BCD3DE1C1480E9005F1280 /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = "<group>"; }; C8BCD3DE1C1480E9005F1280 /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = "<group>"; };
C8BCD3E21C14820B005F1280 /* IntroductionExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntroductionExampleViewController.swift; sourceTree = "<group>"; }; C8BCD3E21C14820B005F1280 /* IntroductionExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntroductionExampleViewController.swift; sourceTree = "<group>"; };
@ -1081,7 +1105,7 @@
C8A468EF1B8A8BD000BF917B /* RxBlocking.framework */, C8A468EF1B8A8BD000BF917B /* RxBlocking.framework */,
C8A468ED1B8A8BCC00BF917B /* RxCocoa.framework */, C8A468ED1B8A8BCC00BF917B /* RxCocoa.framework */,
C8A468EB1B8A8BC900BF917B /* RxSwift.framework */, C8A468EB1B8A8BC900BF917B /* RxSwift.framework */,
C8984C201C36A579001E4272 /* RxDataSourceStarterKit */, C8B290A51C959D2900E923D0 /* RxDataSources */,
C836EB911B8A7A3700AB941D /* NoModule */, C836EB911B8A7A3700AB941D /* NoModule */,
C83366DF1AD0293800C668A7 /* RxExample */, C83366DF1AD0293800C668A7 /* RxExample */,
C849EF621C3190360048AC4A /* RxExample-iOSTests */, C849EF621C3190360048AC4A /* RxExample-iOSTests */,
@ -1665,36 +1689,6 @@
path = Proxies; path = Proxies;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
C8984C201C36A579001E4272 /* RxDataSourceStarterKit */ = {
isa = PBXGroup;
children = (
C8984C211C36A579001E4272 /* Changeset.swift */,
C8984C221C36A579001E4272 /* DataSources */,
C8984C291C36A579001E4272 /* Differentiator.swift */,
C8984C2A1C36A579001E4272 /* ObservableConvertibleType+Differentiator.swift */,
C8984C2B1C36A579001E4272 /* README.md */,
C8984C2C1C36A579001E4272 /* RxDataSourceStarterKit.swift */,
C8984C2D1C36A579001E4272 /* SectionedViewType.swift */,
C8984C2E1C36A579001E4272 /* SectionModel.swift */,
C8984C2F1C36A579001E4272 /* SectionModelType.swift */,
C8984C301C36A579001E4272 /* UISectionedViewType+RxAnimatedDataSource.swift */,
);
path = RxDataSourceStarterKit;
sourceTree = "<group>";
};
C8984C221C36A579001E4272 /* DataSources */ = {
isa = PBXGroup;
children = (
C8984C231C36A579001E4272 /* RxCollectionViewSectionedAnimatedDataSource.swift */,
C8984C241C36A579001E4272 /* RxCollectionViewSectionedDataSource.swift */,
C8984C251C36A579001E4272 /* RxCollectionViewSectionedReloadDataSource.swift */,
C8984C261C36A579001E4272 /* RxTableViewSectionedAnimatedDataSource.swift */,
C8984C271C36A579001E4272 /* RxTableViewSectionedDataSource.swift */,
C8984C281C36A579001E4272 /* RxTableViewSectionedReloadDataSource.swift */,
);
path = DataSources;
sourceTree = "<group>";
};
C8984CCD1C36BC3E001E4272 /* TableViewPartialUpdates */ = { C8984CCD1C36BC3E001E4272 /* TableViewPartialUpdates */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1716,6 +1710,52 @@
path = Mocks; path = Mocks;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
C8B290A51C959D2900E923D0 /* RxDataSources */ = {
isa = PBXGroup;
children = (
C8B290A61C959D2900E923D0 /* DataSources */,
C8B290B71C959D2900E923D0 /* DataSources+Rx */,
C8B290BE1C959D2900E923D0 /* README.md */,
);
path = RxDataSources;
sourceTree = "<group>";
};
C8B290A61C959D2900E923D0 /* DataSources */ = {
isa = PBXGroup;
children = (
C8B290A71C959D2900E923D0 /* AnimatableSectionModel.swift */,
C8B290A81C959D2900E923D0 /* AnimatableSectionModelType+ItemPath.swift */,
C8B290A91C959D2900E923D0 /* AnimatableSectionModelType.swift */,
C8B290AA1C959D2900E923D0 /* AnimationConfiguration.swift */,
C8B290AB1C959D2900E923D0 /* Changeset.swift */,
C8B290AC1C959D2900E923D0 /* CollectionViewSectionedDataSource.swift */,
C8B290AD1C959D2900E923D0 /* DataSources.swift */,
C8B290AE1C959D2900E923D0 /* Differentiator.swift */,
C8B290AF1C959D2900E923D0 /* IdentifiableType.swift */,
C8B290B01C959D2900E923D0 /* IdentifiableValue.swift */,
C8B290B11C959D2900E923D0 /* ItemPath.swift */,
C8B290B21C959D2900E923D0 /* Optional+Extensions.swift */,
C8B290B31C959D2900E923D0 /* SectionModel.swift */,
C8B290B41C959D2900E923D0 /* SectionModelType.swift */,
C8B290B51C959D2900E923D0 /* TableViewSectionedDataSource.swift */,
C8B290B61C959D2900E923D0 /* UI+SectionedViewType.swift */,
);
path = DataSources;
sourceTree = "<group>";
};
C8B290B71C959D2900E923D0 /* DataSources+Rx */ = {
isa = PBXGroup;
children = (
C8B290B81C959D2900E923D0 /* ObservableConvertibleType+Differentiator.swift */,
C8B290B91C959D2900E923D0 /* RxCollectionViewSectionedAnimatedDataSource.swift */,
C8B290BA1C959D2900E923D0 /* RxCollectionViewSectionedReloadDataSource.swift */,
C8B290BB1C959D2900E923D0 /* RxTableViewSectionedAnimatedDataSource.swift */,
C8B290BC1C959D2900E923D0 /* RxTableViewSectionedReloadDataSource.swift */,
C8B290BD1C959D2900E923D0 /* UISectionedViewType+RxAnimatedDataSource.swift */,
);
path = "DataSources+Rx";
sourceTree = "<group>";
};
C8BCD3E11C14820B005F1280 /* OSX simple example */ = { C8BCD3E11C14820B005F1280 /* OSX simple example */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -2097,6 +2137,7 @@
files = ( files = (
0744CDD51C4DB5F000720FD2 /* GeolocationService.swift in Sources */, 0744CDD51C4DB5F000720FD2 /* GeolocationService.swift in Sources */,
C83D73DE1C1DBC2A003DC470 /* AnonymousInvocable.swift in Sources */, C83D73DE1C1DBC2A003DC470 /* AnonymousInvocable.swift in Sources */,
C8B290D21C959D2900E923D0 /* IdentifiableValue.swift in Sources */,
C84015751C34353D009D2E77 /* DispatchQueueSchedulerQOS.swift in Sources */, C84015751C34353D009D2E77 /* DispatchQueueSchedulerQOS.swift in Sources */,
C84CC58B1BDD486300E06A64 /* LockOwnerType.swift in Sources */, C84CC58B1BDD486300E06A64 /* LockOwnerType.swift in Sources */,
C89465971BC6C2BC0055219D /* UIScrollView+Rx.swift in Sources */, C89465971BC6C2BC0055219D /* UIScrollView+Rx.swift in Sources */,
@ -2105,11 +2146,11 @@
C89464D41BC6C2B00055219D /* ObserveOn.swift in Sources */, C89464D41BC6C2B00055219D /* ObserveOn.swift in Sources */,
C894659B1BC6C2BC0055219D /* UIStepper+Rx.swift in Sources */, C894659B1BC6C2BC0055219D /* UIStepper+Rx.swift in Sources */,
8479BC711C3BCB9800FB8B54 /* ImagePickerController.swift in Sources */, 8479BC711C3BCB9800FB8B54 /* ImagePickerController.swift in Sources */,
C8984C321C36A579001E4272 /* Changeset.swift in Sources */,
C864BADA1C3332F10083833C /* RandomUserAPI.swift in Sources */, C864BADA1C3332F10083833C /* RandomUserAPI.swift in Sources */,
C89464F61BC6C2B00055219D /* AnonymousObserver.swift in Sources */, C89464F61BC6C2B00055219D /* AnonymousObserver.swift in Sources */,
C89464C81BC6C2B00055219D /* DistinctUntilChanged.swift in Sources */, C89464C81BC6C2B00055219D /* DistinctUntilChanged.swift in Sources */,
C860ECAC1C42EACD00A664B3 /* SectionedViewDataSourceType.swift in Sources */, C860ECAC1C42EACD00A664B3 /* SectionedViewDataSourceType.swift in Sources */,
C8B290E21C959D2900E923D0 /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */,
C89464CF1BC6C2B00055219D /* Just.swift in Sources */, C89464CF1BC6C2B00055219D /* Just.swift in Sources */,
C8F6A1381BEF9DF6007DF367 /* RetryWhen.swift in Sources */, C8F6A1381BEF9DF6007DF367 /* RetryWhen.swift in Sources */,
C89465921BC6C2BC0055219D /* UIControl+Rx.swift in Sources */, C89465921BC6C2BC0055219D /* UIControl+Rx.swift in Sources */,
@ -2138,12 +2179,11 @@
C89464DA1BC6C2B00055219D /* Repeat.swift in Sources */, C89464DA1BC6C2B00055219D /* Repeat.swift in Sources */,
C89465651BC6C2BC0055219D /* CLLocationManager+Rx.swift in Sources */, C89465651BC6C2BC0055219D /* CLLocationManager+Rx.swift in Sources */,
C80DDED81BCE9046006A1832 /* Driver.swift in Sources */, C80DDED81BCE9046006A1832 /* Driver.swift in Sources */,
C8984C401C36A579001E4272 /* Differentiator.swift in Sources */,
C84015881C343595009D2E77 /* Platform.Darwin.swift in Sources */, C84015881C343595009D2E77 /* Platform.Darwin.swift in Sources */,
C80DDED71BCE9046006A1832 /* Driver+Subscription.swift in Sources */, C80DDED71BCE9046006A1832 /* Driver+Subscription.swift in Sources */,
C8F6A1321BEF9DA3007DF367 /* RecursiveScheduler.swift in Sources */, C8F6A1321BEF9DA3007DF367 /* RecursiveScheduler.swift in Sources */,
07A5C3DC1B70B703001EFE5C /* CalculatorViewController.swift in Sources */, 07A5C3DC1B70B703001EFE5C /* CalculatorViewController.swift in Sources */,
C8984C4C1C36A579001E4272 /* SectionModelType.swift in Sources */, C8B290CC1C959D2900E923D0 /* DataSources.swift in Sources */,
C89465961BC6C2BC0055219D /* UILabel+Rx.swift in Sources */, C89465961BC6C2BC0055219D /* UILabel+Rx.swift in Sources */,
C822B1E01C14CEAA0088A01A /* BindingExtensions.swift in Sources */, C822B1E01C14CEAA0088A01A /* BindingExtensions.swift in Sources */,
C8BCD3CE1C14756F005F1280 /* ShareReplay1WhileConnected.swift in Sources */, C8BCD3CE1C14756F005F1280 /* ShareReplay1WhileConnected.swift in Sources */,
@ -2157,22 +2197,23 @@
C864BADE1C3332F10083833C /* TableViewWithEditingCommandsViewController.swift in Sources */, C864BADE1C3332F10083833C /* TableViewWithEditingCommandsViewController.swift in Sources */,
B1604CC41BE5B8CE002E1279 /* DownloadableImage.swift in Sources */, B1604CC41BE5B8CE002E1279 /* DownloadableImage.swift in Sources */,
C80DDED61BCE9046006A1832 /* Driver+Operators.swift in Sources */, C80DDED61BCE9046006A1832 /* Driver+Operators.swift in Sources */,
C8984C361C36A579001E4272 /* RxCollectionViewSectionedDataSource.swift in Sources */,
C843A0941C1CE58700CBA4BD /* UINavigationController+Extensions.swift in Sources */, C843A0941C1CE58700CBA4BD /* UINavigationController+Extensions.swift in Sources */,
C89464B81BC6C2B00055219D /* Observable.swift in Sources */, C89464B81BC6C2B00055219D /* Observable.swift in Sources */,
C894659E1BC6C2BC0055219D /* UITextField+Rx.swift in Sources */, C894659E1BC6C2BC0055219D /* UITextField+Rx.swift in Sources */,
C89464D11BC6C2B00055219D /* Merge.swift in Sources */, C89464D11BC6C2B00055219D /* Merge.swift in Sources */,
C849EF811C3193B10048AC4A /* GitHubSignupViewController1.swift in Sources */, C849EF811C3193B10048AC4A /* GitHubSignupViewController1.swift in Sources */,
C89464A71BC6C2B00055219D /* BinaryDisposable.swift in Sources */, C89464A71BC6C2B00055219D /* BinaryDisposable.swift in Sources */,
C8984C461C36A579001E4272 /* RxDataSourceStarterKit.swift in Sources */,
C8297E361B6CF905000589EA /* RootViewController.swift in Sources */, C8297E361B6CF905000589EA /* RootViewController.swift in Sources */,
C89464BB1BC6C2B00055219D /* AnonymousObservable.swift in Sources */, C89464BB1BC6C2B00055219D /* AnonymousObservable.swift in Sources */,
C89465991BC6C2BC0055219D /* UISegmentedControl+Rx.swift in Sources */, C89465991BC6C2BC0055219D /* UISegmentedControl+Rx.swift in Sources */,
B1B7C3D01BE006870076934E /* TakeLast.swift in Sources */, B1B7C3D01BE006870076934E /* TakeLast.swift in Sources */,
C849EF9E1C31A8750048AC4A /* String+URL.swift in Sources */, C849EF9E1C31A8750048AC4A /* String+URL.swift in Sources */,
C8B290CA1C959D2900E923D0 /* CollectionViewSectionedDataSource.swift in Sources */,
C8C4B4CC1C17728200828BD5 /* MessageSentObserver.swift in Sources */, C8C4B4CC1C17728200828BD5 /* MessageSentObserver.swift in Sources */,
C8B290C01C959D2900E923D0 /* AnimatableSectionModel.swift in Sources */,
C8297E391B6CF905000589EA /* CollectionViewImageCell.swift in Sources */, C8297E391B6CF905000589EA /* CollectionViewImageCell.swift in Sources */,
C894649E1BC6C2B00055219D /* Cancelable.swift in Sources */, C894649E1BC6C2B00055219D /* Cancelable.swift in Sources */,
C8B290DC1C959D2900E923D0 /* TableViewSectionedDataSource.swift in Sources */,
C89464E01BC6C2B00055219D /* SubscribeOn.swift in Sources */, C89464E01BC6C2B00055219D /* SubscribeOn.swift in Sources */,
C89465801BC6C2BC0055219D /* RxTableViewReactiveArrayDataSource.swift in Sources */, C89465801BC6C2BC0055219D /* RxTableViewReactiveArrayDataSource.swift in Sources */,
C89464FA1BC6C2B00055219D /* ObserverType.swift in Sources */, C89464FA1BC6C2B00055219D /* ObserverType.swift in Sources */,
@ -2181,6 +2222,7 @@
C89464DF1BC6C2B00055219D /* StartWith.swift in Sources */, C89464DF1BC6C2B00055219D /* StartWith.swift in Sources */,
C89465881BC6C2BC0055219D /* RxScrollViewDelegateProxy.swift in Sources */, C89465881BC6C2BC0055219D /* RxScrollViewDelegateProxy.swift in Sources */,
C8984CD21C36BC3E001E4272 /* NumberCell.swift in Sources */, C8984CD21C36BC3E001E4272 /* NumberCell.swift in Sources */,
C8B290C61C959D2900E923D0 /* AnimationConfiguration.swift in Sources */,
C89464B71BC6C2B00055219D /* Observable+Extensions.swift in Sources */, C89464B71BC6C2B00055219D /* Observable+Extensions.swift in Sources */,
C8F8C4A11C277F5A0047640B /* Operation.swift in Sources */, C8F8C4A11C277F5A0047640B /* Operation.swift in Sources */,
C89464A01BC6C2B00055219D /* Lock.swift in Sources */, C89464A01BC6C2B00055219D /* Lock.swift in Sources */,
@ -2189,9 +2231,11 @@
C89464C91BC6C2B00055219D /* Do.swift in Sources */, C89464C91BC6C2B00055219D /* Do.swift in Sources */,
C89464A41BC6C2B00055219D /* Queue.swift in Sources */, C89464A41BC6C2B00055219D /* Queue.swift in Sources */,
C89464B91BC6C2B00055219D /* ObservableConvertibleType.swift in Sources */, C89464B91BC6C2B00055219D /* ObservableConvertibleType.swift in Sources */,
C8B290CE1C959D2900E923D0 /* Differentiator.swift in Sources */,
C894657C1BC6C2BC0055219D /* RxCocoa.swift in Sources */, C894657C1BC6C2BC0055219D /* RxCocoa.swift in Sources */,
C894658F1BC6C2BC0055219D /* UIBarButtonItem+Rx.swift in Sources */, C894658F1BC6C2BC0055219D /* UIBarButtonItem+Rx.swift in Sources */,
C89464D61BC6C2B00055219D /* Producer.swift in Sources */, C89464D61BC6C2B00055219D /* Producer.swift in Sources */,
C8B290D01C959D2900E923D0 /* IdentifiableType.swift in Sources */,
C8BCD4041C14BFCA005F1280 /* UIView+Rx.swift in Sources */, C8BCD4041C14BFCA005F1280 /* UIView+Rx.swift in Sources */,
C822B1E81C14E7250088A01A /* SimpleTableViewExampleSectionedViewController.swift in Sources */, C822B1E81C14E7250088A01A /* SimpleTableViewExampleSectionedViewController.swift in Sources */,
C894658A1BC6C2BC0055219D /* RxTableViewDataSourceProxy.swift in Sources */, C894658A1BC6C2BC0055219D /* RxTableViewDataSourceProxy.swift in Sources */,
@ -2199,13 +2243,11 @@
C89465721BC6C2BC0055219D /* ControlTarget.swift in Sources */, C89465721BC6C2BC0055219D /* ControlTarget.swift in Sources */,
C89464EC1BC6C2B00055219D /* Observable+Binding.swift in Sources */, C89464EC1BC6C2B00055219D /* Observable+Binding.swift in Sources */,
C83D73E01C1DBC2A003DC470 /* InvocableType.swift in Sources */, C83D73E01C1DBC2A003DC470 /* InvocableType.swift in Sources */,
C8984C381C36A579001E4272 /* RxCollectionViewSectionedReloadDataSource.swift in Sources */,
C8297E3A1B6CF905000589EA /* WikipediaSearchViewController.swift in Sources */, C8297E3A1B6CF905000589EA /* WikipediaSearchViewController.swift in Sources */,
C89464F21BC6C2B00055219D /* Observable+StandardSequenceOperators.swift in Sources */, C89464F21BC6C2B00055219D /* Observable+StandardSequenceOperators.swift in Sources */,
C89464CC1BC6C2B00055219D /* Filter.swift in Sources */, C89464CC1BC6C2B00055219D /* Filter.swift in Sources */,
C864BAE01C3332F10083833C /* UIImageView+Extensions.swift in Sources */, C864BAE01C3332F10083833C /* UIImageView+Extensions.swift in Sources */,
C80DDED31BCE9046006A1832 /* ControlProperty+Driver.swift in Sources */, C80DDED31BCE9046006A1832 /* ControlProperty+Driver.swift in Sources */,
C8984C341C36A579001E4272 /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */,
C89464C11BC6C2B00055219D /* CombineLatest+CollectionType.swift in Sources */, C89464C11BC6C2B00055219D /* CombineLatest+CollectionType.swift in Sources */,
C89465671BC6C2BC0055219D /* ControlEvent.swift in Sources */, C89465671BC6C2BC0055219D /* ControlEvent.swift in Sources */,
C89464A61BC6C2B00055219D /* AnonymousDisposable.swift in Sources */, C89464A61BC6C2B00055219D /* AnonymousDisposable.swift in Sources */,
@ -2228,18 +2270,18 @@
C89464F11BC6C2B00055219D /* Observable+Single.swift in Sources */, C89464F11BC6C2B00055219D /* Observable+Single.swift in Sources */,
C89464BA1BC6C2B00055219D /* Amb.swift in Sources */, C89464BA1BC6C2B00055219D /* Amb.swift in Sources */,
C894650A1BC6C2B00055219D /* SubjectType.swift in Sources */, C894650A1BC6C2B00055219D /* SubjectType.swift in Sources */,
C8984C4A1C36A579001E4272 /* SectionModel.swift in Sources */,
C89464B11BC6C2B00055219D /* SingleAssignmentDisposable.swift in Sources */, C89464B11BC6C2B00055219D /* SingleAssignmentDisposable.swift in Sources */,
C89464AA1BC6C2B00055219D /* DisposeBase.swift in Sources */, C89464AA1BC6C2B00055219D /* DisposeBase.swift in Sources */,
C89465871BC6C2BC0055219D /* RxCollectionViewDelegateProxy.swift in Sources */, C89465871BC6C2BC0055219D /* RxCollectionViewDelegateProxy.swift in Sources */,
D2245A191BD5654C00E7146F /* WithLatestFrom.swift in Sources */, D2245A191BD5654C00E7146F /* WithLatestFrom.swift in Sources */,
C809E97E1BE697100058D948 /* UIImage+Extensions.swift in Sources */, C809E97E1BE697100058D948 /* UIImage+Extensions.swift in Sources */,
C8B290C21C959D2900E923D0 /* AnimatableSectionModelType+ItemPath.swift in Sources */,
C89464E61BC6C2B00055219D /* Timer.swift in Sources */, C89464E61BC6C2B00055219D /* Timer.swift in Sources */,
C83D73DF1C1DBC2A003DC470 /* InvocableScheduledItem.swift in Sources */, C83D73DF1C1DBC2A003DC470 /* InvocableScheduledItem.swift in Sources */,
C822B1E41C14E4810088A01A /* SimpleTableViewExampleViewController.swift in Sources */, C822B1E41C14E4810088A01A /* SimpleTableViewExampleViewController.swift in Sources */,
C8984C421C36A579001E4272 /* ObservableConvertibleType+Differentiator.swift in Sources */,
C83974131BF77406004F02CC /* KVORepresentable+CoreGraphics.swift in Sources */, C83974131BF77406004F02CC /* KVORepresentable+CoreGraphics.swift in Sources */,
C8297E401B6CF905000589EA /* ImageService.swift in Sources */, C8297E401B6CF905000589EA /* ImageService.swift in Sources */,
C8B290EA1C959D2900E923D0 /* UISectionedViewType+RxAnimatedDataSource.swift in Sources */,
C89464AD1BC6C2B00055219D /* NopDisposable.swift in Sources */, C89464AD1BC6C2B00055219D /* NopDisposable.swift in Sources */,
C84CC5901BDD486300E06A64 /* AsyncLock.swift in Sources */, C84CC5901BDD486300E06A64 /* AsyncLock.swift in Sources */,
C8F6A1311BEF9DA3007DF367 /* OperationQueueScheduler.swift in Sources */, C8F6A1311BEF9DA3007DF367 /* OperationQueueScheduler.swift in Sources */,
@ -2248,6 +2290,7 @@
C8F3FFF51C6FD62E00E60EEC /* UIBindingObserver.swift in Sources */, C8F3FFF51C6FD62E00E60EEC /* UIBindingObserver.swift in Sources */,
C89465091BC6C2B00055219D /* ReplaySubject.swift in Sources */, C89465091BC6C2B00055219D /* ReplaySubject.swift in Sources */,
C8BCD3E01C1480E9005F1280 /* Operators.swift in Sources */, C8BCD3E01C1480E9005F1280 /* Operators.swift in Sources */,
C8B290DE1C959D2900E923D0 /* UI+SectionedViewType.swift in Sources */,
C84CC58E1BDD486300E06A64 /* SynchronizedSubscribeType.swift in Sources */, C84CC58E1BDD486300E06A64 /* SynchronizedSubscribeType.swift in Sources */,
0744CDEE1C4DB78600720FD2 /* GeolocationViewController.swift in Sources */, 0744CDEE1C4DB78600720FD2 /* GeolocationViewController.swift in Sources */,
8479BC6E1C3BC99C00FB8B54 /* UIImagePickerController+Rx.swift in Sources */, 8479BC6E1C3BC99C00FB8B54 /* UIImagePickerController+Rx.swift in Sources */,
@ -2257,6 +2300,7 @@
C89465821BC6C2BC0055219D /* RxCollectionViewDataSourceType.swift in Sources */, C89465821BC6C2BC0055219D /* RxCollectionViewDataSourceType.swift in Sources */,
C8297E421B6CF905000589EA /* WikipediaSearchResult.swift in Sources */, C8297E421B6CF905000589EA /* WikipediaSearchResult.swift in Sources */,
C89465701BC6C2BC0055219D /* Logging.swift in Sources */, C89465701BC6C2BC0055219D /* Logging.swift in Sources */,
C8B290E81C959D2900E923D0 /* RxTableViewSectionedReloadDataSource.swift in Sources */,
C8F8C49E1C277F4F0047640B /* CalculatorAction.swift in Sources */, C8F8C49E1C277F4F0047640B /* CalculatorAction.swift in Sources */,
C89464A31BC6C2B00055219D /* InfiniteSequence.swift in Sources */, C89464A31BC6C2B00055219D /* InfiniteSequence.swift in Sources */,
C89465611BC6C2BC0055219D /* _RX.m in Sources */, C89465611BC6C2BC0055219D /* _RX.m in Sources */,
@ -2274,6 +2318,7 @@
C843A08F1C1CE39900CBA4BD /* GitHubSearchRepositoriesAPI.swift in Sources */, C843A08F1C1CE39900CBA4BD /* GitHubSearchRepositoriesAPI.swift in Sources */,
C83974231BF77413004F02CC /* NSObject+Rx+KVORepresentable.swift in Sources */, C83974231BF77413004F02CC /* NSObject+Rx+KVORepresentable.swift in Sources */,
C8297E461B6CF905000589EA /* Example.swift in Sources */, C8297E461B6CF905000589EA /* Example.swift in Sources */,
C8B290DA1C959D2900E923D0 /* SectionModelType.swift in Sources */,
C89465081BC6C2B00055219D /* PublishSubject.swift in Sources */, C89465081BC6C2B00055219D /* PublishSubject.swift in Sources */,
C89464FC1BC6C2B00055219D /* RxMutableBox.swift in Sources */, C89464FC1BC6C2B00055219D /* RxMutableBox.swift in Sources */,
C809E97B1BE6841C0058D948 /* Wireframe.swift in Sources */, C809E97B1BE6841C0058D948 /* Wireframe.swift in Sources */,
@ -2291,7 +2336,7 @@
B1604CCB1BE5BC45002E1279 /* UIImageView+DownloadableImage.swift in Sources */, B1604CCB1BE5BC45002E1279 /* UIImageView+DownloadableImage.swift in Sources */,
C8297E471B6CF905000589EA /* ViewController.swift in Sources */, C8297E471B6CF905000589EA /* ViewController.swift in Sources */,
C89464E41BC6C2B00055219D /* TakeWhile.swift in Sources */, C89464E41BC6C2B00055219D /* TakeWhile.swift in Sources */,
C8984C481C36A579001E4272 /* SectionedViewType.swift in Sources */, C8B290C81C959D2900E923D0 /* Changeset.swift in Sources */,
C89464F71BC6C2B00055219D /* ObserverBase.swift in Sources */, C89464F71BC6C2B00055219D /* ObserverBase.swift in Sources */,
C89465951BC6C2BC0055219D /* UIImageView+Rx.swift in Sources */, C89465951BC6C2BC0055219D /* UIImageView+Rx.swift in Sources */,
C89464E31BC6C2B00055219D /* TakeUntil.swift in Sources */, C89464E31BC6C2B00055219D /* TakeUntil.swift in Sources */,
@ -2309,16 +2354,18 @@
C89465621BC6C2BC0055219D /* _RXDelegateProxy.m in Sources */, C89465621BC6C2BC0055219D /* _RXDelegateProxy.m in Sources */,
C89464D31BC6C2B00055219D /* Never.swift in Sources */, C89464D31BC6C2B00055219D /* Never.swift in Sources */,
C8F6A1351BEF9DA3007DF367 /* SerialDispatchQueueScheduler.swift in Sources */, C8F6A1351BEF9DA3007DF367 /* SerialDispatchQueueScheduler.swift in Sources */,
C8984C3A1C36A579001E4272 /* RxTableViewSectionedAnimatedDataSource.swift in Sources */,
C89465931BC6C2BC0055219D /* UIDatePicker+Rx.swift in Sources */, C89465931BC6C2BC0055219D /* UIDatePicker+Rx.swift in Sources */,
C8CCB8D41C2D5FBA000EDACC /* String+Rx.swift in Sources */, C8CCB8D41C2D5FBA000EDACC /* String+Rx.swift in Sources */,
C8B290E41C959D2900E923D0 /* RxCollectionViewSectionedReloadDataSource.swift in Sources */,
C84CC58F1BDD486300E06A64 /* SynchronizedUnsubscribeType.swift in Sources */, C84CC58F1BDD486300E06A64 /* SynchronizedUnsubscribeType.swift in Sources */,
C89CDB711BCC45E5002063D9 /* ShareReplay1.swift in Sources */, C89CDB711BCC45E5002063D9 /* ShareReplay1.swift in Sources */,
C89464BD1BC6C2B00055219D /* Buffer.swift in Sources */, C89464BD1BC6C2B00055219D /* Buffer.swift in Sources */,
84C225AB1C340474008724EC /* RxTextStorageDelegateProxy.swift in Sources */, 84C225AB1C340474008724EC /* RxTextStorageDelegateProxy.swift in Sources */,
C89464B31BC6C2B00055219D /* Errors.swift in Sources */, C89464B31BC6C2B00055219D /* Errors.swift in Sources */,
C864BAE21C3332F10083833C /* User.swift in Sources */, C864BAE21C3332F10083833C /* User.swift in Sources */,
C8B290D41C959D2900E923D0 /* ItemPath.swift in Sources */,
C8F6A1301BEF9DA3007DF367 /* MainScheduler.swift in Sources */, C8F6A1301BEF9DA3007DF367 /* MainScheduler.swift in Sources */,
C8B290D61C959D2900E923D0 /* Optional+Extensions.swift in Sources */,
C89464C51BC6C2B00055219D /* Debug.swift in Sources */, C89464C51BC6C2B00055219D /* Debug.swift in Sources */,
C89464AB1BC6C2B00055219D /* NAryDisposable.swift in Sources */, C89464AB1BC6C2B00055219D /* NAryDisposable.swift in Sources */,
C89465631BC6C2BC0055219D /* _RXKVOObserver.m in Sources */, C89465631BC6C2BC0055219D /* _RXKVOObserver.m in Sources */,
@ -2332,9 +2379,9 @@
C89464CA1BC6C2B00055219D /* Empty.swift in Sources */, C89464CA1BC6C2B00055219D /* Empty.swift in Sources */,
C803973B1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */, C803973B1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */,
C89464C61BC6C2B00055219D /* Deferred.swift in Sources */, C89464C61BC6C2B00055219D /* Deferred.swift in Sources */,
C8B290E01C959D2900E923D0 /* ObservableConvertibleType+Differentiator.swift in Sources */,
C8984CD41C36BC3E001E4272 /* NumberSectionView.swift in Sources */, C8984CD41C36BC3E001E4272 /* NumberSectionView.swift in Sources */,
CB883B611BE3AC72000AC2EE /* AddRef.swift in Sources */, CB883B611BE3AC72000AC2EE /* AddRef.swift in Sources */,
C8984C4E1C36A579001E4272 /* UISectionedViewType+RxAnimatedDataSource.swift in Sources */,
C8BCD4021C14BFB7005F1280 /* NSLayoutConstraint+Rx.swift in Sources */, C8BCD4021C14BFB7005F1280 /* NSLayoutConstraint+Rx.swift in Sources */,
D2AF91981BD3D95900A008C1 /* Using.swift in Sources */, D2AF91981BD3D95900A008C1 /* Using.swift in Sources */,
C8297E521B6CF905000589EA /* Dependencies.swift in Sources */, C8297E521B6CF905000589EA /* Dependencies.swift in Sources */,
@ -2347,11 +2394,13 @@
C8297E531B6CF905000589EA /* WikipediaAPI.swift in Sources */, C8297E531B6CF905000589EA /* WikipediaAPI.swift in Sources */,
C89465071BC6C2B00055219D /* BehaviorSubject.swift in Sources */, C89465071BC6C2B00055219D /* BehaviorSubject.swift in Sources */,
C89465911BC6C2BC0055219D /* UICollectionView+Rx.swift in Sources */, C89465911BC6C2BC0055219D /* UICollectionView+Rx.swift in Sources */,
C8B290C41C959D2900E923D0 /* AnimatableSectionModelType.swift in Sources */,
C89464D01BC6C2B00055219D /* Map.swift in Sources */, C89464D01BC6C2B00055219D /* Map.swift in Sources */,
C8297E541B6CF905000589EA /* AppDelegate.swift in Sources */, C8297E541B6CF905000589EA /* AppDelegate.swift in Sources */,
C894659D1BC6C2BC0055219D /* UITableView+Rx.swift in Sources */, C894659D1BC6C2BC0055219D /* UITableView+Rx.swift in Sources */,
C8F6A12C1BEF9DA3007DF367 /* ConcurrentMainScheduler.swift in Sources */, C8F6A12C1BEF9DA3007DF367 /* ConcurrentMainScheduler.swift in Sources */,
C8F8C48B1C277F460047640B /* CalculatorState.swift in Sources */, C8F8C48B1C277F460047640B /* CalculatorState.swift in Sources */,
C8B290E61C959D2900E923D0 /* RxTableViewSectionedAnimatedDataSource.swift in Sources */,
C894657F1BC6C2BC0055219D /* RxCollectionViewReactiveArrayDataSource.swift in Sources */, C894657F1BC6C2BC0055219D /* RxCollectionViewReactiveArrayDataSource.swift in Sources */,
84C225A81C3402E5008724EC /* UITextView+Rx.swift in Sources */, 84C225A81C3402E5008724EC /* UITextView+Rx.swift in Sources */,
8479BC501C3BC98F00FB8B54 /* RxImagePickerDelegateProxy.swift in Sources */, 8479BC501C3BC98F00FB8B54 /* RxImagePickerDelegateProxy.swift in Sources */,
@ -2360,7 +2409,6 @@
C8297E571B6CF905000589EA /* Randomizer.swift in Sources */, C8297E571B6CF905000589EA /* Randomizer.swift in Sources */,
C89464C31BC6C2B00055219D /* Concat.swift in Sources */, C89464C31BC6C2B00055219D /* Concat.swift in Sources */,
C894657A1BC6C2BC0055219D /* NSURLSession+Rx.swift in Sources */, C894657A1BC6C2BC0055219D /* NSURLSession+Rx.swift in Sources */,
C8984C3C1C36A579001E4272 /* RxTableViewSectionedDataSource.swift in Sources */,
C89464F41BC6C2B00055219D /* ObservableType.swift in Sources */, C89464F41BC6C2B00055219D /* ObservableType.swift in Sources */,
C89464CE1BC6C2B00055219D /* Generate.swift in Sources */, C89464CE1BC6C2B00055219D /* Generate.swift in Sources */,
C89465711BC6C2BC0055219D /* Observable+Bind.swift in Sources */, C89465711BC6C2BC0055219D /* Observable+Bind.swift in Sources */,
@ -2372,12 +2420,12 @@
C89464B41BC6C2B00055219D /* Event.swift in Sources */, C89464B41BC6C2B00055219D /* Event.swift in Sources */,
C89465691BC6C2BC0055219D /* ControlProperty.swift in Sources */, C89465691BC6C2BC0055219D /* ControlProperty.swift in Sources */,
C89465061BC6C2B00055219D /* SchedulerType.swift in Sources */, C89465061BC6C2B00055219D /* SchedulerType.swift in Sources */,
C8984C3E1C36A579001E4272 /* RxTableViewSectionedReloadDataSource.swift in Sources */,
C84015891C343595009D2E77 /* Platform.Linux.swift in Sources */, C84015891C343595009D2E77 /* Platform.Linux.swift in Sources */,
C8C4B4BF1C17724A00828BD5 /* _RXObjCRuntime.m in Sources */, C8C4B4BF1C17724A00828BD5 /* _RXObjCRuntime.m in Sources */,
C83D73E11C1DBC2A003DC470 /* ScheduledItem.swift in Sources */, C83D73E11C1DBC2A003DC470 /* ScheduledItem.swift in Sources */,
C849EF871C3195180048AC4A /* GitHubSignupViewController2.swift in Sources */, C849EF871C3195180048AC4A /* GitHubSignupViewController2.swift in Sources */,
C894658C1BC6C2BC0055219D /* RxTextViewDelegateProxy.swift in Sources */, C894658C1BC6C2BC0055219D /* RxTextViewDelegateProxy.swift in Sources */,
C8B290D81C959D2900E923D0 /* SectionModel.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -2385,75 +2433,83 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
C8B290C51C959D2900E923D0 /* AnimationConfiguration.swift in Sources */,
0744CDD41C4DB5F000720FD2 /* GeolocationService.swift in Sources */, 0744CDD41C4DB5F000720FD2 /* GeolocationService.swift in Sources */,
B1604CC91BE5BBFA002E1279 /* UIImageView+DownloadableImage.swift in Sources */, B1604CC91BE5BBFA002E1279 /* UIImageView+DownloadableImage.swift in Sources */,
C86E2F3E1AE5A0CA00C31024 /* SearchResultViewModel.swift in Sources */, C86E2F3E1AE5A0CA00C31024 /* SearchResultViewModel.swift in Sources */,
C83367241AD029AE00C668A7 /* HtmlParsing.swift in Sources */, C83367241AD029AE00C668A7 /* HtmlParsing.swift in Sources */,
C8984C3F1C36A579001E4272 /* Differentiator.swift in Sources */,
C8984C4D1C36A579001E4272 /* UISectionedViewType+RxAnimatedDataSource.swift in Sources */,
C8F8C48A1C277F460047640B /* CalculatorState.swift in Sources */, C8F8C48A1C277F460047640B /* CalculatorState.swift in Sources */,
C8984C411C36A579001E4272 /* ObservableConvertibleType+Differentiator.swift in Sources */,
C843A08E1C1CE39900CBA4BD /* GitHubSearchRepositoriesAPI.swift in Sources */, C843A08E1C1CE39900CBA4BD /* GitHubSearchRepositoriesAPI.swift in Sources */,
C849EF801C3193B10048AC4A /* GitHubSignupViewController1.swift in Sources */, C849EF801C3193B10048AC4A /* GitHubSignupViewController1.swift in Sources */,
C8DF92E51B0B32DA009BCF9A /* RootViewController.swift in Sources */, C8DF92E51B0B32DA009BCF9A /* RootViewController.swift in Sources */,
C8984C371C36A579001E4272 /* RxCollectionViewSectionedReloadDataSource.swift in Sources */,
C8984C4B1C36A579001E4272 /* SectionModelType.swift in Sources */,
C822B1DC1C14CD1C0088A01A /* DefaultImplementations.swift in Sources */, C822B1DC1C14CD1C0088A01A /* DefaultImplementations.swift in Sources */,
C8B290CB1C959D2900E923D0 /* DataSources.swift in Sources */,
C8C46DA81B47F7110020D71E /* CollectionViewImageCell.swift in Sources */, C8C46DA81B47F7110020D71E /* CollectionViewImageCell.swift in Sources */,
C8984CD31C36BC3E001E4272 /* NumberSectionView.swift in Sources */, C8984CD31C36BC3E001E4272 /* NumberSectionView.swift in Sources */,
C822B1E31C14E4810088A01A /* SimpleTableViewExampleViewController.swift in Sources */, C822B1E31C14E4810088A01A /* SimpleTableViewExampleViewController.swift in Sources */,
C8C46DAC1B47F7110020D71E /* WikipediaSearchViewController.swift in Sources */, C8C46DAC1B47F7110020D71E /* WikipediaSearchViewController.swift in Sources */,
C8B290C11C959D2900E923D0 /* AnimatableSectionModelType+ItemPath.swift in Sources */,
07A5C3DB1B70B703001EFE5C /* CalculatorViewController.swift in Sources */, 07A5C3DB1B70B703001EFE5C /* CalculatorViewController.swift in Sources */,
C849EF861C3195180048AC4A /* GitHubSignupViewController2.swift in Sources */, C849EF861C3195180048AC4A /* GitHubSignupViewController2.swift in Sources */,
C8F8C49D1C277F4F0047640B /* CalculatorAction.swift in Sources */, C8F8C49D1C277F4F0047640B /* CalculatorAction.swift in Sources */,
C8B290D51C959D2900E923D0 /* Optional+Extensions.swift in Sources */,
C8B290D91C959D2900E923D0 /* SectionModelType.swift in Sources */,
C864BAD71C3332F10083833C /* DetailViewController.swift in Sources */, C864BAD71C3332F10083833C /* DetailViewController.swift in Sources */,
C8B290C31C959D2900E923D0 /* AnimatableSectionModelType.swift in Sources */,
C822B1DF1C14CEAA0088A01A /* BindingExtensions.swift in Sources */, C822B1DF1C14CEAA0088A01A /* BindingExtensions.swift in Sources */,
C864BAD91C3332F10083833C /* RandomUserAPI.swift in Sources */, C864BAD91C3332F10083833C /* RandomUserAPI.swift in Sources */,
C8B290E51C959D2900E923D0 /* RxTableViewSectionedAnimatedDataSource.swift in Sources */,
C864BADF1C3332F10083833C /* UIImageView+Extensions.swift in Sources */, C864BADF1C3332F10083833C /* UIImageView+Extensions.swift in Sources */,
C8BCD3DF1C1480E9005F1280 /* Operators.swift in Sources */, C8BCD3DF1C1480E9005F1280 /* Operators.swift in Sources */,
C843A0901C1CE39900CBA4BD /* GitHubSearchRepositoriesViewController.swift in Sources */, C843A0901C1CE39900CBA4BD /* GitHubSearchRepositoriesViewController.swift in Sources */,
C803973A1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */, C803973A1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */,
C849EF821C3193B10048AC4A /* GithubSignupViewModel1.swift in Sources */, C849EF821C3193B10048AC4A /* GithubSignupViewModel1.swift in Sources */,
C864BADD1C3332F10083833C /* TableViewWithEditingCommandsViewController.swift in Sources */, C864BADD1C3332F10083833C /* TableViewWithEditingCommandsViewController.swift in Sources */,
C8984C391C36A579001E4272 /* RxTableViewSectionedAnimatedDataSource.swift in Sources */,
C8F8C4A01C277F5A0047640B /* Operation.swift in Sources */, C8F8C4A01C277F5A0047640B /* Operation.swift in Sources */,
C822B1D91C14CBEA0088A01A /* Protocols.swift in Sources */, C822B1D91C14CBEA0088A01A /* Protocols.swift in Sources */,
C8984C471C36A579001E4272 /* SectionedViewType.swift in Sources */,
C8BCD3E61C14A95E005F1280 /* NumbersViewController.swift in Sources */, C8BCD3E61C14A95E005F1280 /* NumbersViewController.swift in Sources */,
C8B290D71C959D2900E923D0 /* SectionModel.swift in Sources */,
C849EF881C3195180048AC4A /* GithubSignupViewModel2.swift in Sources */, C849EF881C3195180048AC4A /* GithubSignupViewModel2.swift in Sources */,
C8B290BF1C959D2900E923D0 /* AnimatableSectionModel.swift in Sources */,
C809E97A1BE6841C0058D948 /* Wireframe.swift in Sources */, C809E97A1BE6841C0058D948 /* Wireframe.swift in Sources */,
C8984C351C36A579001E4272 /* RxCollectionViewSectionedDataSource.swift in Sources */, C8B290C71C959D2900E923D0 /* Changeset.swift in Sources */,
C843A0931C1CE58700CBA4BD /* UINavigationController+Extensions.swift in Sources */, C843A0931C1CE58700CBA4BD /* UINavigationController+Extensions.swift in Sources */,
C8984C3B1C36A579001E4272 /* RxTableViewSectionedDataSource.swift in Sources */,
C864BADB1C3332F10083833C /* String+extensions.swift in Sources */, C864BADB1C3332F10083833C /* String+extensions.swift in Sources */,
C822B1E71C14E7250088A01A /* SimpleTableViewExampleSectionedViewController.swift in Sources */, C822B1E71C14E7250088A01A /* SimpleTableViewExampleSectionedViewController.swift in Sources */,
C8984C491C36A579001E4272 /* SectionModel.swift in Sources */,
C83367251AD029AE00C668A7 /* ImageService.swift in Sources */, C83367251AD029AE00C668A7 /* ImageService.swift in Sources */,
C86E2F471AE5A0CA00C31024 /* WikipediaSearchResult.swift in Sources */, C86E2F471AE5A0CA00C31024 /* WikipediaSearchResult.swift in Sources */,
C8984CD11C36BC3E001E4272 /* NumberCell.swift in Sources */, C8984CD11C36BC3E001E4272 /* NumberCell.swift in Sources */,
C8B290DD1C959D2900E923D0 /* UI+SectionedViewType.swift in Sources */,
C8A2A2C81B4049E300F11F09 /* PseudoRandomGenerator.swift in Sources */, C8A2A2C81B4049E300F11F09 /* PseudoRandomGenerator.swift in Sources */,
C8B290E31C959D2900E923D0 /* RxCollectionViewSectionedReloadDataSource.swift in Sources */,
C8B290CD1C959D2900E923D0 /* Differentiator.swift in Sources */,
C8D132151C42B54B00B59FFF /* UIImagePickerController+RxCreate.swift in Sources */, C8D132151C42B54B00B59FFF /* UIImagePickerController+RxCreate.swift in Sources */,
C8984CD51C36BC3E001E4272 /* PartialUpdatesViewController.swift in Sources */, C8984CD51C36BC3E001E4272 /* PartialUpdatesViewController.swift in Sources */,
8479BC721C3BDAD400FB8B54 /* ImagePickerController.swift in Sources */, 8479BC721C3BDAD400FB8B54 /* ImagePickerController.swift in Sources */,
C864BAE11C3332F10083833C /* User.swift in Sources */, C864BAE11C3332F10083833C /* User.swift in Sources */,
0744CDED1C4DB78600720FD2 /* GeolocationViewController.swift in Sources */, 0744CDED1C4DB78600720FD2 /* GeolocationViewController.swift in Sources */,
C8984C331C36A579001E4272 /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */, C8B290D31C959D2900E923D0 /* ItemPath.swift in Sources */,
C8B290DB1C959D2900E923D0 /* TableViewSectionedDataSource.swift in Sources */,
C83367231AD029AE00C668A7 /* Example.swift in Sources */, C83367231AD029AE00C668A7 /* Example.swift in Sources */,
C8B290D11C959D2900E923D0 /* IdentifiableValue.swift in Sources */,
C8B290E11C959D2900E923D0 /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */,
C8B290E91C959D2900E923D0 /* UISectionedViewType+RxAnimatedDataSource.swift in Sources */,
C890A65D1AEC084100AFF7E6 /* ViewController.swift in Sources */, C890A65D1AEC084100AFF7E6 /* ViewController.swift in Sources */,
C8C46DAA1B47F7110020D71E /* WikipediaSearchCell.swift in Sources */, C8C46DAA1B47F7110020D71E /* WikipediaSearchCell.swift in Sources */,
C8B290C91C959D2900E923D0 /* CollectionViewSectionedDataSource.swift in Sources */,
B1604CB51BE49F8D002E1279 /* DownloadableImage.swift in Sources */, B1604CB51BE49F8D002E1279 /* DownloadableImage.swift in Sources */,
075F13101B4E9D5A000D7861 /* APIWrappersViewController.swift in Sources */, 075F13101B4E9D5A000D7861 /* APIWrappersViewController.swift in Sources */,
B18F3BE21BDB2E8F000AAC79 /* ReachabilityService.swift in Sources */, B18F3BE21BDB2E8F000AAC79 /* ReachabilityService.swift in Sources */,
B18F3BBC1BD92EC8000AAC79 /* Reachability.swift in Sources */, B18F3BBC1BD92EC8000AAC79 /* Reachability.swift in Sources */,
C8984C311C36A579001E4272 /* Changeset.swift in Sources */,
07E3C2331B03605B0010338D /* Dependencies.swift in Sources */, 07E3C2331B03605B0010338D /* Dependencies.swift in Sources */,
C849EF9C1C31A8750048AC4A /* String+URL.swift in Sources */, C849EF9C1C31A8750048AC4A /* String+URL.swift in Sources */,
C86E2F451AE5A0CA00C31024 /* WikipediaAPI.swift in Sources */, C86E2F451AE5A0CA00C31024 /* WikipediaAPI.swift in Sources */,
C8984C451C36A579001E4272 /* RxDataSourceStarterKit.swift in Sources */,
C8984C3D1C36A579001E4272 /* RxTableViewSectionedReloadDataSource.swift in Sources */,
C8DF92CD1B0B2F84009BCF9A /* AppDelegate.swift in Sources */, C8DF92CD1B0B2F84009BCF9A /* AppDelegate.swift in Sources */,
C86E2F461AE5A0CA00C31024 /* WikipediaPage.swift in Sources */, C86E2F461AE5A0CA00C31024 /* WikipediaPage.swift in Sources */,
C8B290E71C959D2900E923D0 /* RxTableViewSectionedReloadDataSource.swift in Sources */,
C809E97D1BE697100058D948 /* UIImage+Extensions.swift in Sources */, C809E97D1BE697100058D948 /* UIImage+Extensions.swift in Sources */,
C8B290DF1C959D2900E923D0 /* ObservableConvertibleType+Differentiator.swift in Sources */,
C8B290CF1C959D2900E923D0 /* IdentifiableType.swift in Sources */,
C8A2A2CB1B404A1200F11F09 /* Randomizer.swift in Sources */, C8A2A2CB1B404A1200F11F09 /* Randomizer.swift in Sources */,
C8BCD3EA1C14B02A005F1280 /* SimpleValidationViewController.swift in Sources */, C8BCD3EA1C14B02A005F1280 /* SimpleValidationViewController.swift in Sources */,
); );

View File

@ -30,14 +30,14 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat
let tableView = self.tableView let tableView = self.tableView
let searchBar = self.searchBar let searchBar = self.searchBar
dataSource.cellFactory = { (tv, ip, repository: Repository) in dataSource.configureCell = { (_, tv, ip, repository: Repository) in
let cell = tv.dequeueReusableCellWithIdentifier("Cell")! let cell = tv.dequeueReusableCellWithIdentifier("Cell")!
cell.textLabel?.text = repository.name cell.textLabel?.text = repository.name
cell.detailTextLabel?.text = repository.url cell.detailTextLabel?.text = repository.url
return cell return cell
} }
dataSource.titleForHeaderInSection = { [unowned dataSource] sectionIndex in dataSource.titleForHeaderInSection = { dataSource, sectionIndex in
let section = dataSource.sectionAtIndex(sectionIndex) let section = dataSource.sectionAtIndex(sectionIndex)
return section.items.count > 0 ? "Repositories (\(section.items.count))" : "No repositories found" return section.items.count > 0 ? "Repositories (\(section.items.count))" : "No repositories found"
} }

View File

@ -43,7 +43,7 @@ class SimpleTableViewExampleSectionedViewController
]) ])
]) ])
dataSource.cellFactory = { (tv, indexPath, element) in dataSource.configureCell = { (_, tv, indexPath, element) in
let cell = tv.dequeueReusableCellWithIdentifier("Cell")! let cell = tv.dequeueReusableCellWithIdentifier("Cell")!
cell.textLabel?.text = "\(element) @ row \(indexPath.row)" cell.textLabel?.text = "\(element) @ row \(indexPath.row)"
return cell return cell

View File

@ -28,7 +28,7 @@ class PartialUpdatesViewController : ViewController {
var timer: NSTimer? = nil var timer: NSTimer? = nil
static let initialValue: [HashableSectionModel<String, Int>] = [ static let initialValue: [AnimatableSectionModel<String, Int>] = [
NumberSection(model: "section 1", items: [1, 2, 3]), NumberSection(model: "section 1", items: [1, 2, 3]),
NumberSection(model: "section 2", items: [4, 5, 6]), NumberSection(model: "section 2", items: [4, 5, 6]),
NumberSection(model: "section 3", items: [7, 8, 9]), NumberSection(model: "section 3", items: [7, 8, 9]),
@ -42,7 +42,7 @@ class PartialUpdatesViewController : ViewController {
] ]
static let firstChange: [HashableSectionModel<String, Int>]? = nil static let firstChange: [AnimatableSectionModel<String, Int>]? = nil
var generator = Randomizer(rng: PseudoRandomGenerator(4, 3), sections: initialValue) var generator = Randomizer(rng: PseudoRandomGenerator(4, 3), sections: initialValue)
@ -63,10 +63,10 @@ class PartialUpdatesViewController : ViewController {
let nSections = 10 let nSections = 10
let nItems = 100 let nItems = 100
var sections = [HashableSectionModel<String, Int>]() var sections = [AnimatableSectionModel<String, Int>]()
for i in 0 ..< nSections { for i in 0 ..< nSections {
sections.append(HashableSectionModel(model: "Section \(i + 1)", items: Array(i * nItems ..< (i + 1) * nItems))) sections.append(AnimatableSectionModel(model: "Section \(i + 1)", items: Array(i * nItems ..< (i + 1) * nItems)))
} }
generator = Randomizer(rng: PseudoRandomGenerator(4, 3), sections: sections) generator = Randomizer(rng: PseudoRandomGenerator(4, 3), sections: sections)
@ -136,7 +136,7 @@ class PartialUpdatesViewController : ViewController {
} }
func skinTableViewDataSource(dataSource: RxTableViewSectionedDataSource<NumberSection>) { func skinTableViewDataSource(dataSource: RxTableViewSectionedDataSource<NumberSection>) {
dataSource.cellFactory = { (tv, ip, i) in dataSource.configureCell = { (_, tv, ip, i) in
let cell = tv.dequeueReusableCellWithIdentifier("Cell") let cell = tv.dequeueReusableCellWithIdentifier("Cell")
?? UITableViewCell(style:.Default, reuseIdentifier: "Cell") ?? UITableViewCell(style:.Default, reuseIdentifier: "Cell")
@ -145,13 +145,13 @@ class PartialUpdatesViewController : ViewController {
return cell return cell
} }
dataSource.titleForHeaderInSection = { [unowned dataSource] (section: Int) -> String in dataSource.titleForHeaderInSection = { (ds, section: Int) -> String in
return dataSource.sectionAtIndex(section).model return dataSource.sectionAtIndex(section).model
} }
} }
func skinCollectionViewDataSource(dataSource: RxCollectionViewSectionedDataSource<NumberSection>) { func skinCollectionViewDataSource(dataSource: CollectionViewSectionedDataSource<NumberSection>) {
dataSource.cellFactory = { (cv, ip, i) in dataSource.cellFactory = { (_, cv, ip, i) in
let cell = cv.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: ip) as! NumberCell let cell = cv.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: ip) as! NumberCell
cell.value!.text = "\(i)" cell.value!.text = "\(i)"
@ -159,7 +159,7 @@ class PartialUpdatesViewController : ViewController {
return cell return cell
} }
dataSource.supplementaryViewFactory = { [unowned dataSource] (cv, kind, ip) in dataSource.supplementaryViewFactory = { (dataSource, cv, kind, ip) in
let section = cv.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "Section", forIndexPath: ip) as! NumberSectionView let section = cv.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "Section", forIndexPath: ip) as! NumberSectionView
section.value!.text = "\(dataSource.sectionAtIndex(ip.section).model)" section.value!.text = "\(dataSource.sectionAtIndex(ip.section).model)"

View File

@ -152,13 +152,13 @@ class TableViewWithEditingCommandsViewController: ViewController, UITableViewDel
static func configureDataSource() -> RxTableViewSectionedReloadDataSource<SectionModel<String, User>> { static func configureDataSource() -> RxTableViewSectionedReloadDataSource<SectionModel<String, User>> {
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, User>>() let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, User>>()
dataSource.cellFactory = { (tv, ip, user: User) in dataSource.configureCell = { (_, tv, ip, user: User) in
let cell = tv.dequeueReusableCellWithIdentifier("Cell")! let cell = tv.dequeueReusableCellWithIdentifier("Cell")!
cell.textLabel?.text = user.firstName + " " + user.lastName cell.textLabel?.text = user.firstName + " " + user.lastName
return cell return cell
} }
dataSource.titleForHeaderInSection = { [unowned dataSource] sectionIndex in dataSource.titleForHeaderInSection = { dataSource, sectionIndex in
return dataSource.sectionAtIndex(sectionIndex).model return dataSource.sectionAtIndex(sectionIndex).model
} }

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
typealias NumberSection = HashableSectionModel<String, Int> typealias NumberSection = AnimatableSectionModel<String, Int>
let insertItems = true let insertItems = true
let deleteItems = true let deleteItems = true
@ -74,7 +74,7 @@ class Randomizer {
if rng.get_random() % 2 == 0 { if rng.get_random() % 2 == 0 {
let itemIndex = rng.get_random() % (itemCount + 1) let itemIndex = rng.get_random() % (itemCount + 1)
if insertItems { if insertItems {
sections[sectionIndex].items.insert(unusedValue, atIndex: itemIndex) sections[sectionIndex].items.insert(IdentitifiableValue(value: unusedValue), atIndex: itemIndex)
} }
else { else {
nextUnusedItems.append(unusedValue) nextUnusedItems.append(unusedValue)
@ -83,14 +83,14 @@ class Randomizer {
// update // update
else { else {
if itemCount == 0 { if itemCount == 0 {
sections[sectionIndex].items.insert(unusedValue, atIndex: 0) sections[sectionIndex].items.insert(IdentitifiableValue(value: unusedValue), atIndex: 0)
continue continue
} }
let itemIndex = rng.get_random() % itemCount let itemIndex = rng.get_random() % itemCount
if reloadItems { if reloadItems {
nextUnusedItems.append(sections[sectionIndex].items.removeAtIndex(itemIndex)) nextUnusedItems.append(sections[sectionIndex].items.removeAtIndex(itemIndex).value)
sections[sectionIndex].items.insert(unusedValue, atIndex: itemIndex) sections[sectionIndex].items.insert(IdentitifiableValue(value: unusedValue), atIndex: itemIndex)
} }
else { else {
@ -151,7 +151,7 @@ class Randomizer {
let sourceItemIndex = rng.get_random() % sectionItemCount let sourceItemIndex = rng.get_random() % sectionItemCount
if deleteItems { if deleteItems {
nextUnusedItems.append(sections[sourceSectionIndex].items.removeAtIndex(sourceItemIndex)) nextUnusedItems.append(sections[sourceSectionIndex].items.removeAtIndex(sourceItemIndex).value)
} }
} }
@ -188,7 +188,7 @@ class Randomizer {
let section = sections.removeAtIndex(sectionIndex) let section = sections.removeAtIndex(sectionIndex)
for item in section.items { for item in section.items {
nextUnusedItems.append(item) nextUnusedItems.append(item.value)
} }
nextUnusedSections.append(section.model) nextUnusedSections.append(section.model)