mirror of
https://github.com/ReactiveX/RxSwift.git
synced 2024-10-05 06:27:29 +03:00
Synchronizes RxDataSources
.
This commit is contained in:
parent
b8c7e520b8
commit
80edd849ff
@ -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)" : "")
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
|||||||
//
|
|
||||||
// RxDataSourceStarterKit.swift
|
|
||||||
// RxExample
|
|
||||||
//
|
|
||||||
// Created by Krunoslav Zaher on 8/29/15.
|
|
||||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
@ -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))]
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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()
|
@ -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)
|
@ -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()
|
@ -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)
|
@ -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
|
||||||
|
}
|
@ -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]
|
||||||
|
}
|
||||||
|
}
|
@ -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])
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
94
RxExample/RxDataSources/DataSources/Changeset.swift
Normal file
94
RxExample/RxDataSources/DataSources/Changeset.swift
Normal 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)" : "")
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
35
RxExample/RxDataSources/DataSources/DataSources.swift
Normal file
35
RxExample/RxDataSources/DataSources/DataSources.swift
Normal 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
|
||||||
|
}
|
685
RxExample/RxDataSources/DataSources/Differentiator.swift
Normal file
685
RxExample/RxDataSources/DataSources/Differentiator.swift
Normal 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
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
}
|
15
RxExample/RxDataSources/DataSources/IdentifiableType.swift
Normal file
15
RxExample/RxDataSources/DataSources/IdentifiableType.swift
Normal 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 }
|
||||||
|
}
|
35
RxExample/RxDataSources/DataSources/IdentifiableValue.swift
Normal file
35
RxExample/RxDataSources/DataSources/IdentifiableValue.swift
Normal 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
|
||||||
|
}
|
22
RxExample/RxDataSources/DataSources/ItemPath.swift
Normal file
22
RxExample/RxDataSources/DataSources/ItemPath.swift
Normal 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
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
RxExample/RxDataSources/DataSources/SectionModel.swift
Normal file
33
RxExample/RxDataSources/DataSources/SectionModel.swift
Normal 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)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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])
|
|
||||||
}
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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 {
|
@ -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.
|
||||||
|
|
@ -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 */,
|
||||||
);
|
);
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)"
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user