begin fixing up the examples
This commit is contained in:
parent
c4cf2e0d12
commit
39a20441e1
|
|
@ -486,7 +486,7 @@ extension DriverConvertibleType {
|
|||
public func startWith(_ element: E)
|
||||
-> Driver<E> {
|
||||
let source = self.asObservable()
|
||||
.startWith(elements: element)
|
||||
.startWith(element)
|
||||
|
||||
return Driver(source)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ extension DriverConvertibleType {
|
|||
@warn_unused_result(message: "http://git.io/rxs.ud")
|
||||
public func drive(_ onNext: ((E) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil) -> Disposable {
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
return self.asObservable().subscribe(onNext, onCompleted: onCompleted, onDisposed: onDisposed)
|
||||
return self.asObservable().subscribe(onNext: onNext, onCompleted: onCompleted, onDisposed: onDisposed)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ extension ObservableConvertibleType {
|
|||
- returns: Driving observable sequence.
|
||||
*/
|
||||
@warn_unused_result(message:"http://git.io/rxs.uo")
|
||||
public func asDriver(_ onErrorJustReturn: E) -> Driver<E> {
|
||||
public func asDriver(onErrorJustReturn: E) -> Driver<E> {
|
||||
let source = self
|
||||
.asObservable()
|
||||
.observeOn(driverObserveOnScheduler)
|
||||
|
|
@ -34,7 +34,7 @@ extension ObservableConvertibleType {
|
|||
- returns: Driving observable sequence.
|
||||
*/
|
||||
@warn_unused_result(message:"http://git.io/rxs.uo")
|
||||
public func asDriver(_ onErrorDriveWith: Driver<E>) -> Driver<E> {
|
||||
public func asDriver(onErrorDriveWith: Driver<E>) -> Driver<E> {
|
||||
let source = self
|
||||
.asObservable()
|
||||
.observeOn(driverObserveOnScheduler)
|
||||
|
|
@ -51,7 +51,7 @@ extension ObservableConvertibleType {
|
|||
- returns: Driving observable sequence.
|
||||
*/
|
||||
@warn_unused_result(message:"http://git.io/rxs.uo")
|
||||
public func asDriver(_ onErrorRecover: (error: ErrorProtocol) -> Driver<E>) -> Driver<E> {
|
||||
public func asDriver(onErrorRecover: (error: ErrorProtocol) -> Driver<E>) -> Driver<E> {
|
||||
let source = self
|
||||
.asObservable()
|
||||
.observeOn(driverObserveOnScheduler)
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ extension ObservableType {
|
|||
*/
|
||||
@warn_unused_result(message: "http://git.io/rxs.ud")
|
||||
public func bindNext(_ onNext: (E) -> Void) -> Disposable {
|
||||
return subscribe(onNext, onError: { error in
|
||||
return subscribe(onNext: onNext, onError: { error in
|
||||
let error = "Binding error: \(error)"
|
||||
#if DEBUG
|
||||
rxFatalError(error)
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ extension ObservableType where E == AnyObject? {
|
|||
.map { _ in
|
||||
return Observable.just(nil)
|
||||
}
|
||||
.startWith(elements: self.asObservable())
|
||||
.startWith(self.asObservable())
|
||||
.switchLatest()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ extension UISearchBar {
|
|||
.map { a in
|
||||
return a[1] as? String ?? ""
|
||||
}
|
||||
.startWith(elements: text)
|
||||
.startWith(text)
|
||||
}
|
||||
|
||||
let bindingObserver = UIBindingObserver(UIElement: self) { (searchBar, text: String) in
|
||||
|
|
@ -70,7 +70,7 @@ extension UISearchBar {
|
|||
.map { a in
|
||||
return try castOrThrow(Int.self, a[1])
|
||||
}
|
||||
.startWith(elements: index)
|
||||
.startWith(index)
|
||||
}
|
||||
|
||||
let bindingObserver = UIBindingObserver(UIElement: self) { (searchBar, index: Int) in
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ extension UITextView : RxTextInput {
|
|||
?? Observable.empty()
|
||||
|
||||
return textChanged
|
||||
.startWith(elements: text)
|
||||
.startWith(text)
|
||||
}
|
||||
|
||||
let bindingObserver = UIBindingObserver(UIElement: self) { (textView, text: String) in
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public class RxCollectionViewSectionedAnimatedDataSource<S: AnimatableSectionMod
|
|||
super.init()
|
||||
}
|
||||
|
||||
public func collectionView(collectionView: UICollectionView, observedEvent: Event<Element>) {
|
||||
public func collectionView(_ collectionView: UICollectionView, observedEvent: Event<Element>) {
|
||||
UIBindingObserver(UIElement: self) { dataSource, newSections in
|
||||
if !self.dataSet {
|
||||
self.dataSet = true
|
||||
|
|
@ -35,7 +35,7 @@ public class RxCollectionViewSectionedAnimatedDataSource<S: AnimatableSectionMod
|
|||
collectionView.reloadData()
|
||||
}
|
||||
else {
|
||||
dispatch_async(dispatch_get_main_queue()) {
|
||||
DispatchQueue.main.async {
|
||||
let oldSections = dataSource.sectionModels
|
||||
do {
|
||||
let differences = try differencesForSectionedView(oldSections, finalSections: newSections)
|
||||
|
|
@ -55,4 +55,4 @@ public class RxCollectionViewSectionedAnimatedDataSource<S: AnimatableSectionMod
|
|||
}
|
||||
}.on(observedEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ public class RxCollectionViewSectionedReloadDataSource<S: SectionModelType>
|
|||
super.init()
|
||||
}
|
||||
|
||||
public func collectionView(collectionView: UICollectionView, observedEvent: Event<Element>) {
|
||||
public func collectionView(_ collectionView: UICollectionView, observedEvent: Event<Element>) {
|
||||
UIBindingObserver(UIElement: self) { dataSource, element in
|
||||
dataSource.setSections(element)
|
||||
collectionView.reloadData()
|
||||
}.on(observedEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public class RxTableViewSectionedAnimatedDataSource<S: AnimatableSectionModelTyp
|
|||
super.init()
|
||||
}
|
||||
|
||||
public func tableView(tableView: UITableView, observedEvent: Event<Element>) {
|
||||
public func tableView(_ tableView: UITableView, observedEvent: Event<Element>) {
|
||||
UIBindingObserver(UIElement: self) { dataSource, newSections in
|
||||
if !self.dataSet {
|
||||
self.dataSet = true
|
||||
|
|
@ -34,7 +34,7 @@ public class RxTableViewSectionedAnimatedDataSource<S: AnimatableSectionModelTyp
|
|||
tableView.reloadData()
|
||||
}
|
||||
else {
|
||||
dispatch_async(dispatch_get_main_queue()) {
|
||||
DispatchQueue.main.async {
|
||||
let oldSections = dataSource.sectionModels
|
||||
do {
|
||||
let differences = try differencesForSectionedView(oldSections, finalSections: newSections)
|
||||
|
|
@ -54,4 +54,4 @@ public class RxTableViewSectionedAnimatedDataSource<S: AnimatableSectionModelTyp
|
|||
}
|
||||
}.on(observedEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ public class RxTableViewSectionedReloadDataSource<S: SectionModelType>
|
|||
super.init()
|
||||
}
|
||||
|
||||
public func tableView(tableView: UITableView, observedEvent: Event<Element>) {
|
||||
public func tableView(_ tableView: UITableView, observedEvent: Event<Element>) {
|
||||
UIBindingObserver(UIElement: self) { dataSource, element in
|
||||
dataSource.setSections(element)
|
||||
tableView.reloadData()
|
||||
}.on(observedEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,17 +14,17 @@ import RxCocoa
|
|||
#endif
|
||||
|
||||
extension UITableView {
|
||||
@available(*, deprecated=0.7, renamed="rx_itemsWithDataSource", message="You can just use normal `rx_itemsWithDataSource` extension.")
|
||||
@available(*, deprecated:0.7, renamed:"rx_itemsWithDataSource", message:"You can just use normal `rx_itemsWithDataSource` extension.")
|
||||
public func rx_itemsAnimatedWithDataSource<
|
||||
DataSource: protocol<RxTableViewDataSourceType, UITableViewDataSource>,
|
||||
S: SequenceType,
|
||||
S: Sequence,
|
||||
O: ObservableType
|
||||
where
|
||||
DataSource.Element == S,
|
||||
O.E == S,
|
||||
S.Generator.Element: AnimatableSectionModelType
|
||||
S.Iterator.Element: AnimatableSectionModelType
|
||||
>
|
||||
(dataSource: DataSource)
|
||||
(_ dataSource: DataSource)
|
||||
-> (source: O)
|
||||
-> Disposable {
|
||||
return { source in
|
||||
|
|
@ -34,21 +34,21 @@ extension UITableView {
|
|||
}
|
||||
|
||||
extension UICollectionView {
|
||||
@available(*, deprecated=0.7, renamed="rx_itemsWithDataSource", message="You can just use normal `rx_itemsWithDataSource` extension.")
|
||||
@available(*, deprecated:0.7, renamed:"rx_itemsWithDataSource", message:"You can just use normal `rx_itemsWithDataSource` extension.")
|
||||
public func rx_itemsAnimatedWithDataSource<
|
||||
DataSource: protocol<RxCollectionViewDataSourceType, UICollectionViewDataSource>,
|
||||
S: SequenceType,
|
||||
S: Sequence,
|
||||
O: ObservableType
|
||||
where
|
||||
DataSource.Element == S,
|
||||
O.E == S,
|
||||
S.Generator.Element: AnimatableSectionModelType
|
||||
S.Iterator.Element: AnimatableSectionModelType
|
||||
>
|
||||
(dataSource: DataSource)
|
||||
(_ dataSource: DataSource)
|
||||
-> (source: O)
|
||||
-> Disposable {
|
||||
return { source in
|
||||
return self.rx_itemsWithDataSource(dataSource)(source: source)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,11 +17,11 @@ public struct AnimationConfiguration {
|
|||
let reloadAnimation: UITableViewRowAnimation
|
||||
let deleteAnimation: UITableViewRowAnimation
|
||||
|
||||
public init(insertAnimation: UITableViewRowAnimation = .Automatic,
|
||||
reloadAnimation: UITableViewRowAnimation = .Automatic,
|
||||
deleteAnimation: UITableViewRowAnimation = .Automatic) {
|
||||
public init(insertAnimation: UITableViewRowAnimation = .automatic,
|
||||
reloadAnimation: UITableViewRowAnimation = .automatic,
|
||||
deleteAnimation: UITableViewRowAnimation = .automatic) {
|
||||
self.insertAnimation = insertAnimation
|
||||
self.reloadAnimation = reloadAnimation
|
||||
self.deleteAnimation = deleteAnimation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,19 +11,19 @@ import Foundation
|
|||
import Foundation
|
||||
|
||||
extension Array where Element: SectionModelType {
|
||||
mutating func moveFromSourceIndexPath(sourceIndexPath: NSIndexPath, destinationIndexPath: NSIndexPath) {
|
||||
let sourceSection = self[sourceIndexPath.section]
|
||||
mutating func moveFromSourceIndexPath(_ sourceIndexPath: IndexPath, destinationIndexPath: IndexPath) {
|
||||
let sourceSection = self[(sourceIndexPath as NSIndexPath).section]
|
||||
var sourceItems = sourceSection.items
|
||||
|
||||
let sourceItem = sourceItems.removeAtIndex(sourceIndexPath.item)
|
||||
let sourceItem = sourceItems.remove(at: (sourceIndexPath as NSIndexPath).item)
|
||||
|
||||
let sourceSectionNew = Element(original: sourceSection, items: sourceItems)
|
||||
self[sourceIndexPath.section] = sourceSectionNew
|
||||
self[(sourceIndexPath as NSIndexPath).section] = sourceSectionNew
|
||||
|
||||
let destinationSection = self[destinationIndexPath.section]
|
||||
let destinationSection = self[(destinationIndexPath as NSIndexPath).section]
|
||||
var destinationItems = destinationSection.items
|
||||
destinationItems.insert(sourceItem, atIndex: destinationIndexPath.item)
|
||||
destinationItems.insert(sourceItem, at: (destinationIndexPath as NSIndexPath).item)
|
||||
|
||||
self[destinationIndexPath.section] = Element(original: destinationSection, items: destinationItems)
|
||||
self[(destinationIndexPath as NSIndexPath).section] = Element(original: destinationSection, items: destinationItems)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ public struct Changeset<S: SectionModelType> {
|
|||
self.updatedItems = updatedItems
|
||||
}
|
||||
|
||||
public static func initialValue(sections: [S]) -> Changeset<S> {
|
||||
public static func initialValue(_ sections: [S]) -> Changeset<S> {
|
||||
return Changeset<S>(
|
||||
insertedSections: Array(0 ..< sections.count) as [Int],
|
||||
finalSections: sections,
|
||||
|
|
@ -80,7 +80,7 @@ extension Changeset
|
|||
: CustomDebugStringConvertible {
|
||||
|
||||
public var debugDescription : String {
|
||||
let serializedSections = "[\n" + finalSections.map { "\($0)" }.joinWithSeparator(",\n") + "\n]\n"
|
||||
let serializedSections = "[\n" + finalSections.map { "\($0)" }.joined(separator: ",\n") + "\n]\n"
|
||||
return " >> Final sections"
|
||||
+ " \n\(serializedSections)"
|
||||
+ (insertedSections.count > 0 || deletedSections.count > 0 || movedSections.count > 0 || updatedSections.count > 0 ? "\nSections:" : "")
|
||||
|
|
|
|||
|
|
@ -16,50 +16,50 @@ public class _CollectionViewSectionedDataSource
|
|||
: NSObject
|
||||
, UICollectionViewDataSource {
|
||||
|
||||
func _numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
|
||||
func _numberOfSectionsInCollectionView(_ collectionView: UICollectionView) -> Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
public func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
|
||||
public func numberOfSections(in collectionView: UICollectionView) -> Int {
|
||||
return _numberOfSectionsInCollectionView(collectionView)
|
||||
}
|
||||
|
||||
func _rx_collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
func _rx_collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
public func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return _rx_collectionView(collectionView, numberOfItemsInSection: section)
|
||||
}
|
||||
|
||||
func _rx_collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
|
||||
func _rx_collectionView(_ collectionView: UICollectionView, cellForItemAtIndexPath indexPath: IndexPath) -> UICollectionViewCell {
|
||||
return (nil as UICollectionViewCell?)!
|
||||
}
|
||||
|
||||
public func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
|
||||
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
return _rx_collectionView(collectionView, cellForItemAtIndexPath: indexPath)
|
||||
}
|
||||
|
||||
func _rx_collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
|
||||
func _rx_collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: IndexPath) -> UICollectionReusableView {
|
||||
return (nil as UICollectionReusableView?)!
|
||||
}
|
||||
|
||||
public func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
|
||||
public func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
|
||||
return _rx_collectionView(collectionView, viewForSupplementaryElementOfKind: kind, atIndexPath: indexPath)
|
||||
}
|
||||
|
||||
func _rx_collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool {
|
||||
func _rx_collectionView(_ collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: IndexPath) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
public func collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool {
|
||||
public func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {
|
||||
return _rx_collectionView(collectionView, canMoveItemAtIndexPath: indexPath)
|
||||
}
|
||||
|
||||
func _rx_collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
|
||||
func _rx_collectionView(_ collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: IndexPath, toIndexPath destinationIndexPath: IndexPath) {
|
||||
|
||||
}
|
||||
public func collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
|
||||
public func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
|
||||
_rx_collectionView(collectionView, moveItemAtIndexPath: sourceIndexPath, toIndexPath: destinationIndexPath)
|
||||
}
|
||||
|
||||
|
|
@ -70,8 +70,8 @@ public class CollectionViewSectionedDataSource<S: SectionModelType>
|
|||
, SectionedViewDataSourceType {
|
||||
public typealias I = S.Item
|
||||
public typealias Section = S
|
||||
public typealias CellFactory = (CollectionViewSectionedDataSource<S>, UICollectionView, NSIndexPath, I) -> UICollectionViewCell
|
||||
public typealias SupplementaryViewFactory = (CollectionViewSectionedDataSource<S>, UICollectionView, String, NSIndexPath) -> UICollectionReusableView
|
||||
public typealias CellFactory = (CollectionViewSectionedDataSource<S>, UICollectionView, IndexPath, I) -> UICollectionViewCell
|
||||
public typealias SupplementaryViewFactory = (CollectionViewSectionedDataSource<S>, UICollectionView, String, IndexPath) -> UICollectionReusableView
|
||||
|
||||
// This structure exists because model can be mutable
|
||||
// In that case current state value should be preserved.
|
||||
|
|
@ -87,28 +87,28 @@ public class CollectionViewSectionedDataSource<S: SectionModelType>
|
|||
return _sectionModels.map { Section(original: $0.model, items: $0.items) }
|
||||
}
|
||||
|
||||
public func sectionAtIndex(section: Int) -> S {
|
||||
public func sectionAtIndex(_ section: Int) -> S {
|
||||
let sectionModel = self._sectionModels[section]
|
||||
return S(original: sectionModel.model, items: sectionModel.items)
|
||||
}
|
||||
|
||||
public func itemAtIndexPath(indexPath: NSIndexPath) -> I {
|
||||
return self._sectionModels[indexPath.section].items[indexPath.item]
|
||||
public func itemAtIndexPath(_ indexPath: IndexPath) -> I {
|
||||
return self._sectionModels[(indexPath as NSIndexPath).section].items[(indexPath as NSIndexPath).item]
|
||||
}
|
||||
|
||||
public func modelAtIndexPath(indexPath: NSIndexPath) throws -> Any {
|
||||
public func modelAtIndexPath(_ indexPath: IndexPath) 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) }
|
||||
}
|
||||
|
||||
public var cellFactory: CellFactory! = nil
|
||||
public var supplementaryViewFactory: SupplementaryViewFactory
|
||||
|
||||
public var moveItem: ((CollectionViewSectionedDataSource<S>, sourceIndexPath:NSIndexPath, destinationIndexPath:NSIndexPath) -> Void)?
|
||||
public var canMoveItemAtIndexPath: ((CollectionViewSectionedDataSource<S>, indexPath:NSIndexPath) -> Bool)?
|
||||
public var moveItem: ((CollectionViewSectionedDataSource<S>, sourceIndexPath:IndexPath, destinationIndexPath:IndexPath) -> Void)?
|
||||
public var canMoveItemAtIndexPath: ((CollectionViewSectionedDataSource<S>, indexPath:IndexPath) -> Bool)?
|
||||
|
||||
public override init() {
|
||||
self.cellFactory = {_, _, _, _ in return (nil as UICollectionViewCell?)! }
|
||||
|
|
@ -130,25 +130,25 @@ public class CollectionViewSectionedDataSource<S: SectionModelType>
|
|||
|
||||
// UICollectionViewDataSource
|
||||
|
||||
override func _numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
|
||||
override func _numberOfSectionsInCollectionView(_ collectionView: UICollectionView) -> Int {
|
||||
return _sectionModels.count
|
||||
}
|
||||
|
||||
override func _rx_collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
override func _rx_collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return _sectionModels[section].items.count
|
||||
}
|
||||
|
||||
override func _rx_collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
|
||||
precondition(indexPath.item < _sectionModels[indexPath.section].items.count)
|
||||
override func _rx_collectionView(_ collectionView: UICollectionView, cellForItemAtIndexPath indexPath: IndexPath) -> UICollectionViewCell {
|
||||
precondition((indexPath as NSIndexPath).item < _sectionModels[(indexPath as NSIndexPath).section].items.count)
|
||||
|
||||
return cellFactory(self, collectionView, indexPath, itemAtIndexPath(indexPath))
|
||||
}
|
||||
|
||||
override func _rx_collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
|
||||
override func _rx_collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: IndexPath) -> UICollectionReusableView {
|
||||
return supplementaryViewFactory(self, collectionView, kind, indexPath)
|
||||
}
|
||||
|
||||
override func _rx_collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool {
|
||||
override func _rx_collectionView(_ collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: IndexPath) -> Bool {
|
||||
guard let canMoveItem = canMoveItemAtIndexPath?(self, indexPath: indexPath) else {
|
||||
return super._rx_collectionView(collectionView, canMoveItemAtIndexPath: indexPath)
|
||||
}
|
||||
|
|
@ -156,8 +156,8 @@ public class CollectionViewSectionedDataSource<S: SectionModelType>
|
|||
return canMoveItem
|
||||
}
|
||||
|
||||
override func _rx_collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
|
||||
override func _rx_collectionView(_ collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: IndexPath, toIndexPath destinationIndexPath: IndexPath) {
|
||||
self._sectionModels.moveFromSourceIndexPath(sourceIndexPath, destinationIndexPath: destinationIndexPath)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,27 +9,27 @@
|
|||
import Foundation
|
||||
|
||||
enum RxDataSourceError : ErrorProtocol {
|
||||
case UnwrappingOptional
|
||||
case PreconditionFailed(message: String)
|
||||
case unwrappingOptional
|
||||
case preconditionFailed(message: String)
|
||||
}
|
||||
|
||||
func rxPrecondition(condition: Bool, @autoclosure _ message: () -> String) throws -> () {
|
||||
func rxPrecondition(_ condition: Bool, _ message: @autoclosure() -> String) throws -> () {
|
||||
if condition {
|
||||
return
|
||||
}
|
||||
rxDebugFatalError("Precondition failed")
|
||||
|
||||
throw RxDataSourceError.PreconditionFailed(message: message())
|
||||
throw RxDataSourceError.preconditionFailed(message: message())
|
||||
}
|
||||
|
||||
func rxDebugFatalError(error: ErrorProtocol) {
|
||||
func rxDebugFatalError(_ error: ErrorProtocol) {
|
||||
rxDebugFatalError("\(error)")
|
||||
}
|
||||
|
||||
func rxDebugFatalError(message: String) {
|
||||
func rxDebugFatalError(_ message: String) {
|
||||
#if DEBUG
|
||||
fatalError(message)
|
||||
#else
|
||||
print(message)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,48 +11,48 @@ import Foundation
|
|||
public enum DifferentiatorError
|
||||
: ErrorProtocol
|
||||
, CustomDebugStringConvertible {
|
||||
case DuplicateItem(item: Any)
|
||||
case DuplicateSection(section: Any)
|
||||
case duplicateItem(item: Any)
|
||||
case duplicateSection(section: Any)
|
||||
}
|
||||
|
||||
extension DifferentiatorError {
|
||||
public var debugDescription: String {
|
||||
switch self {
|
||||
case let .DuplicateItem(item):
|
||||
case let .duplicateItem(item):
|
||||
return "Duplicate item \(item)"
|
||||
case let .DuplicateSection(section):
|
||||
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
|
||||
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:
|
||||
case .inserted:
|
||||
return "Inserted"
|
||||
case .InsertedAutomatically:
|
||||
case .insertedAutomatically:
|
||||
return "InsertedAutomatically"
|
||||
case .Deleted:
|
||||
case .deleted:
|
||||
return "Deleted"
|
||||
case .DeletedAutomatically:
|
||||
case .deletedAutomatically:
|
||||
return "DeletedAutomatically"
|
||||
case .Moved:
|
||||
case .moved:
|
||||
return "Moved"
|
||||
case .MovedAutomatically:
|
||||
case .movedAutomatically:
|
||||
return "MovedAutomatically"
|
||||
case .Untouched:
|
||||
case .untouched:
|
||||
return "Untouched"
|
||||
}
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ extension SectionAssociatedData : CustomDebugStringConvertible {
|
|||
|
||||
extension SectionAssociatedData {
|
||||
static var initial: SectionAssociatedData {
|
||||
return SectionAssociatedData(event: .Untouched, indexAfterDelete: nil, moveIndex: nil)
|
||||
return SectionAssociatedData(event: .untouched, indexAfterDelete: nil, moveIndex: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,18 +95,18 @@ extension ItemAssociatedData : CustomDebugStringConvertible {
|
|||
|
||||
extension ItemAssociatedData {
|
||||
static var initial : ItemAssociatedData {
|
||||
return ItemAssociatedData(event: .Untouched, indexAfterDelete: nil, moveIndex: nil)
|
||||
return ItemAssociatedData(event: .untouched, indexAfterDelete: nil, moveIndex: nil)
|
||||
}
|
||||
}
|
||||
|
||||
func indexSections<S: AnimatableSectionModelType>(sections: [S]) throws -> [S.Identity : Int] {
|
||||
func indexSections<S: AnimatableSectionModelType>(_ sections: [S]) throws -> [S.Identity : Int] {
|
||||
var indexedSections: [S.Identity : Int] = [:]
|
||||
for (i, section) in sections.enumerate() {
|
||||
for (i, section) in sections.enumerated() {
|
||||
guard indexedSections[section.identity] == nil else {
|
||||
#if DEBUG
|
||||
precondition(indexedSections[section.identity] == nil, "Section \(section) has already been indexed at \(indexedSections[section.identity]!)")
|
||||
#endif
|
||||
throw DifferentiatorError.DuplicateItem(item: section)
|
||||
throw DifferentiatorError.duplicateItem(item: section)
|
||||
}
|
||||
indexedSections[section.identity] = i
|
||||
}
|
||||
|
|
@ -114,7 +114,7 @@ func indexSections<S: AnimatableSectionModelType>(sections: [S]) throws -> [S.Id
|
|||
return indexedSections
|
||||
}
|
||||
|
||||
func indexSectionItems<S: AnimatableSectionModelType>(sections: [S]) throws -> [S.Item.Identity : (Int, Int)] {
|
||||
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
|
||||
|
|
@ -124,12 +124,12 @@ func indexSectionItems<S: AnimatableSectionModelType>(sections: [S]) throws -> [
|
|||
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() {
|
||||
for (j, item) in sections[i].items.enumerated() {
|
||||
guard indexedItems[item.identity] == nil else {
|
||||
#if DEBUG
|
||||
precondition(indexedItems[item.identity] == nil, "Item \(item) has already been indexed at \(indexedItems[item.identity]!)" )
|
||||
#endif
|
||||
throw DifferentiatorError.DuplicateItem(item: item)
|
||||
throw DifferentiatorError.duplicateItem(item: item)
|
||||
}
|
||||
indexedItems[item.identity] = (i, j)
|
||||
}
|
||||
|
|
@ -263,7 +263,7 @@ to = [
|
|||
// There maybe exists a better division, but time will tell.
|
||||
//
|
||||
public func differencesForSectionedView<S: AnimatableSectionModelType>(
|
||||
initialSections: [S],
|
||||
_ initialSections: [S],
|
||||
finalSections: [S]
|
||||
)
|
||||
throws -> [Changeset<S>] {
|
||||
|
|
@ -273,9 +273,9 @@ public func differencesForSectionedView<S: AnimatableSectionModelType>(
|
|||
|
||||
var sectionCommands = try CommandGenerator<S>.generatorForInitialSections(initialSections, finalSections: finalSections)
|
||||
|
||||
result.appendContentsOf(try sectionCommands.generateDeleteSections())
|
||||
result.appendContentsOf(try sectionCommands.generateInsertAndMoveSections())
|
||||
result.appendContentsOf(try sectionCommands.generateNewAndMovedItems())
|
||||
result.append(contentsOf: try sectionCommands.generateDeleteSections())
|
||||
result.append(contentsOf: try sectionCommands.generateInsertAndMoveSections())
|
||||
result.append(contentsOf: try sectionCommands.generateNewAndMovedItems())
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
@ -291,7 +291,7 @@ struct CommandGenerator<S: AnimatableSectionModelType> {
|
|||
let finalItemData: [[ItemAssociatedData]]
|
||||
|
||||
static func generatorForInitialSections(
|
||||
initialSections: [S],
|
||||
_ initialSections: [S],
|
||||
finalSections: [S]
|
||||
) throws -> CommandGenerator<S> {
|
||||
|
||||
|
|
@ -314,25 +314,25 @@ struct CommandGenerator<S: AnimatableSectionModelType> {
|
|||
)
|
||||
}
|
||||
|
||||
static func calculateItemMovementsForInitialSections(initialSections: [S], finalSections: [S],
|
||||
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)
|
||||
return [ItemAssociatedData](repeating: ItemAssociatedData.initial, count: s.items.count)
|
||||
}
|
||||
|
||||
var finalItemData = finalSections.map { s in
|
||||
return [ItemAssociatedData](count: s.items.count, repeatedValue: ItemAssociatedData.initial)
|
||||
return [ItemAssociatedData](repeating: ItemAssociatedData.initial, count: s.items.count)
|
||||
}
|
||||
|
||||
let initialItemIndexes = try indexSectionItems(initialSections)
|
||||
|
||||
for i in 0 ..< finalSections.count {
|
||||
for (j, item) in finalSections[i].items.enumerate() {
|
||||
for (j, item) in finalSections[i].items.enumerated() {
|
||||
guard let initialItemIndex = initialItemIndexes[item.identity] else {
|
||||
continue
|
||||
}
|
||||
if initialItemData[initialItemIndex.0][initialItemIndex.1].moveIndex != nil {
|
||||
throw DifferentiatorError.DuplicateItem(item: item)
|
||||
throw DifferentiatorError.duplicateItem(item: item)
|
||||
}
|
||||
|
||||
initialItemData[initialItemIndex.0][initialItemIndex.1].moveIndex = ItemPath(sectionIndex: i, itemIndex: j)
|
||||
|
|
@ -346,7 +346,7 @@ struct CommandGenerator<S: AnimatableSectionModelType> {
|
|||
}
|
||||
|
||||
while i2 < initialSections[initialSectionIndex].items.count {
|
||||
if initialItemData[initialSectionIndex][i2].event == .Untouched {
|
||||
if initialItemData[initialSectionIndex][i2].event == .untouched {
|
||||
return i2
|
||||
}
|
||||
|
||||
|
|
@ -366,15 +366,15 @@ struct CommandGenerator<S: AnimatableSectionModelType> {
|
|||
for j in 0 ..< initialSections[i].items.count {
|
||||
|
||||
guard let finalIndexPath = initialItemData[i][j].moveIndex else {
|
||||
initialItemData[i][j].event = .Deleted
|
||||
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
|
||||
if finalSectionData[finalIndexPath.sectionIndex].event == .inserted {
|
||||
initialItemData[i][j].event = .deleted
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -394,25 +394,25 @@ struct CommandGenerator<S: AnimatableSectionModelType> {
|
|||
untouchedIndex = findNextUntouchedOldIndex(originalSectionIndex, untouchedIndex)
|
||||
|
||||
guard let originalIndex = finalItemData[i][j].moveIndex else {
|
||||
finalItemData[i][j].event = .Inserted
|
||||
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
|
||||
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 {
|
||||
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")
|
||||
try rxPrecondition(initialSectionEvent == .moved || initialSectionEvent == .movedAutomatically, "Section not moved")
|
||||
|
||||
let eventType = originalIndex == ItemPath(sectionIndex: originalSectionIndex, itemIndex: untouchedIndex ?? -1)
|
||||
? EditEvent.MovedAutomatically : EditEvent.Moved
|
||||
? EditEvent.movedAutomatically : EditEvent.moved
|
||||
|
||||
initialItemData[originalIndex.sectionIndex][originalIndex.itemIndex].event = eventType
|
||||
finalItemData[i][j].event = eventType
|
||||
|
|
@ -423,20 +423,20 @@ struct CommandGenerator<S: AnimatableSectionModelType> {
|
|||
return (initialItemData, finalItemData)
|
||||
}
|
||||
|
||||
static func calculateSectionMovementsForInitialSections(initialSections: [S], finalSections: [S]) throws -> ([SectionAssociatedData], [SectionAssociatedData]) {
|
||||
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)
|
||||
var initialSectionData = [SectionAssociatedData](repeating: SectionAssociatedData.initial, count: initialSections.count)
|
||||
var finalSectionData = [SectionAssociatedData](repeating: SectionAssociatedData.initial, count: finalSections.count)
|
||||
|
||||
for (i, section) in finalSections.enumerate() {
|
||||
for (i, section) in finalSections.enumerated() {
|
||||
guard let initialSectionIndex = initialSectionIndexes[section.identity] else {
|
||||
continue
|
||||
}
|
||||
|
||||
if initialSectionData[initialSectionIndex].moveIndex != nil {
|
||||
throw DifferentiatorError.DuplicateSection(section: section)
|
||||
throw DifferentiatorError.duplicateSection(section: section)
|
||||
}
|
||||
|
||||
initialSectionData[initialSectionIndex].moveIndex = i
|
||||
|
|
@ -448,7 +448,7 @@ struct CommandGenerator<S: AnimatableSectionModelType> {
|
|||
// deleted sections
|
||||
for i in 0 ..< initialSectionData.count {
|
||||
if initialSectionData[i].moveIndex == nil {
|
||||
initialSectionData[i].event = .Deleted
|
||||
initialSectionData[i].event = .deleted
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -465,7 +465,7 @@ struct CommandGenerator<S: AnimatableSectionModelType> {
|
|||
}
|
||||
|
||||
while i < initialSections.count {
|
||||
if initialSectionData[i].event == .Untouched {
|
||||
if initialSectionData[i].event == .untouched {
|
||||
return i
|
||||
}
|
||||
|
||||
|
|
@ -483,20 +483,20 @@ struct CommandGenerator<S: AnimatableSectionModelType> {
|
|||
|
||||
// oh, it did exist
|
||||
if let oldSectionIndex = finalSectionData[i].moveIndex {
|
||||
let moveType = oldSectionIndex != untouchedOldIndex ? EditEvent.Moved : EditEvent.MovedAutomatically
|
||||
let moveType = oldSectionIndex != untouchedOldIndex ? EditEvent.moved : EditEvent.movedAutomatically
|
||||
|
||||
finalSectionData[i].event = moveType
|
||||
initialSectionData[oldSectionIndex].event = moveType
|
||||
}
|
||||
else {
|
||||
finalSectionData[i].event = .Inserted
|
||||
finalSectionData[i].event = .inserted
|
||||
}
|
||||
}
|
||||
|
||||
// inserted sections
|
||||
for (i, section) in finalSectionData.enumerate() {
|
||||
for (i, section) in finalSectionData.enumerated() {
|
||||
if section.moveIndex == nil {
|
||||
finalSectionData[i].event == .Inserted
|
||||
finalSectionData[i].event == .inserted
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -512,13 +512,13 @@ struct CommandGenerator<S: AnimatableSectionModelType> {
|
|||
|
||||
// mark deleted items {
|
||||
// 1rst stage again (I know, I know ...)
|
||||
for (i, initialSection) in initialSections.enumerate() {
|
||||
for (i, initialSection) in initialSections.enumerated() {
|
||||
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 {
|
||||
if event == .deleted {
|
||||
deletedSections.append(i)
|
||||
continue
|
||||
}
|
||||
|
|
@ -527,9 +527,9 @@ struct CommandGenerator<S: AnimatableSectionModelType> {
|
|||
for j in 0 ..< initialSection.items.count {
|
||||
let event = initialItemData[i][j].event
|
||||
switch event {
|
||||
case .Deleted:
|
||||
case .deleted:
|
||||
deletedItems.append(ItemPath(sectionIndex: i, itemIndex: j))
|
||||
case .Moved, .MovedAutomatically:
|
||||
case .moved, .movedAutomatically:
|
||||
let finalItemIndex = try initialItemData[i][j].moveIndex.unwrap()
|
||||
let finalItem = finalSections[finalItemIndex]
|
||||
if finalItem != initialSections[i].items[j] {
|
||||
|
|
@ -564,11 +564,11 @@ struct CommandGenerator<S: AnimatableSectionModelType> {
|
|||
|
||||
for i in 0 ..< initialSections.count {
|
||||
switch initialSectionData[i].event {
|
||||
case .Deleted:
|
||||
case .deleted:
|
||||
break
|
||||
case .Moved:
|
||||
case .moved:
|
||||
movedSections.append((from: try initialSectionData[i].indexAfterDelete.unwrap(), to: try initialSectionData[i].moveIndex.unwrap()))
|
||||
case .MovedAutomatically:
|
||||
case .movedAutomatically:
|
||||
break
|
||||
default:
|
||||
try rxPrecondition(false, "Unhandled case in initial sections")
|
||||
|
|
@ -577,7 +577,7 @@ struct CommandGenerator<S: AnimatableSectionModelType> {
|
|||
|
||||
for i in 0 ..< finalSections.count {
|
||||
switch finalSectionData[i].event {
|
||||
case .Inserted:
|
||||
case .inserted:
|
||||
insertedSections.append(i)
|
||||
default:
|
||||
break
|
||||
|
|
@ -589,22 +589,22 @@ struct CommandGenerator<S: AnimatableSectionModelType> {
|
|||
}
|
||||
|
||||
// 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 sectionsAfterChange: [S] = try self.finalSections.enumerated().map { i, s -> S in
|
||||
let event = self.finalSectionData[i].event
|
||||
|
||||
if event == .Inserted {
|
||||
if event == .inserted {
|
||||
// it's already set up
|
||||
return s
|
||||
}
|
||||
else if event == .Moved || event == .MovedAutomatically {
|
||||
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() {
|
||||
for (j, _) in originalSection.items.enumerated() {
|
||||
let initialData = self.initialItemData[originalSectionIndex][j]
|
||||
|
||||
guard initialData.event != .Deleted else {
|
||||
guard initialData.event != .deleted else {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -642,21 +642,21 @@ struct CommandGenerator<S: AnimatableSectionModelType> {
|
|||
|
||||
let sectionEvent = finalSectionData[i].event
|
||||
// new and deleted sections cause reload automatically
|
||||
if sectionEvent != .Moved && sectionEvent != .MovedAutomatically {
|
||||
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")
|
||||
try rxPrecondition(currentItemEvent != .untouched, "Current event is not untouched")
|
||||
|
||||
let event = finalItemData[i][j].event
|
||||
|
||||
switch event {
|
||||
case .Inserted:
|
||||
case .inserted:
|
||||
insertedItems.append(ItemPath(sectionIndex: i, itemIndex: j))
|
||||
case .Moved:
|
||||
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()
|
||||
|
|
@ -682,4 +682,4 @@ struct CommandGenerator<S: AnimatableSectionModelType> {
|
|||
movedItems: movedItems
|
||||
)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ extension Optional {
|
|||
}
|
||||
else {
|
||||
rxDebugFatalError("Error during unwrapping optional")
|
||||
throw RxDataSourceError.UnwrappingOptional
|
||||
throw RxDataSourceError.unwrappingOptional
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,82 +17,82 @@ public class _TableViewSectionedDataSource
|
|||
: NSObject
|
||||
, UITableViewDataSource {
|
||||
|
||||
func _numberOfSectionsInTableView(tableView: UITableView) -> Int {
|
||||
func _numberOfSectionsInTableView(_ tableView: UITableView) -> Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
public func numberOfSectionsInTableView(tableView: UITableView) -> Int {
|
||||
public func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return _numberOfSectionsInTableView(tableView)
|
||||
}
|
||||
|
||||
func _rx_tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
func _rx_tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
public func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return _rx_tableView(tableView, numberOfRowsInSection: section)
|
||||
}
|
||||
|
||||
func _rx_tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
|
||||
func _rx_tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell {
|
||||
return (nil as UITableViewCell?)!
|
||||
}
|
||||
|
||||
public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
|
||||
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
return _rx_tableView(tableView, cellForRowAtIndexPath: indexPath)
|
||||
}
|
||||
|
||||
func _rx_tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
func _rx_tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
return nil
|
||||
}
|
||||
|
||||
public func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
public func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
return _rx_tableView(tableView, titleForHeaderInSection: section)
|
||||
}
|
||||
|
||||
func _rx_tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||
func _rx_tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||
return nil
|
||||
}
|
||||
|
||||
public func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||
public func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||
return _rx_tableView(tableView, titleForFooterInSection: section)
|
||||
}
|
||||
|
||||
func _rx_tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
|
||||
func _rx_tableView(_ tableView: UITableView, canEditRowAtIndexPath indexPath: IndexPath) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
public func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
|
||||
public func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
||||
return _rx_tableView(tableView, canEditRowAtIndexPath: indexPath)
|
||||
}
|
||||
|
||||
func _rx_tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
|
||||
func _rx_tableView(_ tableView: UITableView, canMoveRowAtIndexPath indexPath: IndexPath) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
public func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
|
||||
public func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
|
||||
return _rx_tableView(tableView, canMoveRowAtIndexPath: indexPath)
|
||||
}
|
||||
|
||||
func _sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
|
||||
func _sectionIndexTitlesForTableView(_ tableView: UITableView) -> [String]? {
|
||||
return nil
|
||||
}
|
||||
|
||||
public func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
|
||||
public func sectionIndexTitles(for tableView: UITableView) -> [String]? {
|
||||
return _sectionIndexTitlesForTableView(tableView)
|
||||
}
|
||||
|
||||
func _rx_tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
|
||||
func _rx_tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
public func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
|
||||
public func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
|
||||
return _rx_tableView(tableView, sectionForSectionIndexTitle: title, atIndex: index)
|
||||
}
|
||||
|
||||
func _rx_tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
|
||||
func _rx_tableView(_ tableView: UITableView, moveRowAtIndexPath sourceIndexPath: IndexPath, toIndexPath destinationIndexPath: IndexPath) {
|
||||
}
|
||||
|
||||
public func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
|
||||
public func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
|
||||
_rx_tableView(tableView, moveRowAtIndexPath: sourceIndexPath, toIndexPath: destinationIndexPath)
|
||||
}
|
||||
}
|
||||
|
|
@ -103,7 +103,7 @@ public class RxTableViewSectionedDataSource<S: SectionModelType>
|
|||
|
||||
public typealias I = S.Item
|
||||
public typealias Section = S
|
||||
public typealias CellFactory = (RxTableViewSectionedDataSource<S>, UITableView, NSIndexPath, I) -> UITableViewCell
|
||||
public typealias CellFactory = (RxTableViewSectionedDataSource<S>, UITableView, IndexPath, I) -> UITableViewCell
|
||||
|
||||
// This structure exists because model can be mutable
|
||||
// In that case current state value should be preserved.
|
||||
|
|
@ -119,26 +119,26 @@ public class RxTableViewSectionedDataSource<S: SectionModelType>
|
|||
return _sectionModels.map { Section(original: $0.model, items: $0.items) }
|
||||
}
|
||||
|
||||
public func sectionAtIndex(section: Int) -> S {
|
||||
public func sectionAtIndex(_ section: Int) -> S {
|
||||
let sectionModel = _sectionModels[section]
|
||||
return Section(original: sectionModel.model, items: sectionModel.items)
|
||||
}
|
||||
|
||||
public func itemAtIndexPath(indexPath: NSIndexPath) -> I {
|
||||
return self._sectionModels[indexPath.section].items[indexPath.item]
|
||||
public func itemAtIndexPath(_ indexPath: IndexPath) -> I {
|
||||
return self._sectionModels[(indexPath as NSIndexPath).section].items[(indexPath as NSIndexPath).item]
|
||||
}
|
||||
|
||||
public func setItem(item item: I, indexPath: NSIndexPath) {
|
||||
var section = self._sectionModels[indexPath.section]
|
||||
section.items[indexPath.item] = item
|
||||
self._sectionModels[indexPath.section] = section
|
||||
public func setItem(item: I, indexPath: IndexPath) {
|
||||
var section = self._sectionModels[(indexPath as NSIndexPath).section]
|
||||
section.items[(indexPath as NSIndexPath).item] = item
|
||||
self._sectionModels[(indexPath as NSIndexPath).section] = section
|
||||
}
|
||||
|
||||
public func modelAtIndexPath(indexPath: NSIndexPath) throws -> Any {
|
||||
public func modelAtIndexPath(_ indexPath: IndexPath) 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) }
|
||||
}
|
||||
|
||||
|
|
@ -147,13 +147,13 @@ public class RxTableViewSectionedDataSource<S: SectionModelType>
|
|||
public var titleForHeaderInSection: ((RxTableViewSectionedDataSource<S>, section: Int) -> String?)?
|
||||
public var titleForFooterInSection: ((RxTableViewSectionedDataSource<S>, section: Int) -> String?)?
|
||||
|
||||
public var canEditRowAtIndexPath: ((RxTableViewSectionedDataSource<S>, indexPath: NSIndexPath) -> Bool)?
|
||||
public var canMoveRowAtIndexPath: ((RxTableViewSectionedDataSource<S>, indexPath: NSIndexPath) -> Bool)?
|
||||
public var canEditRowAtIndexPath: ((RxTableViewSectionedDataSource<S>, indexPath: IndexPath) -> Bool)?
|
||||
public var canMoveRowAtIndexPath: ((RxTableViewSectionedDataSource<S>, indexPath: IndexPath) -> 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() {
|
||||
super.init()
|
||||
|
|
@ -168,29 +168,29 @@ public class RxTableViewSectionedDataSource<S: SectionModelType>
|
|||
|
||||
// UITableViewDataSource
|
||||
|
||||
override func _numberOfSectionsInTableView(tableView: UITableView) -> Int {
|
||||
override func _numberOfSectionsInTableView(_ tableView: UITableView) -> Int {
|
||||
return _sectionModels.count
|
||||
}
|
||||
|
||||
override func _rx_tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
override func _rx_tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return _sectionModels[section].items.count
|
||||
}
|
||||
|
||||
override func _rx_tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
|
||||
precondition(indexPath.item < _sectionModels[indexPath.section].items.count)
|
||||
override func _rx_tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell {
|
||||
precondition((indexPath as NSIndexPath).item < _sectionModels[(indexPath as NSIndexPath).section].items.count)
|
||||
|
||||
return configureCell(self, tableView, indexPath, itemAtIndexPath(indexPath))
|
||||
}
|
||||
|
||||
override func _rx_tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
override func _rx_tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
return titleForHeaderInSection?(self, section: section)
|
||||
}
|
||||
|
||||
override func _rx_tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||
override func _rx_tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||
return titleForFooterInSection?(self, section: section)
|
||||
}
|
||||
|
||||
override func _rx_tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
|
||||
override func _rx_tableView(_ tableView: UITableView, canEditRowAtIndexPath indexPath: IndexPath) -> Bool {
|
||||
guard let canEditRow = canEditRowAtIndexPath?(self, indexPath: indexPath) else {
|
||||
return super._rx_tableView(tableView, canEditRowAtIndexPath: indexPath)
|
||||
}
|
||||
|
|
@ -198,7 +198,7 @@ public class RxTableViewSectionedDataSource<S: SectionModelType>
|
|||
return canEditRow
|
||||
}
|
||||
|
||||
override func _rx_tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
|
||||
override func _rx_tableView(_ tableView: UITableView, canMoveRowAtIndexPath indexPath: IndexPath) -> Bool {
|
||||
guard let canMoveRow = canMoveRowAtIndexPath?(self, indexPath: indexPath) else {
|
||||
return super._rx_tableView(tableView, canMoveRowAtIndexPath: indexPath)
|
||||
}
|
||||
|
|
@ -206,11 +206,11 @@ public class RxTableViewSectionedDataSource<S: SectionModelType>
|
|||
return canMoveRow
|
||||
}
|
||||
|
||||
override func _rx_tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
|
||||
override func _rx_tableView(_ tableView: UITableView, moveRowAtIndexPath sourceIndexPath: IndexPath, toIndexPath destinationIndexPath: IndexPath) {
|
||||
self._sectionModels.moveFromSourceIndexPath(sourceIndexPath, destinationIndexPath: destinationIndexPath)
|
||||
}
|
||||
|
||||
override func _sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
|
||||
override func _sectionIndexTitlesForTableView(_ tableView: UITableView) -> [String]? {
|
||||
guard let titles = sectionIndexTitles?(self) else {
|
||||
return super._sectionIndexTitlesForTableView(tableView)
|
||||
}
|
||||
|
|
@ -218,7 +218,7 @@ public class RxTableViewSectionedDataSource<S: SectionModelType>
|
|||
return titles
|
||||
}
|
||||
|
||||
override func _rx_tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
|
||||
override func _rx_tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
|
||||
guard let section = sectionForSectionIndexTitle?(self, title: title, index: index) else {
|
||||
return super._rx_tableView(tableView, sectionForSectionIndexTitle: title, atIndex: index)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,49 +9,49 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
|
||||
func indexSet(values: [Int]) -> NSIndexSet {
|
||||
func indexSet(_ values: [Int]) -> IndexSet {
|
||||
let indexSet = NSMutableIndexSet()
|
||||
for i in values {
|
||||
indexSet.addIndex(i)
|
||||
indexSet.add(i)
|
||||
}
|
||||
return indexSet
|
||||
return indexSet as IndexSet
|
||||
}
|
||||
|
||||
extension UITableView : SectionedViewType {
|
||||
|
||||
public func insertItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) {
|
||||
self.insertRowsAtIndexPaths(paths, withRowAnimation: animationStyle)
|
||||
public func insertItemsAtIndexPaths(_ paths: [IndexPath], animationStyle: UITableViewRowAnimation) {
|
||||
self.insertRows(at: paths, with: animationStyle)
|
||||
}
|
||||
|
||||
public func deleteItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) {
|
||||
self.deleteRowsAtIndexPaths(paths, withRowAnimation: animationStyle)
|
||||
public func deleteItemsAtIndexPaths(_ paths: [IndexPath], animationStyle: UITableViewRowAnimation) {
|
||||
self.deleteRows(at: paths, with: animationStyle)
|
||||
}
|
||||
|
||||
public func moveItemAtIndexPath(from: NSIndexPath, to: NSIndexPath) {
|
||||
self.moveRowAtIndexPath(from, toIndexPath: to)
|
||||
public func moveItemAtIndexPath(_ from: IndexPath, to: IndexPath) {
|
||||
self.moveRow(at: from, to: to)
|
||||
}
|
||||
|
||||
public func reloadItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) {
|
||||
self.reloadRowsAtIndexPaths(paths, withRowAnimation: animationStyle)
|
||||
public func reloadItemsAtIndexPaths(_ paths: [IndexPath], animationStyle: UITableViewRowAnimation) {
|
||||
self.reloadRows(at: paths, with: animationStyle)
|
||||
}
|
||||
|
||||
public func insertSections(sections: [Int], animationStyle: UITableViewRowAnimation) {
|
||||
self.insertSections(indexSet(sections), withRowAnimation: animationStyle)
|
||||
public func insertSections(_ sections: [Int], animationStyle: UITableViewRowAnimation) {
|
||||
self.insertSections(indexSet(sections), with: animationStyle)
|
||||
}
|
||||
|
||||
public func deleteSections(sections: [Int], animationStyle: UITableViewRowAnimation) {
|
||||
self.deleteSections(indexSet(sections), withRowAnimation: animationStyle)
|
||||
public func deleteSections(_ sections: [Int], animationStyle: UITableViewRowAnimation) {
|
||||
self.deleteSections(indexSet(sections), with: animationStyle)
|
||||
}
|
||||
|
||||
public func moveSection(from: Int, to: Int) {
|
||||
public func moveSection(_ from: Int, to: Int) {
|
||||
self.moveSection(from, toSection: to)
|
||||
}
|
||||
|
||||
public func reloadSections(sections: [Int], animationStyle: UITableViewRowAnimation) {
|
||||
self.reloadSections(indexSet(sections), withRowAnimation: animationStyle)
|
||||
public func reloadSections(_ sections: [Int], animationStyle: UITableViewRowAnimation) {
|
||||
self.reloadSections(indexSet(sections), with: animationStyle)
|
||||
}
|
||||
|
||||
public func performBatchUpdates<S: SectionModelType>(changes: Changeset<S>, animationConfiguration: AnimationConfiguration) {
|
||||
public func performBatchUpdates<S: SectionModelType>(_ changes: Changeset<S>, animationConfiguration: AnimationConfiguration) {
|
||||
self.beginUpdates()
|
||||
_performBatchUpdates(self, changes: changes, animationConfiguration: animationConfiguration)
|
||||
self.endUpdates()
|
||||
|
|
@ -59,39 +59,39 @@ extension UITableView : SectionedViewType {
|
|||
}
|
||||
|
||||
extension UICollectionView : SectionedViewType {
|
||||
public func insertItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) {
|
||||
self.insertItemsAtIndexPaths(paths)
|
||||
public func insertItemsAtIndexPaths(_ paths: [IndexPath], animationStyle: UITableViewRowAnimation) {
|
||||
self.insertItems(at: paths)
|
||||
}
|
||||
|
||||
public func deleteItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) {
|
||||
self.deleteItemsAtIndexPaths(paths)
|
||||
public func deleteItemsAtIndexPaths(_ paths: [IndexPath], animationStyle: UITableViewRowAnimation) {
|
||||
self.deleteItems(at: paths)
|
||||
}
|
||||
|
||||
public func moveItemAtIndexPath(from: NSIndexPath, to: NSIndexPath) {
|
||||
self.moveItemAtIndexPath(from, toIndexPath: to)
|
||||
public func moveItemAtIndexPath(_ from: IndexPath, to: IndexPath) {
|
||||
self.moveItem(at: from, to: to)
|
||||
}
|
||||
|
||||
public func reloadItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation) {
|
||||
self.reloadItemsAtIndexPaths(paths)
|
||||
public func reloadItemsAtIndexPaths(_ paths: [IndexPath], animationStyle: UITableViewRowAnimation) {
|
||||
self.reloadItems(at: paths)
|
||||
}
|
||||
|
||||
public func insertSections(sections: [Int], animationStyle: UITableViewRowAnimation) {
|
||||
public func insertSections(_ sections: [Int], animationStyle: UITableViewRowAnimation) {
|
||||
self.insertSections(indexSet(sections))
|
||||
}
|
||||
|
||||
public func deleteSections(sections: [Int], animationStyle: UITableViewRowAnimation) {
|
||||
public func deleteSections(_ sections: [Int], animationStyle: UITableViewRowAnimation) {
|
||||
self.deleteSections(indexSet(sections))
|
||||
}
|
||||
|
||||
public func moveSection(from: Int, to: Int) {
|
||||
public func moveSection(_ from: Int, to: Int) {
|
||||
self.moveSection(from, toSection: to)
|
||||
}
|
||||
|
||||
public func reloadSections(sections: [Int], animationStyle: UITableViewRowAnimation) {
|
||||
public func reloadSections(_ sections: [Int], animationStyle: UITableViewRowAnimation) {
|
||||
self.reloadSections(indexSet(sections))
|
||||
}
|
||||
|
||||
public func performBatchUpdates<S: SectionModelType>(changes: Changeset<S>, animationConfiguration: AnimationConfiguration) {
|
||||
public func performBatchUpdates<S: SectionModelType>(_ changes: Changeset<S>, animationConfiguration: AnimationConfiguration) {
|
||||
self.performBatchUpdates({ () -> Void in
|
||||
_performBatchUpdates(self, changes: changes, animationConfiguration: animationConfiguration)
|
||||
}, completion: { (completed: Bool) -> Void in
|
||||
|
|
@ -100,20 +100,20 @@ extension UICollectionView : SectionedViewType {
|
|||
}
|
||||
|
||||
public protocol SectionedViewType {
|
||||
func insertItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation)
|
||||
func deleteItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation)
|
||||
func moveItemAtIndexPath(from: NSIndexPath, to: NSIndexPath)
|
||||
func reloadItemsAtIndexPaths(paths: [NSIndexPath], animationStyle: UITableViewRowAnimation)
|
||||
func insertItemsAtIndexPaths(_ paths: [IndexPath], animationStyle: UITableViewRowAnimation)
|
||||
func deleteItemsAtIndexPaths(_ paths: [IndexPath], animationStyle: UITableViewRowAnimation)
|
||||
func moveItemAtIndexPath(_ from: IndexPath, to: IndexPath)
|
||||
func reloadItemsAtIndexPaths(_ paths: [IndexPath], animationStyle: UITableViewRowAnimation)
|
||||
|
||||
func insertSections(sections: [Int], animationStyle: UITableViewRowAnimation)
|
||||
func deleteSections(sections: [Int], animationStyle: UITableViewRowAnimation)
|
||||
func moveSection(from: Int, to: Int)
|
||||
func reloadSections(sections: [Int], animationStyle: UITableViewRowAnimation)
|
||||
func insertSections(_ sections: [Int], animationStyle: UITableViewRowAnimation)
|
||||
func deleteSections(_ sections: [Int], animationStyle: UITableViewRowAnimation)
|
||||
func moveSection(_ from: Int, to: Int)
|
||||
func reloadSections(_ sections: [Int], animationStyle: UITableViewRowAnimation)
|
||||
|
||||
func performBatchUpdates<S>(changes: Changeset<S>, animationConfiguration: AnimationConfiguration)
|
||||
func performBatchUpdates<S>(_ changes: Changeset<S>, animationConfiguration: AnimationConfiguration)
|
||||
}
|
||||
|
||||
func _performBatchUpdates<V: SectionedViewType, S: SectionModelType>(view: V, changes: Changeset<S>, animationConfiguration:AnimationConfiguration) {
|
||||
func _performBatchUpdates<V: SectionedViewType, S: SectionModelType>(_ view: V, changes: Changeset<S>, animationConfiguration:AnimationConfiguration) {
|
||||
typealias I = S.Item
|
||||
|
||||
view.deleteSections(changes.deletedSections, animationStyle: animationConfiguration.deleteAnimation)
|
||||
|
|
@ -126,22 +126,22 @@ func _performBatchUpdates<V: SectionedViewType, S: SectionModelType>(view: V, ch
|
|||
}
|
||||
|
||||
view.deleteItemsAtIndexPaths(
|
||||
changes.deletedItems.map { NSIndexPath(forItem: $0.itemIndex, inSection: $0.sectionIndex) },
|
||||
changes.deletedItems.map { IndexPath(item: $0.itemIndex, section: $0.sectionIndex) },
|
||||
animationStyle: animationConfiguration.deleteAnimation
|
||||
)
|
||||
view.insertItemsAtIndexPaths(
|
||||
changes.insertedItems.map { NSIndexPath(forItem: $0.itemIndex, inSection: $0.sectionIndex) },
|
||||
changes.insertedItems.map { IndexPath(item: $0.itemIndex, section: $0.sectionIndex) },
|
||||
animationStyle: animationConfiguration.insertAnimation
|
||||
)
|
||||
view.reloadItemsAtIndexPaths(
|
||||
changes.updatedItems.map { NSIndexPath(forItem: $0.itemIndex, inSection: $0.sectionIndex) },
|
||||
changes.updatedItems.map { IndexPath(item: $0.itemIndex, section: $0.sectionIndex) },
|
||||
animationStyle: animationConfiguration.reloadAnimation
|
||||
)
|
||||
|
||||
for (from, to) in changes.movedItems {
|
||||
view.moveItemAtIndexPath(
|
||||
NSIndexPath(forItem: from.itemIndex, inSection: from.sectionIndex),
|
||||
to: NSIndexPath(forItem: to.itemIndex, inSection: to.sectionIndex)
|
||||
IndexPath(item: from.itemIndex, section: from.sectionIndex),
|
||||
to: IndexPath(item: to.itemIndex, section: to.sectionIndex)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1912,6 +1912,7 @@
|
|||
TargetAttributes = {
|
||||
C83366DC1AD0293800C668A7 = {
|
||||
CreatedOnToolsVersion = 6.2;
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
C849EF601C3190360048AC4A = {
|
||||
CreatedOnToolsVersion = 7.2;
|
||||
|
|
@ -2729,6 +2730,7 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = "io.rx.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "RxExample-iOS";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
|
@ -2743,6 +2745,7 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = "io.rx.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "RxExample-iOS";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
|
@ -2873,6 +2876,7 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = "io.rx.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "RxExample-iOS";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = "Release-Tests";
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,23 +19,23 @@ import Foundation
|
|||
|
||||
let MB = 1024 * 1024
|
||||
|
||||
func exampleError(error: String, location: String = "\(#file):\(#line)") -> NSError {
|
||||
func exampleError(_ error: String, location: String = "\(#file):\(#line)") -> NSError {
|
||||
return NSError(domain: "ExampleError", code: -1, userInfo: [NSLocalizedDescriptionKey: "\(location): \(error)"])
|
||||
}
|
||||
|
||||
extension String {
|
||||
func toFloat() -> Float? {
|
||||
let numberFormatter = NSNumberFormatter()
|
||||
return numberFormatter.numberFromString(self)?.floatValue
|
||||
let numberFormatter = NumberFormatter()
|
||||
return numberFormatter.number(from: self)?.floatValue
|
||||
}
|
||||
|
||||
func toDouble() -> Double? {
|
||||
let numberFormatter = NSNumberFormatter()
|
||||
return numberFormatter.numberFromString(self)?.doubleValue
|
||||
let numberFormatter = NumberFormatter()
|
||||
return numberFormatter.number(from: self)?.doubleValue
|
||||
}
|
||||
}
|
||||
|
||||
func showAlert(message: String) {
|
||||
func showAlert(_ message: String) {
|
||||
#if os(iOS)
|
||||
UIAlertView(title: "RxExample", message: message, delegate: nil, cancelButtonTitle: "OK").show()
|
||||
#elseif os(OSX)
|
||||
|
|
@ -43,4 +43,4 @@ func showAlert(message: String) {
|
|||
alert.messageText = message
|
||||
alert.runModal()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ class APIWrappersViewController: ViewController {
|
|||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
datePicker.date = NSDate(timeIntervalSince1970: 0)
|
||||
datePicker.date = Date(timeIntervalSince1970: 0)
|
||||
|
||||
// MARK: UIBarButtonItem
|
||||
|
||||
|
|
@ -125,7 +125,7 @@ class APIWrappersViewController: ViewController {
|
|||
// MARK: UIDatePicker
|
||||
|
||||
// also test two way binding
|
||||
let dateValue = Variable(NSDate(timeIntervalSince1970: 0))
|
||||
let dateValue = Variable(Date(timeIntervalSince1970: 0))
|
||||
datePicker.rx_date <-> dateValue
|
||||
|
||||
|
||||
|
|
@ -199,7 +199,7 @@ class APIWrappersViewController: ViewController {
|
|||
|
||||
}
|
||||
|
||||
func debug(string: String) {
|
||||
func debug(_ string: String) {
|
||||
print(string)
|
||||
debugLabel.text = string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@
|
|||
import Foundation
|
||||
|
||||
enum Action {
|
||||
case Clear
|
||||
case ChangeSign
|
||||
case Percent
|
||||
case Operation(Operator)
|
||||
case Equal
|
||||
case AddNumber(Character)
|
||||
case AddDot
|
||||
}
|
||||
case clear
|
||||
case changeSign
|
||||
case percent
|
||||
case operation(Operator)
|
||||
case equal
|
||||
case addNumber(Character)
|
||||
case addDot
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
import Foundation
|
||||
|
||||
struct CalculatorState {
|
||||
static let CLEAR_STATE = CalculatorState(previousNumber: nil, action: .Clear, currentNumber: "0", inScreen: "0", replace: true)
|
||||
static let CLEAR_STATE = CalculatorState(previousNumber: nil, action: .clear, currentNumber: "0", inScreen: "0", replace: true)
|
||||
|
||||
let previousNumber: String!
|
||||
let action: Action
|
||||
|
|
@ -19,64 +19,64 @@ struct CalculatorState {
|
|||
}
|
||||
|
||||
extension CalculatorState {
|
||||
func tranformState(x: Action) -> CalculatorState {
|
||||
func tranformState(_ x: Action) -> CalculatorState {
|
||||
switch x {
|
||||
case .Clear:
|
||||
case .clear:
|
||||
return CalculatorState.CLEAR_STATE
|
||||
case .AddNumber(let c):
|
||||
case .addNumber(let c):
|
||||
return addNumber(c)
|
||||
case .AddDot:
|
||||
case .addDot:
|
||||
return self.addDot()
|
||||
case .ChangeSign:
|
||||
case .changeSign:
|
||||
let d = "\(-Double(self.inScreen)!)"
|
||||
return CalculatorState(previousNumber: previousNumber, action: action, currentNumber: d, inScreen: d, replace: true)
|
||||
case .Percent:
|
||||
case .percent:
|
||||
let d = "\(Double(self.inScreen)!/100)"
|
||||
return CalculatorState(previousNumber: previousNumber, action: action, currentNumber: d, inScreen: d, replace: true)
|
||||
case .Operation(let o):
|
||||
case .operation(let o):
|
||||
return performOperation(o)
|
||||
case .Equal:
|
||||
case .equal:
|
||||
return performEqual()
|
||||
}
|
||||
}
|
||||
|
||||
func addNumber(char: Character) -> CalculatorState {
|
||||
func addNumber(_ char: Character) -> CalculatorState {
|
||||
let cn = currentNumber == nil || replace ? String(char) : inScreen + String(char)
|
||||
return CalculatorState(previousNumber: previousNumber, action: action, currentNumber: cn, inScreen: cn, replace: false)
|
||||
}
|
||||
|
||||
func addDot() -> CalculatorState {
|
||||
let cn = inScreen.rangeOfString(".") == nil ? currentNumber + "." : currentNumber
|
||||
return CalculatorState(previousNumber: previousNumber, action: action, currentNumber: cn, inScreen: cn, replace: false)
|
||||
let cn = inScreen.range(of: ".") == nil ? currentNumber + "." : currentNumber
|
||||
return CalculatorState(previousNumber: previousNumber, action: action, currentNumber: cn, inScreen: cn!, replace: false)
|
||||
}
|
||||
|
||||
func performOperation(o: Operator) -> CalculatorState {
|
||||
func performOperation(_ o: Operator) -> CalculatorState {
|
||||
|
||||
if previousNumber == nil {
|
||||
return CalculatorState(previousNumber: currentNumber, action: .Operation(o), currentNumber: nil, inScreen: currentNumber, replace: true)
|
||||
return CalculatorState(previousNumber: currentNumber, action: .operation(o), currentNumber: nil, inScreen: currentNumber, replace: true)
|
||||
}
|
||||
else {
|
||||
let previous = Double(previousNumber)!
|
||||
let current = Double(inScreen)!
|
||||
|
||||
switch action {
|
||||
case .Operation(let op):
|
||||
case .operation(let op):
|
||||
switch op {
|
||||
case .Addition:
|
||||
case .addition:
|
||||
let result = "\(previous + current)"
|
||||
return CalculatorState(previousNumber: result, action: .Operation(o), currentNumber: nil, inScreen: result, replace: true)
|
||||
case .Subtraction:
|
||||
return CalculatorState(previousNumber: result, action: .operation(o), currentNumber: nil, inScreen: result, replace: true)
|
||||
case .subtraction:
|
||||
let result = "\(previous - current)"
|
||||
return CalculatorState(previousNumber: result, action: .Operation(o), currentNumber: nil, inScreen: result, replace: true)
|
||||
case .Multiplication:
|
||||
return CalculatorState(previousNumber: result, action: .operation(o), currentNumber: nil, inScreen: result, replace: true)
|
||||
case .multiplication:
|
||||
let result = "\(previous * current)"
|
||||
return CalculatorState(previousNumber: result, action: .Operation(o), currentNumber: nil, inScreen: result, replace: true)
|
||||
case .Division:
|
||||
return CalculatorState(previousNumber: result, action: .operation(o), currentNumber: nil, inScreen: result, replace: true)
|
||||
case .division:
|
||||
let result = "\(previous / current)"
|
||||
return CalculatorState(previousNumber: result, action: .Operation(o), currentNumber: nil, inScreen: result, replace: true)
|
||||
return CalculatorState(previousNumber: result, action: .operation(o), currentNumber: nil, inScreen: result, replace: true)
|
||||
}
|
||||
default:
|
||||
return CalculatorState(previousNumber: nil, action: .Operation(o), currentNumber: currentNumber, inScreen: inScreen, replace: true)
|
||||
return CalculatorState(previousNumber: nil, action: .operation(o), currentNumber: currentNumber, inScreen: inScreen, replace: true)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -88,25 +88,25 @@ extension CalculatorState {
|
|||
let current = Double(inScreen)!
|
||||
|
||||
switch action {
|
||||
case .Operation(let op):
|
||||
case .operation(let op):
|
||||
switch op {
|
||||
case .Addition:
|
||||
case .addition:
|
||||
let result = "\(previous! + current)"
|
||||
return CalculatorState(previousNumber: nil, action: .Clear, currentNumber: result, inScreen: result, replace: true)
|
||||
case .Subtraction:
|
||||
return CalculatorState(previousNumber: nil, action: .clear, currentNumber: result, inScreen: result, replace: true)
|
||||
case .subtraction:
|
||||
let result = "\(previous! - current)"
|
||||
return CalculatorState(previousNumber: nil, action: .Clear, currentNumber: result, inScreen: result, replace: true)
|
||||
case .Multiplication:
|
||||
return CalculatorState(previousNumber: nil, action: .clear, currentNumber: result, inScreen: result, replace: true)
|
||||
case .multiplication:
|
||||
let result = "\(previous! * current)"
|
||||
return CalculatorState(previousNumber: nil, action: .Clear, currentNumber: result, inScreen: result, replace: true)
|
||||
case .Division:
|
||||
return CalculatorState(previousNumber: nil, action: .clear, currentNumber: result, inScreen: result, replace: true)
|
||||
case .division:
|
||||
let result = previous! / current
|
||||
let resultText = result == Double.infinity ? "0" : "\(result)"
|
||||
return CalculatorState(previousNumber: nil, action: .Clear, currentNumber: resultText, inScreen: resultText, replace: true)
|
||||
return CalculatorState(previousNumber: nil, action: .clear, currentNumber: resultText, inScreen: resultText, replace: true)
|
||||
}
|
||||
default:
|
||||
return CalculatorState(previousNumber: nil, action: .Clear, currentNumber: currentNumber, inScreen: inScreen, replace: true)
|
||||
return CalculatorState(previousNumber: nil, action: .clear, currentNumber: currentNumber, inScreen: inScreen, replace: true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,30 +43,30 @@ class CalculatorViewController: ViewController {
|
|||
|
||||
override func viewDidLoad() {
|
||||
let commands:[Observable<Action>] = [
|
||||
allClearButton.rx_tap.map { _ in .Clear },
|
||||
allClearButton.rx_tap.map { _ in .clear },
|
||||
|
||||
changeSignButton.rx_tap.map { _ in .ChangeSign },
|
||||
percentButton.rx_tap.map { _ in .Percent },
|
||||
changeSignButton.rx_tap.map { _ in .changeSign },
|
||||
percentButton.rx_tap.map { _ in .percent },
|
||||
|
||||
divideButton.rx_tap.map { _ in .Operation(.Division) },
|
||||
multiplyButton.rx_tap.map { _ in .Operation(.Multiplication) },
|
||||
minusButton.rx_tap.map { _ in .Operation(.Subtraction) },
|
||||
plusButton.rx_tap.map { _ in .Operation(.Addition) },
|
||||
divideButton.rx_tap.map { _ in .operation(.division) },
|
||||
multiplyButton.rx_tap.map { _ in .operation(.multiplication) },
|
||||
minusButton.rx_tap.map { _ in .operation(.subtraction) },
|
||||
plusButton.rx_tap.map { _ in .operation(.addition) },
|
||||
|
||||
equalButton.rx_tap.map { _ in .Equal },
|
||||
equalButton.rx_tap.map { _ in .equal },
|
||||
|
||||
dotButton.rx_tap.map { _ in .AddDot },
|
||||
dotButton.rx_tap.map { _ in .addDot },
|
||||
|
||||
zeroButton.rx_tap.map { _ in .AddNumber("0") },
|
||||
oneButton.rx_tap.map { _ in .AddNumber("1") },
|
||||
twoButton.rx_tap.map { _ in .AddNumber("2") },
|
||||
threeButton.rx_tap.map { _ in .AddNumber("3") },
|
||||
fourButton.rx_tap.map { _ in .AddNumber("4") },
|
||||
fiveButton.rx_tap.map { _ in .AddNumber("5") },
|
||||
sixButton.rx_tap.map { _ in .AddNumber("6") },
|
||||
sevenButton.rx_tap.map { _ in .AddNumber("7") },
|
||||
eightButton.rx_tap.map { _ in .AddNumber("8") },
|
||||
nineButton.rx_tap.map { _ in .AddNumber("9") }
|
||||
zeroButton.rx_tap.map { _ in .addNumber("0") },
|
||||
oneButton.rx_tap.map { _ in .addNumber("1") },
|
||||
twoButton.rx_tap.map { _ in .addNumber("2") },
|
||||
threeButton.rx_tap.map { _ in .addNumber("3") },
|
||||
fourButton.rx_tap.map { _ in .addNumber("4") },
|
||||
fiveButton.rx_tap.map { _ in .addNumber("5") },
|
||||
sixButton.rx_tap.map { _ in .addNumber("6") },
|
||||
sevenButton.rx_tap.map { _ in .addNumber("7") },
|
||||
eightButton.rx_tap.map { _ in .addNumber("8") },
|
||||
nineButton.rx_tap.map { _ in .addNumber("9") }
|
||||
]
|
||||
|
||||
commands
|
||||
|
|
@ -77,17 +77,17 @@ class CalculatorViewController: ViewController {
|
|||
}
|
||||
.debug("debugging")
|
||||
.subscribeNext { [weak self] calState in
|
||||
self?.resultLabel.text = self?.prettyFormat(calState.inScreen)
|
||||
self?.resultLabel.text = calState.inScreen
|
||||
switch calState.action {
|
||||
case .Operation(let operation):
|
||||
case .operation(let operation):
|
||||
switch operation {
|
||||
case .Addition:
|
||||
case .addition:
|
||||
self?.lastSignLabel.text = "+"
|
||||
case .Subtraction:
|
||||
case .subtraction:
|
||||
self?.lastSignLabel.text = "-"
|
||||
case .Multiplication:
|
||||
case .multiplication:
|
||||
self?.lastSignLabel.text = "x"
|
||||
case .Division:
|
||||
case .division:
|
||||
self?.lastSignLabel.text = "/"
|
||||
}
|
||||
default:
|
||||
|
|
@ -97,10 +97,11 @@ class CalculatorViewController: ViewController {
|
|||
.addDisposableTo(disposeBag)
|
||||
}
|
||||
|
||||
//swifts string api sucks
|
||||
|
||||
func prettyFormat(str: String) -> String {
|
||||
if str.hasSuffix(".0") {
|
||||
return str.substringToIndex(str.endIndex.predecessor().predecessor())
|
||||
// return str[str.startIndex..<str.endIndex.pre]
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
import Foundation
|
||||
|
||||
enum Operator {
|
||||
case Addition
|
||||
case Subtraction
|
||||
case Multiplication
|
||||
case Division
|
||||
}
|
||||
case addition
|
||||
case subtraction
|
||||
case multiplication
|
||||
case division
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class Dependencies {
|
|||
// *****************************************************************************************
|
||||
static let sharedDependencies = Dependencies() // Singleton
|
||||
|
||||
let URLSession = NSURLSession.sharedSession()
|
||||
let URLSession = Foundation.URLSession.shared()
|
||||
let backgroundWorkScheduler: ImmediateSchedulerType
|
||||
let mainScheduler: SerialDispatchQueueScheduler
|
||||
let wireframe: Wireframe
|
||||
|
|
@ -28,10 +28,10 @@ class Dependencies {
|
|||
private init() {
|
||||
wireframe = DefaultWireframe()
|
||||
|
||||
let operationQueue = NSOperationQueue()
|
||||
let operationQueue = OperationQueue()
|
||||
operationQueue.maxConcurrentOperationCount = 2
|
||||
#if !RX_NO_MODULE
|
||||
operationQueue.qualityOfService = NSQualityOfService.UserInitiated
|
||||
operationQueue.qualityOfService = QualityOfService.userInitiated
|
||||
#endif
|
||||
backgroundWorkScheduler = OperationQueueScheduler(operationQueue: operationQueue)
|
||||
|
||||
|
|
|
|||
|
|
@ -25,12 +25,12 @@ private extension UIView {
|
|||
var rx_driveAuthorization: AnyObserver<Bool> {
|
||||
return UIBindingObserver(UIElement: self) { view, authorized in
|
||||
if authorized {
|
||||
view.hidden = true
|
||||
view.superview?.sendSubviewToBack(view)
|
||||
view.isHidden = true
|
||||
view.superview?.sendSubview(toBack:view)
|
||||
}
|
||||
else {
|
||||
view.hidden = false
|
||||
view.superview?.bringSubviewToFront(view)
|
||||
view.isHidden = false
|
||||
view.superview?.bringSubview(toFront:view)
|
||||
}
|
||||
}.asObserver()
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ class GeolocationViewController: ViewController {
|
|||
}
|
||||
|
||||
private func openAppPreferences() {
|
||||
UIApplication.sharedApplication().openURL(NSURL(string: UIApplicationOpenSettingsURLString)!)
|
||||
UIApplication.shared().openURL(URL(string: UIApplicationOpenSettingsURLString)!)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ extension Repository {
|
|||
ServiceState state.
|
||||
*/
|
||||
enum ServiceState {
|
||||
case Online
|
||||
case Offline
|
||||
case online
|
||||
case offline
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -45,14 +45,14 @@ enum SearchRepositoryResponse {
|
|||
/**
|
||||
New repositories just fetched
|
||||
*/
|
||||
case Repositories(repositories: [Repository], nextURL: NSURL?)
|
||||
case repositories(repositories: [Repository], nextURL: URL?)
|
||||
|
||||
/**
|
||||
In case there was some problem fetching data from service, this will be returned.
|
||||
It really doesn't matter if that is a failure in network layer, parsing error or something else.
|
||||
In case data can't be read and parsed properly, something is wrong with server response.
|
||||
*/
|
||||
case ServiceOffline
|
||||
case serviceOffline
|
||||
|
||||
/**
|
||||
This example uses unauthenticated GitHub API. That API does have throttling policy and you won't
|
||||
|
|
@ -62,7 +62,7 @@ enum SearchRepositoryResponse {
|
|||
|
||||
Just search like mad, and everything will be handled right.
|
||||
*/
|
||||
case LimitExceeded
|
||||
case limitExceeded
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -117,15 +117,15 @@ extension GitHubSearchRepositoriesAPI {
|
|||
/**
|
||||
Public fascade for search.
|
||||
*/
|
||||
func search(query: String, loadNextPageTrigger: Observable<Void>) -> Observable<RepositoriesState> {
|
||||
func search(_ query: String, loadNextPageTrigger: Observable<Void>) -> Observable<RepositoriesState> {
|
||||
let escapedQuery = query.URLEscaped
|
||||
let url = NSURL(string: "https://api.github.com/search/repositories?q=\(escapedQuery)")!
|
||||
let url = URL(string: "https://api.github.com/search/repositories?q=\(escapedQuery)")!
|
||||
return recursivelySearch([], loadNextURL: url, loadNextPageTrigger: loadNextPageTrigger)
|
||||
// Here we go again
|
||||
.startWith(RepositoriesState.empty)
|
||||
}
|
||||
|
||||
private func recursivelySearch(loadedSoFar: [Repository], loadNextURL: NSURL, loadNextPageTrigger: Observable<Void>) -> Observable<RepositoriesState> {
|
||||
private func recursivelySearch(_ loadedSoFar: [Repository], loadNextURL: URL, loadNextPageTrigger: Observable<Void>) -> Observable<RepositoriesState> {
|
||||
return loadSearchURL(loadNextURL).flatMap { searchResponse -> Observable<RepositoriesState> in
|
||||
switch searchResponse {
|
||||
/**
|
||||
|
|
@ -133,18 +133,18 @@ extension GitHubSearchRepositoriesAPI {
|
|||
It will retry until either battery drains, you become angry and close the app or evil machine comes back
|
||||
from the future, steals your device and Googles Sarah Connor's address.
|
||||
*/
|
||||
case .ServiceOffline:
|
||||
return Observable.just(RepositoriesState(repositories: loadedSoFar, serviceState: .Offline, limitExceeded: false))
|
||||
case .serviceOffline:
|
||||
return Observable.just(RepositoriesState(repositories: loadedSoFar, serviceState: .offline, limitExceeded: false))
|
||||
|
||||
case .LimitExceeded:
|
||||
return Observable.just(RepositoriesState(repositories: loadedSoFar, serviceState: .Online, limitExceeded: true))
|
||||
case .limitExceeded:
|
||||
return Observable.just(RepositoriesState(repositories: loadedSoFar, serviceState: .online, limitExceeded: true))
|
||||
|
||||
case let .Repositories(newPageRepositories, maybeNextURL):
|
||||
case let .repositories(newPageRepositories, maybeNextURL):
|
||||
|
||||
var loadedRepositories = loadedSoFar
|
||||
loadedRepositories.appendContentsOf(newPageRepositories)
|
||||
loadedRepositories.append(contentsOf: newPageRepositories)
|
||||
|
||||
let appenedRepositories = RepositoriesState(repositories: loadedRepositories, serviceState: .Online, limitExceeded: false)
|
||||
let appenedRepositories = RepositoriesState(repositories: loadedRepositories, serviceState: .online, limitExceeded: false)
|
||||
|
||||
// if next page can't be loaded, just return what was loaded, and stop
|
||||
guard let nextURL = maybeNextURL else {
|
||||
|
|
@ -163,15 +163,15 @@ extension GitHubSearchRepositoriesAPI {
|
|||
}
|
||||
}
|
||||
|
||||
private func loadSearchURL(searchURL: NSURL) -> Observable<SearchRepositoryResponse> {
|
||||
return NSURLSession.sharedSession()
|
||||
.rx_response(NSURLRequest(URL: searchURL))
|
||||
private func loadSearchURL(_ searchURL: URL) -> Observable<SearchRepositoryResponse> {
|
||||
return URLSession.shared()
|
||||
.rx_response(URLRequest(url: searchURL))
|
||||
.retry(3)
|
||||
.trackActivity(self.activityIndicator)
|
||||
.observeOn(Dependencies.sharedDependencies.backgroundWorkScheduler)
|
||||
.map { data, httpResponse -> SearchRepositoryResponse in
|
||||
if httpResponse.statusCode == 403 {
|
||||
return .LimitExceeded
|
||||
return .limitExceeded
|
||||
}
|
||||
|
||||
let jsonRoot = try GitHubSearchRepositoriesAPI.parseJSON(httpResponse, data: data)
|
||||
|
|
@ -184,9 +184,9 @@ extension GitHubSearchRepositoriesAPI {
|
|||
|
||||
let nextURL = try GitHubSearchRepositoriesAPI.parseNextURL(httpResponse)
|
||||
|
||||
return .Repositories(repositories: repositories, nextURL: nextURL)
|
||||
return .repositories(repositories: repositories, nextURL: nextURL)
|
||||
}
|
||||
.retryOnBecomesReachable(.ServiceOffline, reachabilityService: _reachabilityService)
|
||||
.retryOnBecomesReachable(.serviceOffline, reachabilityService: _reachabilityService)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -195,22 +195,22 @@ extension GitHubSearchRepositoriesAPI {
|
|||
extension GitHubSearchRepositoriesAPI {
|
||||
|
||||
private static let parseLinksPattern = "\\s*,?\\s*<([^\\>]*)>\\s*;\\s*rel=\"([^\"]*)\""
|
||||
private static let linksRegex = try! NSRegularExpression(pattern: parseLinksPattern, options: [.AllowCommentsAndWhitespace])
|
||||
private static let linksRegex = try! RegularExpression(pattern: parseLinksPattern, options: [.allowCommentsAndWhitespace])
|
||||
|
||||
private static func parseLinks(links: String) throws -> [String: String] {
|
||||
private static func parseLinks(_ links: String) throws -> [String: String] {
|
||||
|
||||
let length = (links as NSString).length
|
||||
let matches = GitHubSearchRepositoriesAPI.linksRegex.matchesInString(links, options: NSMatchingOptions(), range: NSRange(location: 0, length: length))
|
||||
let matches = GitHubSearchRepositoriesAPI.linksRegex.matches(in: links, options: RegularExpression.MatchingOptions(), range: NSRange(location: 0, length: length))
|
||||
|
||||
var result: [String: String] = [:]
|
||||
|
||||
for m in matches {
|
||||
let matches = (1 ..< m.numberOfRanges).map { rangeIndex -> String in
|
||||
let range = m.rangeAtIndex(rangeIndex)
|
||||
let startIndex = links.startIndex.advancedBy(range.location)
|
||||
let endIndex = startIndex.advancedBy(range.length)
|
||||
let range = m.range(at: rangeIndex)
|
||||
let startIndex = links.characters.index(links.startIndex, offsetBy: range.location)
|
||||
let endIndex = links.characters.index(links.startIndex, offsetBy: range.location + range.length)
|
||||
let stringRange = startIndex ..< endIndex
|
||||
return links.substringWithRange(stringRange)
|
||||
return links.substring(with: stringRange)
|
||||
}
|
||||
|
||||
if matches.count != 2 {
|
||||
|
|
@ -223,7 +223,7 @@ extension GitHubSearchRepositoriesAPI {
|
|||
return result
|
||||
}
|
||||
|
||||
private static func parseNextURL(httpResponse: NSHTTPURLResponse) throws -> NSURL? {
|
||||
private static func parseNextURL(_ httpResponse: HTTPURLResponse) throws -> URL? {
|
||||
guard let serializedLinks = httpResponse.allHeaderFields["Link"] as? String else {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -234,22 +234,22 @@ extension GitHubSearchRepositoriesAPI {
|
|||
return nil
|
||||
}
|
||||
|
||||
guard let nextUrl = NSURL(string: nextPageURL) else {
|
||||
guard let nextUrl = URL(string: nextPageURL) else {
|
||||
throw exampleError("Error parsing next url `\(nextPageURL)`")
|
||||
}
|
||||
|
||||
return nextUrl
|
||||
}
|
||||
|
||||
private static func parseJSON(httpResponse: NSHTTPURLResponse, data: NSData) throws -> AnyObject {
|
||||
private static func parseJSON(_ httpResponse: HTTPURLResponse, data: Data) throws -> AnyObject {
|
||||
if !(200 ..< 300 ~= httpResponse.statusCode) {
|
||||
throw exampleError("Call failed")
|
||||
}
|
||||
|
||||
return try NSJSONSerialization.JSONObjectWithData(data ?? NSData(), options: [])
|
||||
return try JSONSerialization.jsonObject(with: data ?? Data(), options: [])
|
||||
}
|
||||
|
||||
private static func parseRepositories(json: [String: AnyObject]) throws -> [Repository] {
|
||||
private static func parseRepositories(_ json: [String: AnyObject]) throws -> [Repository] {
|
||||
guard let items = json["items"] as? [[String: AnyObject]] else {
|
||||
throw exampleError("Can't find items")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,16 @@ import RxSwift
|
|||
import RxCocoa
|
||||
#endif
|
||||
|
||||
extension UIScrollView {
|
||||
func isNearBottomEdge(edgeOffset: CGFloat = 20.0) -> Bool {
|
||||
return self.contentOffset.y + self.frame.size.height + edgeOffset > self.contentSize.height
|
||||
}
|
||||
}
|
||||
|
||||
class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegate {
|
||||
static let startLoadingOffset: CGFloat = 20.0
|
||||
|
||||
static func isNearTheBottomEdge(contentOffset: CGPoint, _ tableView: UITableView) -> Bool {
|
||||
static func isNearTheBottomEdge(_ contentOffset: CGPoint, _ tableView: UITableView) -> Bool {
|
||||
return contentOffset.y + tableView.frame.size.height + startLoadingOffset > tableView.contentSize.height
|
||||
}
|
||||
|
||||
|
|
@ -27,11 +33,10 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat
|
|||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
let tableView = self.tableView
|
||||
let searchBar = self.searchBar
|
||||
|
||||
|
||||
dataSource.configureCell = { (_, tv, ip, repository: Repository) in
|
||||
let cell = tv.dequeueReusableCellWithIdentifier("Cell")!
|
||||
let cell = tv.dequeueReusableCell(withIdentifier: "Cell")!
|
||||
cell.textLabel?.text = repository.name
|
||||
cell.detailTextLabel?.text = repository.url
|
||||
return cell
|
||||
|
|
@ -43,14 +48,14 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat
|
|||
}
|
||||
|
||||
|
||||
let loadNextPageTrigger = tableView.rx_contentOffset
|
||||
.flatMap { offset in
|
||||
GitHubSearchRepositoriesViewController.isNearTheBottomEdge(offset, tableView)
|
||||
? Observable.just()
|
||||
let loadNextPageTrigger = self.tableView.rx_contentOffset
|
||||
.flatMap { _ in
|
||||
self.tableView.isNearBottomEdge(edgeOffset: 20.0)
|
||||
? Observable.just(())
|
||||
: Observable.empty()
|
||||
}
|
||||
|
||||
let searchResult = searchBar.rx_text.asDriver()
|
||||
let searchResult = self.searchBar.rx_text.asDriver()
|
||||
.throttle(0.3)
|
||||
.distinctUntilChanged()
|
||||
.flatMapLatest { query -> Driver<RepositoriesState> in
|
||||
|
|
@ -82,8 +87,8 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat
|
|||
// dismiss keyboard on scroll
|
||||
tableView.rx_contentOffset
|
||||
.subscribe { _ in
|
||||
if searchBar.isFirstResponder() {
|
||||
_ = searchBar.resignFirstResponder()
|
||||
if self.searchBar.isFirstResponder() {
|
||||
_ = self.searchBar.resignFirstResponder()
|
||||
}
|
||||
}
|
||||
.addDisposableTo(disposeBag)
|
||||
|
|
@ -95,14 +100,14 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat
|
|||
// activity indicator in status bar
|
||||
// {
|
||||
GitHubSearchRepositoriesAPI.sharedAPI.activityIndicator
|
||||
.drive(UIApplication.sharedApplication().rx_networkActivityIndicatorVisible)
|
||||
.drive(UIApplication.shared().rx_networkActivityIndicatorVisible)
|
||||
.addDisposableTo(disposeBag)
|
||||
// }
|
||||
}
|
||||
|
||||
// MARK: Table view delegate
|
||||
|
||||
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
return 30
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ extension UINavigationController {
|
|||
return UIBindingObserver(UIElement: self) { navigationController, maybeServiceState in
|
||||
// if nil is being bound, then don't change color, it's not perfect, but :)
|
||||
if let serviceState = maybeServiceState {
|
||||
let isOffline = serviceState ?? .Online == .Offline
|
||||
let safeState = (serviceState ?? .online)
|
||||
let isOffline = safeState == .offline
|
||||
|
||||
self.navigationBar.backgroundColor = isOffline
|
||||
? Colors.OfflineColor
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@ import RxCocoa
|
|||
extension ValidationResult: CustomStringConvertible {
|
||||
var description: String {
|
||||
switch self {
|
||||
case let .OK(message):
|
||||
case let .ok(message):
|
||||
return message
|
||||
case .Empty:
|
||||
case .empty:
|
||||
return ""
|
||||
case .Validating:
|
||||
case .validating:
|
||||
return "validating ..."
|
||||
case let .Failed(message):
|
||||
case let .failed(message):
|
||||
return message
|
||||
}
|
||||
}
|
||||
|
|
@ -30,19 +30,19 @@ extension ValidationResult: CustomStringConvertible {
|
|||
|
||||
struct ValidationColors {
|
||||
static let okColor = UIColor(red: 138.0 / 255.0, green: 221.0 / 255.0, blue: 109.0 / 255.0, alpha: 1.0)
|
||||
static let errorColor = UIColor.redColor()
|
||||
static let errorColor = UIColor.red()
|
||||
}
|
||||
|
||||
extension ValidationResult {
|
||||
var textColor: UIColor {
|
||||
switch self {
|
||||
case .OK:
|
||||
case .ok:
|
||||
return ValidationColors.okColor
|
||||
case .Empty:
|
||||
return UIColor.blackColor()
|
||||
case .Validating:
|
||||
return UIColor.blackColor()
|
||||
case .Failed:
|
||||
case .empty:
|
||||
return UIColor.black()
|
||||
case .validating:
|
||||
return UIColor.black()
|
||||
case .failed:
|
||||
return ValidationColors.errorColor
|
||||
}
|
||||
}
|
||||
|
|
@ -55,4 +55,4 @@ extension UILabel {
|
|||
label.text = result.description
|
||||
}.asObserver()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,75 +25,76 @@ class GitHubDefaultValidationService: GitHubValidationService {
|
|||
|
||||
let minPasswordCount = 5
|
||||
|
||||
func validateUsername(username: String) -> Observable<ValidationResult> {
|
||||
func validateUsername(_ username: String) -> Observable<ValidationResult> {
|
||||
if username.characters.count == 0 {
|
||||
return Observable.just(.Empty)
|
||||
return Observable.just(.empty)
|
||||
}
|
||||
|
||||
|
||||
// this obviously won't be
|
||||
if username.rangeOfCharacterFromSet(NSCharacterSet.alphanumericCharacterSet().invertedSet) != nil {
|
||||
return Observable.just(.Failed(message: "Username can only contain numbers or digits"))
|
||||
if username.rangeOfCharacter(from: CharacterSet.alphanumerics.inverted) != nil {
|
||||
return Observable.just(.failed(message: "Username can only contain numbers or digits"))
|
||||
}
|
||||
|
||||
let loadingValue = ValidationResult.Validating
|
||||
let loadingValue = ValidationResult.validating
|
||||
|
||||
return API
|
||||
.usernameAvailable(username)
|
||||
.map { available in
|
||||
if available {
|
||||
return .OK(message: "Username available")
|
||||
return .ok(message: "Username available")
|
||||
}
|
||||
else {
|
||||
return .Failed(message: "Username already taken")
|
||||
return .failed(message: "Username already taken")
|
||||
}
|
||||
}
|
||||
.startWith(loadingValue)
|
||||
}
|
||||
|
||||
func validatePassword(password: String) -> ValidationResult {
|
||||
func validatePassword(_ password: String) -> ValidationResult {
|
||||
let numberOfCharacters = password.characters.count
|
||||
if numberOfCharacters == 0 {
|
||||
return .Empty
|
||||
return .empty
|
||||
}
|
||||
|
||||
if numberOfCharacters < minPasswordCount {
|
||||
return .Failed(message: "Password must be at least \(minPasswordCount) characters")
|
||||
return .failed(message: "Password must be at least \(minPasswordCount) characters")
|
||||
}
|
||||
|
||||
return .OK(message: "Password acceptable")
|
||||
return .ok(message: "Password acceptable")
|
||||
}
|
||||
|
||||
func validateRepeatedPassword(password: String, repeatedPassword: String) -> ValidationResult {
|
||||
func validateRepeatedPassword(_ password: String, repeatedPassword: String) -> ValidationResult {
|
||||
if repeatedPassword.characters.count == 0 {
|
||||
return .Empty
|
||||
return .empty
|
||||
}
|
||||
|
||||
if repeatedPassword == password {
|
||||
return .OK(message: "Password repeated")
|
||||
return .ok(message: "Password repeated")
|
||||
}
|
||||
else {
|
||||
return .Failed(message: "Password different")
|
||||
return .failed(message: "Password different")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class GitHubDefaultAPI : GitHubAPI {
|
||||
let URLSession: NSURLSession
|
||||
let URLSession: Foundation.URLSession
|
||||
|
||||
static let sharedAPI = GitHubDefaultAPI(
|
||||
URLSession: NSURLSession.sharedSession()
|
||||
URLSession: Foundation.URLSession.shared()
|
||||
)
|
||||
|
||||
init(URLSession: NSURLSession) {
|
||||
init(URLSession: Foundation.URLSession) {
|
||||
self.URLSession = URLSession
|
||||
}
|
||||
|
||||
func usernameAvailable(username: String) -> Observable<Bool> {
|
||||
func usernameAvailable(_ username: String) -> Observable<Bool> {
|
||||
// this is ofc just mock, but good enough
|
||||
|
||||
let URL = NSURL(string: "https://github.com/\(username.URLEscaped)")!
|
||||
let request = NSURLRequest(URL: URL)
|
||||
let url = URL(string: "https://github.com/\(username.URLEscaped)")!
|
||||
let request = URLRequest(url: url)
|
||||
return self.URLSession.rx_response(request)
|
||||
.map { (maybeData, response) in
|
||||
return response.statusCode == 404
|
||||
|
|
@ -101,7 +102,7 @@ class GitHubDefaultAPI : GitHubAPI {
|
|||
.catchErrorJustReturn(false)
|
||||
}
|
||||
|
||||
func signup(username: String, password: String) -> Observable<Bool> {
|
||||
func signup(_ username: String, password: String) -> Observable<Bool> {
|
||||
// this is also just a mock
|
||||
let signupResult = arc4random() % 5 == 0 ? false : true
|
||||
return Observable.just(signupResult)
|
||||
|
|
@ -109,4 +110,4 @@ class GitHubDefaultAPI : GitHubAPI {
|
|||
.throttle(0.4, scheduler: MainScheduler.instance)
|
||||
.take(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,31 +13,31 @@ import RxCocoa
|
|||
#endif
|
||||
|
||||
enum ValidationResult {
|
||||
case OK(message: String)
|
||||
case Empty
|
||||
case Validating
|
||||
case Failed(message: String)
|
||||
case ok(message: String)
|
||||
case empty
|
||||
case validating
|
||||
case failed(message: String)
|
||||
}
|
||||
|
||||
enum SignupState {
|
||||
case SignedUp(signedUp: Bool)
|
||||
case signedUp(signedUp: Bool)
|
||||
}
|
||||
|
||||
protocol GitHubAPI {
|
||||
func usernameAvailable(username: String) -> Observable<Bool>
|
||||
func signup(username: String, password: String) -> Observable<Bool>
|
||||
func usernameAvailable(_ username: String) -> Observable<Bool>
|
||||
func signup(_ username: String, password: String) -> Observable<Bool>
|
||||
}
|
||||
|
||||
protocol GitHubValidationService {
|
||||
func validateUsername(username: String) -> Observable<ValidationResult>
|
||||
func validatePassword(password: String) -> ValidationResult
|
||||
func validateRepeatedPassword(password: String, repeatedPassword: String) -> ValidationResult
|
||||
func validateUsername(_ username: String) -> Observable<ValidationResult>
|
||||
func validatePassword(_ password: String) -> ValidationResult
|
||||
func validateRepeatedPassword(_ password: String, repeatedPassword: String) -> ValidationResult
|
||||
}
|
||||
|
||||
extension ValidationResult {
|
||||
var isValid: Bool {
|
||||
switch self {
|
||||
case .OK:
|
||||
case .ok:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class GitHubSignupViewController2 : ViewController {
|
|||
// bind results to {
|
||||
viewModel.signupEnabled
|
||||
.driveNext { [weak self] valid in
|
||||
self?.signupOutlet.enabled = valid
|
||||
self?.signupOutlet.isEnabled = valid
|
||||
self?.signupOutlet.alpha = valid ? 1.0 : 0.5
|
||||
}
|
||||
.addDisposableTo(disposeBag)
|
||||
|
|
@ -91,13 +91,13 @@ class GitHubSignupViewController2 : ViewController {
|
|||
// This will work well with UINavigationController, but has an assumption that view controller will
|
||||
// never be added as a child view controller. If we didn't recreate the dispose bag here,
|
||||
// then our resources would never be properly released.
|
||||
override func willMoveToParentViewController(parent: UIViewController?) {
|
||||
override func willMove(toParentViewController parent: UIViewController?) {
|
||||
if let parent = parent {
|
||||
assert(parent.isKindOfClass(UINavigationController), "Please read comments")
|
||||
assert(parent as? UINavigationController != nil, "Please read comments")
|
||||
}
|
||||
else {
|
||||
self.disposeBag = DisposeBag()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ class GithubSignupViewModel2 {
|
|||
validatedUsername = input.username
|
||||
.flatMapLatest { username in
|
||||
return validationService.validateUsername(username)
|
||||
.asDriver(onErrorJustReturn: .Failed(message: "Error contacting server"))
|
||||
.asDriver(onErrorJustReturn: .failed(message: "Error contacting server"))
|
||||
}
|
||||
|
||||
validatedPassword = input.password
|
||||
|
|
@ -123,4 +123,4 @@ class GithubSignupViewModel2 {
|
|||
}
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class GitHubSignupViewController1 : ViewController {
|
|||
// bind results to {
|
||||
viewModel.signupEnabled
|
||||
.subscribeNext { [weak self] valid in
|
||||
self?.signupOutlet.enabled = valid
|
||||
self?.signupOutlet.isEnabled = valid
|
||||
self?.signupOutlet.alpha = valid ? 1.0 : 0.5
|
||||
}
|
||||
.addDisposableTo(disposeBag)
|
||||
|
|
@ -91,13 +91,15 @@ class GitHubSignupViewController1 : ViewController {
|
|||
// This will work well with UINavigationController, but has an assumption that view controller will
|
||||
// never be added as a child view controller. If we didn't recreate the dispose bag here,
|
||||
// then our resources would never be properly released.
|
||||
override func willMoveToParentViewController(parent: UIViewController?) {
|
||||
override func willMove(toParentViewController parent: UIViewController?) {
|
||||
if let parent = parent {
|
||||
assert(parent.isKindOfClass(UINavigationController), "Please read comments")
|
||||
if parent as? UINavigationController == nil {
|
||||
assert(false, "something")
|
||||
}
|
||||
}
|
||||
else {
|
||||
self.disposeBag = DisposeBag()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ class GithubSignupViewModel1 {
|
|||
.flatMapLatest { username in
|
||||
return validationService.validateUsername(username)
|
||||
.observeOn(MainScheduler.instance)
|
||||
.catchErrorJustReturn(.Failed(message: "Error contacting server"))
|
||||
.catchErrorJustReturn(.failed(message: "Error contacting server"))
|
||||
}
|
||||
.shareReplay(1)
|
||||
|
||||
|
|
@ -118,4 +118,4 @@ class GithubSignupViewModel1 {
|
|||
.distinctUntilChanged()
|
||||
.shareReplay(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,12 +24,12 @@ class ImagePickerController: ViewController {
|
|||
super.viewDidLoad()
|
||||
|
||||
|
||||
cameraButton.enabled = UIImagePickerController.isSourceTypeAvailable(.Camera)
|
||||
cameraButton.isEnabled = UIImagePickerController.isSourceTypeAvailable(.camera)
|
||||
|
||||
cameraButton.rx_tap
|
||||
.flatMapLatest { [weak self] _ in
|
||||
return UIImagePickerController.rx_createWithParent(self) { picker in
|
||||
picker.sourceType = .Camera
|
||||
picker.sourceType = .camera
|
||||
picker.allowsEditing = false
|
||||
}
|
||||
.flatMap { $0.rx_didFinishPickingMediaWithInfo }
|
||||
|
|
@ -44,7 +44,7 @@ class ImagePickerController: ViewController {
|
|||
galleryButton.rx_tap
|
||||
.flatMapLatest { [weak self] _ in
|
||||
return UIImagePickerController.rx_createWithParent(self) { picker in
|
||||
picker.sourceType = .PhotoLibrary
|
||||
picker.sourceType = .photoLibrary
|
||||
picker.allowsEditing = false
|
||||
}
|
||||
.flatMap {
|
||||
|
|
@ -61,7 +61,7 @@ class ImagePickerController: ViewController {
|
|||
cropButton.rx_tap
|
||||
.flatMapLatest { [weak self] _ in
|
||||
return UIImagePickerController.rx_createWithParent(self) { picker in
|
||||
picker.sourceType = .PhotoLibrary
|
||||
picker.sourceType = .photoLibrary
|
||||
picker.allowsEditing = true
|
||||
}
|
||||
.flatMap { $0.rx_didFinishPickingMediaWithInfo }
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ import UIKit
|
|||
import RxCocoa
|
||||
#endif
|
||||
|
||||
func dismissViewController(viewController: UIViewController, animated: Bool) {
|
||||
func dismissViewController(_ viewController: UIViewController, animated: Bool) {
|
||||
if viewController.isBeingDismissed() || viewController.isBeingPresented() {
|
||||
dispatch_async(dispatch_get_main_queue()) {
|
||||
DispatchQueue.main.async {
|
||||
dismissViewController(viewController, animated: animated)
|
||||
}
|
||||
|
||||
|
|
@ -23,12 +23,12 @@ func dismissViewController(viewController: UIViewController, animated: Bool) {
|
|||
}
|
||||
|
||||
if viewController.presentingViewController != nil {
|
||||
viewController.dismissViewControllerAnimated(animated, completion: nil)
|
||||
viewController.dismiss(animated: animated, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIImagePickerController {
|
||||
static func rx_createWithParent(parent: UIViewController?, animated: Bool = true, configureImagePicker: (UIImagePickerController) throws -> () = { x in }) -> Observable<UIImagePickerController> {
|
||||
static func rx_createWithParent(_ parent: UIViewController?, animated: Bool = true, configureImagePicker: (UIImagePickerController) throws -> () = { x in }) -> Observable<UIImagePickerController> {
|
||||
return Observable.create { [weak parent] observer in
|
||||
let imagePicker = UIImagePickerController()
|
||||
let dismissDisposable = imagePicker
|
||||
|
|
@ -44,21 +44,21 @@ extension UIImagePickerController {
|
|||
try configureImagePicker(imagePicker)
|
||||
}
|
||||
catch let error {
|
||||
observer.on(.Error(error))
|
||||
observer.on(.error(error))
|
||||
return NopDisposable.instance
|
||||
}
|
||||
|
||||
guard let parent = parent else {
|
||||
observer.on(.Completed)
|
||||
observer.on(.completed)
|
||||
return NopDisposable.instance
|
||||
}
|
||||
|
||||
parent.presentViewController(imagePicker, animated: animated, completion: nil)
|
||||
observer.on(.Next(imagePicker))
|
||||
parent.present(imagePicker, animated: animated, completion: nil)
|
||||
observer.on(.next(imagePicker))
|
||||
|
||||
return CompositeDisposable(dismissDisposable, AnonymousDisposable {
|
||||
dismissViewController(imagePicker, animated: animated)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@ class SimpleTableViewExampleSectionedViewController
|
|||
])
|
||||
|
||||
dataSource.configureCell = { (_, tv, indexPath, element) in
|
||||
let cell = tv.dequeueReusableCellWithIdentifier("Cell")!
|
||||
cell.textLabel?.text = "\(element) @ row \(indexPath.row)"
|
||||
let cell = tv.dequeueReusableCell(withIdentifier: "Cell")!
|
||||
cell.textLabel?.text = "\(element) @ row \((indexPath as NSIndexPath).row)"
|
||||
return cell
|
||||
}
|
||||
|
||||
|
|
@ -68,9 +68,9 @@ class SimpleTableViewExampleSectionedViewController
|
|||
.addDisposableTo(disposeBag)
|
||||
}
|
||||
|
||||
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
let label = UILabel(frame: CGRect.zero)
|
||||
label.text = dataSource.sectionAtIndex(section).model ?? ""
|
||||
return label
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class PartialUpdatesViewController : ViewController {
|
|||
@IBOutlet weak var partialUpdatesTableViewOutlet: UITableView!
|
||||
@IBOutlet weak var partialUpdatesCollectionViewOutlet: UICollectionView!
|
||||
|
||||
var timer: NSTimer? = nil
|
||||
var timer: Timer? = nil
|
||||
|
||||
static let initialValue: [AnimatableSectionModel<String, Int>] = [
|
||||
NumberSection(model: "section 1", items: [1, 2, 3]),
|
||||
|
|
@ -123,22 +123,22 @@ class PartialUpdatesViewController : ViewController {
|
|||
|
||||
partialUpdatesCollectionViewOutlet.rx_itemSelected
|
||||
.subscribeNext { [weak self] i in
|
||||
print("Let me guess, it's .... It's \(self?.generator.sections[i.section].items[i.item]), isn't it? Yeah, I've got it.")
|
||||
print("Let me guess, it's .... It's \(self?.generator.sections[i.section].items[i.item!]), isn't it? Yeah, I've got it.")
|
||||
}
|
||||
.addDisposableTo(disposeBag)
|
||||
|
||||
Observable.of(partialUpdatesTableViewOutlet.rx_itemSelected, reloadTableViewOutlet.rx_itemSelected)
|
||||
.merge()
|
||||
.subscribeNext { [weak self] i in
|
||||
print("I have a feeling it's .... \(self?.generator.sections[i.section].items[i.item])?")
|
||||
print("I have a feeling it's .... \(self?.generator.sections[i.section].items[i.item!])?")
|
||||
}
|
||||
.addDisposableTo(disposeBag)
|
||||
}
|
||||
|
||||
func skinTableViewDataSource(dataSource: RxTableViewSectionedDataSource<NumberSection>) {
|
||||
func skinTableViewDataSource(_ dataSource: RxTableViewSectionedDataSource<NumberSection>) {
|
||||
dataSource.configureCell = { (_, tv, ip, i) in
|
||||
let cell = tv.dequeueReusableCellWithIdentifier("Cell")
|
||||
?? UITableViewCell(style:.Default, reuseIdentifier: "Cell")
|
||||
let cell = tv.dequeueReusableCell(withIdentifier: "Cell")
|
||||
?? UITableViewCell(style:.default, reuseIdentifier: "Cell")
|
||||
|
||||
cell.textLabel!.text = "\(i)"
|
||||
|
||||
|
|
@ -150,9 +150,9 @@ class PartialUpdatesViewController : ViewController {
|
|||
}
|
||||
}
|
||||
|
||||
func skinCollectionViewDataSource(dataSource: CollectionViewSectionedDataSource<NumberSection>) {
|
||||
func skinCollectionViewDataSource(_ dataSource: CollectionViewSectionedDataSource<NumberSection>) {
|
||||
dataSource.cellFactory = { (_, cv, ip, i) in
|
||||
let cell = cv.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: ip) as! NumberCell
|
||||
let cell = cv.dequeueReusableCell(withReuseIdentifier: "Cell", for: ip as IndexPath) as! NumberCell
|
||||
|
||||
cell.value!.text = "\(i)"
|
||||
|
||||
|
|
@ -160,15 +160,15 @@ class PartialUpdatesViewController : ViewController {
|
|||
}
|
||||
|
||||
dataSource.supplementaryViewFactory = { (dataSource, cv, kind, ip) in
|
||||
let section = cv.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "Section", forIndexPath: ip) as! NumberSectionView
|
||||
let section = cv.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "Section", for: ip as IndexPath) as! NumberSectionView
|
||||
|
||||
section.value!.text = "\(dataSource.sectionAtIndex(ip.section).model)"
|
||||
section.value!.text = "\(dataSource.sectionAtIndex((ip as NSIndexPath).section).model)"
|
||||
|
||||
return section
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillDisappear(animated: Bool) {
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
self.timer?.invalidate()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,10 +25,10 @@ class DetailViewController: ViewController {
|
|||
|
||||
imageView.makeRoundedCorners(5)
|
||||
|
||||
let url = NSURL(string: user.imageURL)!
|
||||
let request = NSURLRequest(URL: url)
|
||||
let url = URL(string: user.imageURL)!
|
||||
let request = URLRequest(url: url)
|
||||
|
||||
NSURLSession.sharedSession().rx_data(request)
|
||||
URLSession.shared().rx_data(request)
|
||||
.map { data in
|
||||
UIImage(data: data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ class RandomUserAPI {
|
|||
private init() {}
|
||||
|
||||
func getExampleUserResultSet() -> Observable<[User]> {
|
||||
let url = NSURL(string: "http://api.randomuser.me/?results=20")!
|
||||
return NSURLSession.sharedSession().rx_JSON(url)
|
||||
let url = URL(string: "http://api.randomuser.me/?results=20")!
|
||||
return URLSession.shared().rx_JSON(url)
|
||||
.map { json in
|
||||
guard let json = json as? [String: AnyObject] else {
|
||||
throw exampleError("Casting to dictionary failed")
|
||||
|
|
@ -29,7 +29,7 @@ class RandomUserAPI {
|
|||
}
|
||||
}
|
||||
|
||||
private func parseJSON(json: [String: AnyObject]) throws -> [User] {
|
||||
private func parseJSON(_ json: [String: AnyObject]) throws -> [User] {
|
||||
guard let results = json["results"] as? [[String: AnyObject]] else {
|
||||
throw exampleError("Can't find results")
|
||||
}
|
||||
|
|
@ -47,8 +47,8 @@ class RandomUserAPI {
|
|||
}
|
||||
|
||||
let returnUser = User(
|
||||
firstName: firstName.capitalizedString,
|
||||
lastName: lastName.capitalizedString,
|
||||
firstName: firstName.capitalized,
|
||||
lastName: lastName.capitalized,
|
||||
imageURL: imageURL
|
||||
)
|
||||
return returnUser
|
||||
|
|
@ -56,4 +56,4 @@ class RandomUserAPI {
|
|||
|
||||
return searchResults
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,21 +25,21 @@ struct TableViewEditingCommandsViewModel {
|
|||
let favoriteUsers: [User]
|
||||
let users: [User]
|
||||
|
||||
func executeCommand(command: TableViewEditingCommand) -> TableViewEditingCommandsViewModel {
|
||||
func executeCommand(_ command: TableViewEditingCommand) -> TableViewEditingCommandsViewModel {
|
||||
switch command {
|
||||
case let .SetUsers(users):
|
||||
case let .setUsers(users):
|
||||
return TableViewEditingCommandsViewModel(favoriteUsers: favoriteUsers, users: users)
|
||||
case let .SetFavoriteUsers(favoriteUsers):
|
||||
case let .setFavoriteUsers(favoriteUsers):
|
||||
return TableViewEditingCommandsViewModel(favoriteUsers: favoriteUsers, users: users)
|
||||
case let .DeleteUser(indexPath):
|
||||
case let .deleteUser(indexPath):
|
||||
var all = [self.favoriteUsers, self.users]
|
||||
all[indexPath.section].removeAtIndex(indexPath.row)
|
||||
all[(indexPath as NSIndexPath).section].remove(at: (indexPath as NSIndexPath).row)
|
||||
return TableViewEditingCommandsViewModel(favoriteUsers: all[0], users: all[1])
|
||||
case let .MoveUser(from, to):
|
||||
case let .moveUser(from, to):
|
||||
var all = [self.favoriteUsers, self.users]
|
||||
let user = all[from.section][from.row]
|
||||
all[from.section].removeAtIndex(from.row)
|
||||
all[to.section].insert(user, atIndex: to.row)
|
||||
let user = all[(from as NSIndexPath).section][(from as NSIndexPath).row]
|
||||
all[(from as NSIndexPath).section].remove(at: (from as NSIndexPath).row)
|
||||
all[(to as NSIndexPath).section].insert(user, at: (to as NSIndexPath).row)
|
||||
|
||||
return TableViewEditingCommandsViewModel(favoriteUsers: all[0], users: all[1])
|
||||
}
|
||||
|
|
@ -47,10 +47,10 @@ struct TableViewEditingCommandsViewModel {
|
|||
}
|
||||
|
||||
enum TableViewEditingCommand {
|
||||
case SetUsers(users: [User])
|
||||
case SetFavoriteUsers(favoriteUsers: [User])
|
||||
case DeleteUser(indexPath: NSIndexPath)
|
||||
case MoveUser(from: NSIndexPath, to: NSIndexPath)
|
||||
case setUsers(users: [User])
|
||||
case setFavoriteUsers(favoriteUsers: [User])
|
||||
case deleteUser(indexPath: IndexPath)
|
||||
case moveUser(from: IndexPath, to: IndexPath)
|
||||
}
|
||||
|
||||
class TableViewWithEditingCommandsViewController: ViewController, UITableViewDelegate {
|
||||
|
|
@ -77,14 +77,14 @@ class TableViewWithEditingCommandsViewController: ViewController, UITableViewDel
|
|||
|
||||
let loadFavoriteUsers = RandomUserAPI.sharedAPI
|
||||
.getExampleUserResultSet()
|
||||
.map(TableViewEditingCommand.SetUsers)
|
||||
.map(TableViewEditingCommand.setUsers)
|
||||
|
||||
let initialLoadCommand = Observable.just(TableViewEditingCommand.SetFavoriteUsers(favoriteUsers: [superMan, watMan]))
|
||||
let initialLoadCommand = Observable.just(TableViewEditingCommand.setFavoriteUsers(favoriteUsers: [superMan, watMan]))
|
||||
.concat(loadFavoriteUsers)
|
||||
.observeOn(MainScheduler.instance)
|
||||
|
||||
let deleteUserCommand = tableView.rx_itemDeleted.map(TableViewEditingCommand.DeleteUser)
|
||||
let moveUserCommand = tableView.rx_itemMoved.map(TableViewEditingCommand.MoveUser)
|
||||
let deleteUserCommand = tableView.rx_itemDeleted.map(TableViewEditingCommand.deleteUser)
|
||||
let moveUserCommand = tableView.rx_itemMoved.map(TableViewEditingCommand.moveUser)
|
||||
|
||||
let initialState = TableViewEditingCommandsViewModel(favoriteUsers: [], users: [])
|
||||
|
||||
|
|
@ -119,35 +119,35 @@ class TableViewWithEditingCommandsViewController: ViewController, UITableViewDel
|
|||
.addDisposableTo(disposeBag)
|
||||
}
|
||||
|
||||
override func setEditing(editing: Bool, animated: Bool) {
|
||||
override func setEditing(_ editing: Bool, animated: Bool) {
|
||||
super.setEditing(editing, animated: animated)
|
||||
tableView.editing = editing
|
||||
tableView.isEditing = editing
|
||||
}
|
||||
|
||||
// MARK: Table view delegate ;)
|
||||
|
||||
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
let title = dataSource.sectionAtIndex(section)
|
||||
|
||||
let label = UILabel(frame: CGRect.zero)
|
||||
// hacky I know :)
|
||||
label.text = " \(title)"
|
||||
label.textColor = UIColor.whiteColor()
|
||||
label.backgroundColor = UIColor.darkGrayColor()
|
||||
label.textColor = UIColor.white()
|
||||
label.backgroundColor = UIColor.darkGray()
|
||||
label.alpha = 0.9
|
||||
|
||||
return label
|
||||
}
|
||||
|
||||
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
return 40
|
||||
}
|
||||
|
||||
// MARK: Navigation
|
||||
|
||||
private func showDetailsForUser(user: User) {
|
||||
let storyboard = UIStoryboard(name: "Main", bundle: NSBundle(identifier: "RxExample-iOS"))
|
||||
let viewController = storyboard.instantiateViewControllerWithIdentifier("DetailViewController") as! DetailViewController
|
||||
private func showDetailsForUser(_ user: User) {
|
||||
let storyboard = UIStoryboard(name: "Main", bundle: Bundle(identifier: "RxExample-iOS"))
|
||||
let viewController = storyboard.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
|
||||
viewController.user = user
|
||||
self.navigationController?.pushViewController(viewController, animated: true)
|
||||
}
|
||||
|
|
@ -158,7 +158,7 @@ class TableViewWithEditingCommandsViewController: ViewController, UITableViewDel
|
|||
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, User>>()
|
||||
|
||||
dataSource.configureCell = { (_, tv, ip, user: User) in
|
||||
let cell = tv.dequeueReusableCellWithIdentifier("Cell")!
|
||||
let cell = tv.dequeueReusableCell(withIdentifier: "Cell")!
|
||||
cell.textLabel?.text = user.firstName + " " + user.lastName
|
||||
return cell
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ import UIKit
|
|||
|
||||
extension UIImageView {
|
||||
|
||||
func makeRoundedCorners(radius: CGFloat) {
|
||||
func makeRoundedCorners(_ radius: CGFloat) {
|
||||
self.layer.cornerRadius = self.frame.size.width / 2
|
||||
self.layer.borderColor = UIColor.darkGrayColor().CGColor
|
||||
self.layer.borderColor = UIColor.darkGray().cgColor
|
||||
self.layer.borderWidth = radius
|
||||
self.layer.masksToBounds = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class SearchResultViewModel {
|
|||
let searchResult: WikipediaSearchResult
|
||||
|
||||
var title: Driver<String>
|
||||
var imageURLs: Driver<[NSURL]>
|
||||
var imageURLs: Driver<[URL]>
|
||||
|
||||
let API = DefaultWikipediaAPI.sharedAPI
|
||||
let $: Dependencies = Dependencies.sharedDependencies
|
||||
|
|
@ -35,10 +35,10 @@ class SearchResultViewModel {
|
|||
|
||||
// private methods
|
||||
|
||||
func configureTitle(imageURLs: Observable<[NSURL]>) -> Observable<String> {
|
||||
func configureTitle(_ imageURLs: Observable<[URL]>) -> Observable<String> {
|
||||
let searchResult = self.searchResult
|
||||
|
||||
let loadingValue: [NSURL]? = nil
|
||||
let loadingValue: [URL]? = nil
|
||||
|
||||
return imageURLs
|
||||
.map(Optional.init)
|
||||
|
|
@ -54,7 +54,7 @@ class SearchResultViewModel {
|
|||
.retryOnBecomesReachable("⚠️ Service offline ⚠️", reachabilityService: $.reachabilityService)
|
||||
}
|
||||
|
||||
func configureImageURLs() -> Observable<[NSURL]> {
|
||||
func configureImageURLs() -> Observable<[URL]> {
|
||||
let searchResult = self.searchResult
|
||||
return API.articleContent(searchResult)
|
||||
.observeOn($.backgroundWorkScheduler)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ public class CollectionViewImageCell: UICollectionViewCell {
|
|||
let disposeBag = DisposeBag()
|
||||
|
||||
self.downloadableImage?
|
||||
.asDriver(onErrorJustReturn: DownloadableImage.OfflinePlaceholder)
|
||||
.asDriver(onErrorJustReturn: DownloadableImage.offlinePlaceholder)
|
||||
.drive(imageOutlet.rxex_downloadableImageAnimated(kCATransitionFade))
|
||||
.addDisposableTo(disposeBag)
|
||||
|
||||
|
|
@ -39,4 +39,4 @@ public class CollectionViewImageCell: UICollectionViewCell {
|
|||
|
||||
deinit {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public class WikipediaSearchCell: UITableViewCell {
|
|||
public override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
self.imagesOutlet.registerNib(UINib(nibName: "WikipediaImageCell", bundle: nil), forCellWithReuseIdentifier: "ImageCell")
|
||||
self.imagesOutlet.register(UINib(nibName: "WikipediaImageCell", bundle: nil), forCellWithReuseIdentifier: "ImageCell")
|
||||
}
|
||||
|
||||
var viewModel: SearchResultViewModel! {
|
||||
|
|
@ -42,8 +42,8 @@ public class WikipediaSearchCell: UITableViewCell {
|
|||
|
||||
let reachabilityService = Dependencies.sharedDependencies.reachabilityService
|
||||
viewModel.imageURLs
|
||||
.drive(self.imagesOutlet.rx_itemsWithCellIdentifier("ImageCell", cellType: CollectionViewImageCell.self)) { [weak self] (_, URL, cell) in
|
||||
cell.downloadableImage = self?.imageService.imageFromURL(URL, reachabilityService: reachabilityService) ?? Observable.empty()
|
||||
.drive(self.imagesOutlet.rx_itemsWithCellIdentifier("ImageCell", cellType: CollectionViewImageCell.self)) { [weak self] (_, url, cell) in
|
||||
cell.downloadableImage = self?.imageService.imageFromURL(url, reachabilityService: reachabilityService) ?? Observable.empty()
|
||||
}
|
||||
.addDisposableTo(disposeBag)
|
||||
|
||||
|
|
|
|||
|
|
@ -41,11 +41,11 @@ class WikipediaSearchViewController: ViewController {
|
|||
let searchBar = self.searchBar
|
||||
let searchBarContainer = self.searchBarContainer
|
||||
|
||||
searchBarContainer.addSubview(searchBar)
|
||||
searchBar.frame = searchBarContainer.bounds
|
||||
searchBar.autoresizingMask = .FlexibleWidth
|
||||
searchBarContainer?.addSubview(searchBar)
|
||||
searchBar.frame = (searchBarContainer?.bounds)!
|
||||
searchBar.autoresizingMask = .flexibleWidth
|
||||
|
||||
resultsViewController.edgesForExtendedLayout = UIRectEdge.None
|
||||
resultsViewController.edgesForExtendedLayout = UIRectEdge()
|
||||
|
||||
configureTableDataSource()
|
||||
configureKeyboardDismissesOnScroll()
|
||||
|
|
@ -54,7 +54,7 @@ class WikipediaSearchViewController: ViewController {
|
|||
}
|
||||
|
||||
func configureTableDataSource() {
|
||||
resultsTableView.registerNib(UINib(nibName: "WikipediaSearchCell", bundle: nil), forCellReuseIdentifier: "WikipediaSearchCell")
|
||||
resultsTableView.register(UINib(nibName: "WikipediaSearchCell", bundle: nil), forCellReuseIdentifier: "WikipediaSearchCell")
|
||||
|
||||
resultsTableView.rowHeight = 194
|
||||
|
||||
|
|
@ -118,7 +118,7 @@ class WikipediaSearchViewController: ViewController {
|
|||
DefaultImageService.sharedImageService.loadingImage
|
||||
) { $0 || $1 }
|
||||
.distinctUntilChanged()
|
||||
.drive(UIApplication.sharedApplication().rx_networkActivityIndicatorVisible)
|
||||
.drive(UIApplication.shared().rx_networkActivityIndicatorVisible)
|
||||
.addDisposableTo(disposeBag)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,15 +12,15 @@ import RxSwift
|
|||
import RxCocoa
|
||||
#endif
|
||||
|
||||
func apiError(error: String) -> NSError {
|
||||
func apiError(_ error: String) -> NSError {
|
||||
return NSError(domain: "WikipediaAPI", code: -1, userInfo: [NSLocalizedDescriptionKey: error])
|
||||
}
|
||||
|
||||
public let WikipediaParseError = apiError("Error during parsing")
|
||||
|
||||
protocol WikipediaAPI {
|
||||
func getSearchResults(query: String) -> Observable<[WikipediaSearchResult]>
|
||||
func articleContent(searchResult: WikipediaSearchResult) -> Observable<WikipediaPage>
|
||||
func getSearchResults(_ query: String) -> Observable<[WikipediaSearchResult]>
|
||||
func articleContent(_ searchResult: WikipediaSearchResult) -> Observable<WikipediaPage>
|
||||
}
|
||||
|
||||
class DefaultWikipediaAPI: WikipediaAPI {
|
||||
|
|
@ -33,17 +33,17 @@ class DefaultWikipediaAPI: WikipediaAPI {
|
|||
|
||||
private init() {}
|
||||
|
||||
private func rx_JSON(URL: NSURL) -> Observable<AnyObject> {
|
||||
private func rx_JSON(_ url: URL) -> Observable<AnyObject> {
|
||||
return $.URLSession
|
||||
.rx_JSON(URL)
|
||||
.rx_JSON(url)
|
||||
.trackActivity(loadingWikipediaData)
|
||||
}
|
||||
|
||||
// Example wikipedia response http://en.wikipedia.org/w/api.php?action=opensearch&search=Rx
|
||||
func getSearchResults(query: String) -> Observable<[WikipediaSearchResult]> {
|
||||
func getSearchResults(_ query: String) -> Observable<[WikipediaSearchResult]> {
|
||||
let escapedQuery = query.URLEscaped
|
||||
let urlContent = "http://en.wikipedia.org/w/api.php?action=opensearch&search=\(escapedQuery)"
|
||||
let url = NSURL(string: urlContent)!
|
||||
let url = URL(string: urlContent)!
|
||||
|
||||
return rx_JSON(url)
|
||||
.observeOn($.backgroundWorkScheduler)
|
||||
|
|
@ -58,9 +58,9 @@ class DefaultWikipediaAPI: WikipediaAPI {
|
|||
}
|
||||
|
||||
// http://en.wikipedia.org/w/api.php?action=parse&page=rx&format=json
|
||||
func articleContent(searchResult: WikipediaSearchResult) -> Observable<WikipediaPage> {
|
||||
func articleContent(_ searchResult: WikipediaSearchResult) -> Observable<WikipediaPage> {
|
||||
let escapedPage = searchResult.title.URLEscaped
|
||||
guard let url = NSURL(string: "http://en.wikipedia.org/w/api.php?action=parse&page=\(escapedPage)&format=json") else {
|
||||
guard let url = URL(string: "http://en.wikipedia.org/w/api.php?action=parse&page=\(escapedPage)&format=json") else {
|
||||
return Observable.error(apiError("Can't create url"))
|
||||
}
|
||||
|
||||
|
|
@ -74,4 +74,4 @@ class DefaultWikipediaAPI: WikipediaAPI {
|
|||
}
|
||||
.observeOn($.mainScheduler)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,12 +21,12 @@ struct WikipediaPage {
|
|||
}
|
||||
|
||||
// tedious parsing part
|
||||
static func parseJSON(json: NSDictionary) throws -> WikipediaPage {
|
||||
guard let title = json.valueForKey("parse")?.valueForKey("title") as? String,
|
||||
let text = json.valueForKey("parse")?.valueForKey("text")?.valueForKey("*") as? String else {
|
||||
static func parseJSON(_ json: NSDictionary) throws -> WikipediaPage {
|
||||
guard let title = json.value(forKey: "parse")?.value(forKey: "title") as? String,
|
||||
let text = json.value(forKey: "parse")?.value(forKey: "text")?.value(forKey: "*") as? String else {
|
||||
throw apiError("Error parsing page content")
|
||||
}
|
||||
|
||||
return WikipediaPage(title: title, text: text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,16 +14,16 @@ import RxSwift
|
|||
struct WikipediaSearchResult: CustomDebugStringConvertible {
|
||||
let title: String
|
||||
let description: String
|
||||
let URL: NSURL
|
||||
let URL: Foundation.URL
|
||||
|
||||
init(title: String, description: String, URL: NSURL) {
|
||||
init(title: String, description: String, URL: Foundation.URL) {
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.URL = URL
|
||||
}
|
||||
|
||||
// tedious parsing part
|
||||
static func parseJSON(json: [AnyObject]) throws -> [WikipediaSearchResult] {
|
||||
static func parseJSON(_ json: [AnyObject]) throws -> [WikipediaSearchResult] {
|
||||
let rootArrayTyped = json.map { $0 as? [AnyObject] }
|
||||
.filter { $0 != nil }
|
||||
.map { $0! }
|
||||
|
|
@ -42,7 +42,7 @@ struct WikipediaSearchResult: CustomDebugStringConvertible {
|
|||
guard let titleString = title as? String,
|
||||
let descriptionString = description as? String,
|
||||
let urlString = url as? String,
|
||||
let URL = NSURL(string: urlString) else {
|
||||
let URL = Foundation.URL(string: urlString) else {
|
||||
throw WikipediaParseError
|
||||
}
|
||||
|
||||
|
|
@ -57,4 +57,4 @@ extension WikipediaSearchResult {
|
|||
var debugDescription: String {
|
||||
return "[\(title)](\(URL))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@ import UIKit
|
|||
infix operator <-> {
|
||||
}
|
||||
|
||||
func nonMarkedText(textInput: UITextInput) -> String? {
|
||||
func nonMarkedText(_ textInput: UITextInput) -> String? {
|
||||
let start = textInput.beginningOfDocument
|
||||
let end = textInput.endOfDocument
|
||||
|
||||
guard let rangeAll = textInput.textRangeFromPosition(start, toPosition: end),
|
||||
text = textInput.textInRange(rangeAll) else {
|
||||
guard let rangeAll = textInput.textRange(from: start, to: end),
|
||||
text = textInput.text(in: rangeAll) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -32,12 +32,12 @@ func nonMarkedText(textInput: UITextInput) -> String? {
|
|||
return text
|
||||
}
|
||||
|
||||
guard let startRange = textInput.textRangeFromPosition(start, toPosition: markedTextRange.start),
|
||||
endRange = textInput.textRangeFromPosition(markedTextRange.end, toPosition: end) else {
|
||||
guard let startRange = textInput.textRange(from: start, to: markedTextRange.start),
|
||||
endRange = textInput.textRange(from: markedTextRange.end, to: end) else {
|
||||
return text
|
||||
}
|
||||
|
||||
return (textInput.textInRange(startRange) ?? "") + (textInput.textInRange(endRange) ?? "")
|
||||
return (textInput.text(in: startRange) ?? "") + (textInput.text(in: endRange) ?? "")
|
||||
}
|
||||
|
||||
func <-> (textInput: RxTextInput, variable: Variable<String>) -> Disposable {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ When all activities complete `false` will be sent.
|
|||
public class ActivityIndicator : DriverConvertibleType {
|
||||
public typealias E = Bool
|
||||
|
||||
private let _lock = NSRecursiveLock()
|
||||
private let _lock = RecursiveLock()
|
||||
private let _variable = Variable(0)
|
||||
private let _loading: Driver<Bool>
|
||||
|
||||
|
|
@ -47,22 +47,15 @@ public class ActivityIndicator : DriverConvertibleType {
|
|||
_loading = _variable.asObservable()
|
||||
.map { $0 > 0 }
|
||||
.distinctUntilChanged()
|
||||
<<<<<<< 6b259b6618bc93a56405726866e1a103f72a49ad
|
||||
.asDriver(onErrorRecover: ActivityIndicator.ifItStillErrors)
|
||||
=======
|
||||
.asDriver { (error: ErrorProtocol) -> Driver<Bool> in
|
||||
_ = fatalError("Loader can't fail")
|
||||
return Driver.empty()
|
||||
}
|
||||
>>>>>>> Changes for Swift 3.0.
|
||||
}
|
||||
|
||||
private static func ifItStillErrors(error: ErrorType) -> Driver<Bool> {
|
||||
_ = fatalError("Loader can't fail")
|
||||
}
|
||||
|
||||
|
||||
private func trackActivityOfObservable<O: ObservableConvertibleType>(source: O) -> Observable<O.E> {
|
||||
private func trackActivityOfObservable<O: ObservableConvertibleType>(_ source: O) -> Observable<O.E> {
|
||||
return Observable.using({ () -> ActivityToken<O.E> in
|
||||
self.increment()
|
||||
return ActivityToken(source: source.asObservable(), disposeAction: self.decrement)
|
||||
|
|
@ -89,7 +82,7 @@ public class ActivityIndicator : DriverConvertibleType {
|
|||
}
|
||||
|
||||
public extension ObservableConvertibleType {
|
||||
public func trackActivity(activityIndicator: ActivityIndicator) -> Observable<E> {
|
||||
public func trackActivity(_ activityIndicator: ActivityIndicator) -> Observable<E> {
|
||||
return activityIndicator.trackActivityOfObservable(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import RxSwift
|
|||
#endif
|
||||
|
||||
enum DownloadableImage{
|
||||
case Content(image:Image)
|
||||
case OfflinePlaceholder
|
||||
case content(image:Image)
|
||||
case offlinePlaceholder
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,10 +35,10 @@ class GeolocationService {
|
|||
.rx_didChangeAuthorizationStatus
|
||||
.startWith(status)
|
||||
}
|
||||
.asDriver(onErrorJustReturn: CLAuthorizationStatus.NotDetermined)
|
||||
.asDriver(onErrorJustReturn: CLAuthorizationStatus.notDetermined)
|
||||
.map {
|
||||
switch $0 {
|
||||
case .AuthorizedAlways:
|
||||
case .authorizedAlways:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
|
@ -57,4 +57,4 @@ class GeolocationService {
|
|||
locationManager.startUpdatingLocation()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,29 +8,29 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
func parseImageURLsfromHTML(html: NSString) throws -> [NSURL] {
|
||||
let regularExpression = try NSRegularExpression(pattern: "<img[^>]*src=\"([^\"]+)\"[^>]*>", options: [])
|
||||
func parseImageURLsfromHTML(_ html: NSString) throws -> [URL] {
|
||||
let regularExpression = try RegularExpression(pattern: "<img[^>]*src=\"([^\"]+)\"[^>]*>", options: [])
|
||||
|
||||
let matches = regularExpression.matchesInString(html as String, options: [], range: NSMakeRange(0, html.length))
|
||||
let matches = regularExpression.matches(in: html as String, options: [], range: NSMakeRange(0, html.length))
|
||||
|
||||
return matches.map { match -> NSURL? in
|
||||
return matches.map { match -> URL? in
|
||||
if match.numberOfRanges != 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
let url = html.substringWithRange(match.rangeAtIndex(1))
|
||||
let url = html.substring(with: match.range(at: 1))
|
||||
|
||||
var absoluteURLString = url
|
||||
if url.hasPrefix("//") {
|
||||
absoluteURLString = "http:" + url
|
||||
}
|
||||
|
||||
return NSURL(string: absoluteURLString)
|
||||
return URL(string: absoluteURLString)
|
||||
}.filter { $0 != nil }.map { $0! }
|
||||
}
|
||||
|
||||
func parseImageURLsfromHTMLSuitableForDisplay(html: NSString) throws -> [NSURL] {
|
||||
func parseImageURLsfromHTMLSuitableForDisplay(_ html: NSString) throws -> [URL] {
|
||||
return try parseImageURLsfromHTML(html).filter {
|
||||
return $0.absoluteString.rangeOfString(".svg.") == nil
|
||||
return $0.absoluteString?.range(of: ".svg.") == nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import RxCocoa
|
|||
#endif
|
||||
|
||||
protocol ImageService {
|
||||
func imageFromURL(URL: NSURL, reachabilityService: ReachabilityService) -> Observable<DownloadableImage>
|
||||
func imageFromURL(_ url: URL, reachabilityService: ReachabilityService) -> Observable<DownloadableImage>
|
||||
}
|
||||
|
||||
class DefaultImageService: ImageService {
|
||||
|
|
@ -29,10 +29,10 @@ class DefaultImageService: ImageService {
|
|||
let $: Dependencies = Dependencies.sharedDependencies
|
||||
|
||||
// 1st level cache
|
||||
private let _imageCache = NSCache()
|
||||
private let _imageCache = Cache<AnyObject, AnyObject>()
|
||||
|
||||
// 2nd level cache
|
||||
private let _imageDataCache = NSCache()
|
||||
private let _imageDataCache = Cache<AnyObject, AnyObject>()
|
||||
|
||||
let loadingImage = ActivityIndicator()
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ class DefaultImageService: ImageService {
|
|||
_imageCache.countLimit = 20
|
||||
}
|
||||
|
||||
private func decodeImage(imageData: NSData) -> Observable<Image> {
|
||||
private func decodeImage(_ imageData: Data) -> Observable<Image> {
|
||||
return Observable.just(imageData)
|
||||
.observeOn($.backgroundWorkScheduler)
|
||||
.map { data in
|
||||
|
|
@ -55,9 +55,9 @@ class DefaultImageService: ImageService {
|
|||
}
|
||||
}
|
||||
|
||||
private func _imageFromURL(URL: NSURL) -> Observable<Image> {
|
||||
private func _imageFromURL(_ url: URL) -> Observable<Image> {
|
||||
return Observable.deferred {
|
||||
let maybeImage = self._imageCache.objectForKey(URL) as? Image
|
||||
let maybeImage = self._imageCache.object(forKey: url) as? Image
|
||||
|
||||
let decodedImage: Observable<Image>
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ class DefaultImageService: ImageService {
|
|||
decodedImage = Observable.just(image)
|
||||
}
|
||||
else {
|
||||
let cachedData = self._imageDataCache.objectForKey(URL) as? NSData
|
||||
let cachedData = self._imageDataCache.object(forKey: url) as? Data
|
||||
|
||||
// does image data cache contain anything
|
||||
if let cachedData = cachedData {
|
||||
|
|
@ -74,9 +74,9 @@ class DefaultImageService: ImageService {
|
|||
}
|
||||
else {
|
||||
// fetch from network
|
||||
decodedImage = self.$.URLSession.rx_data(NSURLRequest(URL: URL))
|
||||
decodedImage = self.$.URLSession.rx_data(URLRequest(url: url))
|
||||
.doOnNext { data in
|
||||
self._imageDataCache.setObject(data, forKey: URL)
|
||||
self._imageDataCache.setObject(data, forKey: url)
|
||||
}
|
||||
.flatMap(self.decodeImage)
|
||||
.trackActivity(self.loadingImage)
|
||||
|
|
@ -84,7 +84,7 @@ class DefaultImageService: ImageService {
|
|||
}
|
||||
|
||||
return decodedImage.doOnNext { image in
|
||||
self._imageCache.setObject(image, forKey: URL)
|
||||
self._imageCache.setObject(image, forKey: url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -97,10 +97,10 @@ class DefaultImageService: ImageService {
|
|||
|
||||
After image is sucessfully downloaded, sequence is completed.
|
||||
*/
|
||||
func imageFromURL(URL: NSURL, reachabilityService: ReachabilityService) -> Observable<DownloadableImage> {
|
||||
return _imageFromURL(URL)
|
||||
.map { DownloadableImage.Content(image: $0) }
|
||||
.retryOnBecomesReachable(DownloadableImage.OfflinePlaceholder, reachabilityService: reachabilityService)
|
||||
.startWith(.Content(image: Image()))
|
||||
func imageFromURL(_ url: URL, reachabilityService: ReachabilityService) -> Observable<DownloadableImage> {
|
||||
return _imageFromURL(url)
|
||||
.map { DownloadableImage.content(image: $0) }
|
||||
.retryOnBecomesReachable(DownloadableImage.offlinePlaceholder, reachabilityService: reachabilityService)
|
||||
.startWith(.content(image: Image()))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class Randomizer {
|
|||
self.unusedItems = []
|
||||
}
|
||||
|
||||
func countTotalItemsInSections(sections: [NumberSection]) -> Int {
|
||||
func countTotalItemsInSections(_ sections: [NumberSection]) -> Int {
|
||||
return sections.reduce(0) { p, s in
|
||||
return p + s.items.count
|
||||
}
|
||||
|
|
@ -73,7 +73,7 @@ class Randomizer {
|
|||
for section in unusedSections {
|
||||
let index = rng.get_random() % (sections.count + 1)
|
||||
if insertSections {
|
||||
sections.insert(NumberSection(model: section, items: []), atIndex: index)
|
||||
sections.insert(NumberSection(model: section, items: []), at: index)
|
||||
}
|
||||
else {
|
||||
nextUnusedSections.append(section)
|
||||
|
|
@ -90,7 +90,7 @@ class Randomizer {
|
|||
if rng.get_random() % 2 == 0 {
|
||||
let itemIndex = rng.get_random() % (itemCount + 1)
|
||||
if insertItems {
|
||||
sections[sectionIndex].items.insert(unusedValue, atIndex: itemIndex)
|
||||
sections[sectionIndex].items.insert(unusedValue, at: itemIndex)
|
||||
}
|
||||
else {
|
||||
nextUnusedItems.append(unusedValue)
|
||||
|
|
@ -99,14 +99,14 @@ class Randomizer {
|
|||
// update
|
||||
else {
|
||||
if itemCount == 0 {
|
||||
sections[sectionIndex].items.insert(unusedValue, atIndex: 0)
|
||||
sections[sectionIndex].items.insert(unusedValue, at: 0)
|
||||
continue
|
||||
}
|
||||
|
||||
let itemIndex = rng.get_random() % itemCount
|
||||
if reloadItems {
|
||||
nextUnusedItems.append(sections[sectionIndex].items.removeAtIndex(itemIndex))
|
||||
sections[sectionIndex].items.insert(unusedValue, atIndex: itemIndex)
|
||||
nextUnusedItems.append(sections[sectionIndex].items.remove(at: itemIndex))
|
||||
sections[sectionIndex].items.insert(unusedValue, at: itemIndex)
|
||||
|
||||
}
|
||||
else {
|
||||
|
|
@ -141,9 +141,9 @@ class Randomizer {
|
|||
let nextRandom = rng.get_random()
|
||||
|
||||
if moveItems {
|
||||
let item = sections[sourceSectionIndex].items.removeAtIndex(sourceItemIndex)
|
||||
let item = sections[sourceSectionIndex].items.remove(at: sourceItemIndex)
|
||||
let targetItemIndex = nextRandom % (self.sections[destinationSectionIndex].items.count + 1)
|
||||
sections[destinationSectionIndex].items.insert(item, atIndex: targetItemIndex)
|
||||
sections[destinationSectionIndex].items.insert(item, at: targetItemIndex)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -167,7 +167,7 @@ class Randomizer {
|
|||
let sourceItemIndex = rng.get_random() % sectionItemCount
|
||||
|
||||
if deleteItems {
|
||||
nextUnusedItems.append(sections[sourceSectionIndex].items.removeAtIndex(sourceItemIndex))
|
||||
nextUnusedItems.append(sections[sourceSectionIndex].items.remove(at: sourceItemIndex))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -184,8 +184,8 @@ class Randomizer {
|
|||
let targetIndex = rng.get_random() % sections.count
|
||||
|
||||
if explicitlyMoveSections {
|
||||
let section = sections.removeAtIndex(sectionIndex)
|
||||
sections.insert(section, atIndex: targetIndex)
|
||||
let section = sections.remove(at: sectionIndex)
|
||||
sections.insert(section, at: targetIndex)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -201,7 +201,7 @@ class Randomizer {
|
|||
let sectionIndex = rng.get_random() % sections.count
|
||||
|
||||
if deleteSections {
|
||||
let section = sections.removeAtIndex(sectionIndex)
|
||||
let section = sections.remove(at: sectionIndex)
|
||||
|
||||
for item in section.items {
|
||||
nextUnusedItems.append(item)
|
||||
|
|
@ -217,4 +217,4 @@ class Randomizer {
|
|||
unusedSections = nextUnusedSections
|
||||
unusedItems = nextUnusedItems
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,18 +29,19 @@ import SystemConfiguration
|
|||
import Foundation
|
||||
|
||||
enum ReachabilityError: ErrorProtocol {
|
||||
case FailedToCreateWithAddress(sockaddr_in)
|
||||
case FailedToCreateWithHostname(String)
|
||||
case UnableToSetCallback
|
||||
case UnableToSetDispatchQueue
|
||||
case failedToCreateWithAddress(sockaddr_in)
|
||||
case failedToCreateWithHostname(String)
|
||||
case unableToSetCallback
|
||||
case unableToSetDispatchQueue
|
||||
}
|
||||
|
||||
public let ReachabilityChangedNotification = "ReachabilityChangedNotification"
|
||||
|
||||
func callback(reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutablePointer<Void>) {
|
||||
let reachability = Unmanaged<Reachability>.fromOpaque(COpaquePointer(info)).takeUnretainedValue()
|
||||
func callback(_ reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutablePointer<Void>?) {
|
||||
guard let info = info else { return }
|
||||
let reachability = Unmanaged<Reachability>.fromOpaque(OpaquePointer(info)).takeUnretainedValue()
|
||||
|
||||
dispatch_async(dispatch_get_main_queue()) {
|
||||
DispatchQueue.main.async {
|
||||
reachability.reachabilityChanged(flags)
|
||||
}
|
||||
}
|
||||
|
|
@ -53,15 +54,15 @@ public class Reachability: NSObject {
|
|||
|
||||
public enum NetworkStatus: CustomStringConvertible {
|
||||
|
||||
case NotReachable, ReachableViaWiFi, ReachableViaWWAN
|
||||
case notReachable, reachableViaWiFi, reachableViaWWAN
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .ReachableViaWWAN:
|
||||
case .reachableViaWWAN:
|
||||
return "Cellular"
|
||||
case .ReachableViaWiFi:
|
||||
case .reachableViaWiFi:
|
||||
return "WiFi"
|
||||
case .NotReachable:
|
||||
case .notReachable:
|
||||
return "No Connection"
|
||||
}
|
||||
}
|
||||
|
|
@ -72,19 +73,19 @@ public class Reachability: NSObject {
|
|||
public var whenReachable: NetworkReachable?
|
||||
public var whenUnreachable: NetworkUnreachable?
|
||||
public var reachableOnWWAN: Bool
|
||||
public var notificationCenter = NSNotificationCenter.defaultCenter()
|
||||
public var notificationCenter = NotificationCenter.default()
|
||||
|
||||
public var currentReachabilityStatus: NetworkStatus {
|
||||
if isReachable() {
|
||||
if isReachableViaWiFi() {
|
||||
return .ReachableViaWiFi
|
||||
return .reachableViaWiFi
|
||||
}
|
||||
if isRunningOnDevice {
|
||||
return .ReachableViaWWAN
|
||||
return .reachableViaWWAN
|
||||
}
|
||||
}
|
||||
|
||||
return .NotReachable
|
||||
return .notReachable
|
||||
}
|
||||
|
||||
public var currentReachabilityString: String {
|
||||
|
|
@ -100,8 +101,8 @@ public class Reachability: NSObject {
|
|||
|
||||
public convenience init(hostname: String) throws {
|
||||
|
||||
let nodename = (hostname as NSString).UTF8String
|
||||
guard let ref = SCNetworkReachabilityCreateWithName(nil, nodename) else { throw ReachabilityError.FailedToCreateWithHostname(hostname) }
|
||||
let nodename = (hostname as NSString).utf8String
|
||||
guard let ref = SCNetworkReachabilityCreateWithName(nil, nodename!) else { throw ReachabilityError.failedToCreateWithHostname(hostname) }
|
||||
|
||||
self.init(reachabilityRef: ref)
|
||||
}
|
||||
|
|
@ -114,7 +115,7 @@ public class Reachability: NSObject {
|
|||
|
||||
guard let ref = withUnsafePointer(&zeroAddress, {
|
||||
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
|
||||
}) else { throw ReachabilityError.FailedToCreateWithAddress(zeroAddress) }
|
||||
}) else { throw ReachabilityError.failedToCreateWithAddress(zeroAddress) }
|
||||
|
||||
return Reachability(reachabilityRef: ref)
|
||||
}
|
||||
|
|
@ -131,7 +132,7 @@ public class Reachability: NSObject {
|
|||
|
||||
guard let ref = withUnsafePointer(&localWifiAddress, {
|
||||
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
|
||||
}) else { throw ReachabilityError.FailedToCreateWithAddress(localWifiAddress) }
|
||||
}) else { throw ReachabilityError.failedToCreateWithAddress(localWifiAddress) }
|
||||
|
||||
return Reachability(reachabilityRef: ref)
|
||||
}
|
||||
|
|
@ -142,16 +143,21 @@ public class Reachability: NSObject {
|
|||
if notifierRunning { return }
|
||||
|
||||
var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
|
||||
context.info = UnsafeMutablePointer(Unmanaged.passUnretained(self).toOpaque())
|
||||
|
||||
|
||||
let u = Unmanaged.passUnretained(self).toOpaque()
|
||||
|
||||
//context.info = u as? UnsafeMutablePointer<Void>
|
||||
|
||||
|
||||
if !SCNetworkReachabilitySetCallback(reachabilityRef!, callback, &context) {
|
||||
stopNotifier()
|
||||
throw ReachabilityError.UnableToSetCallback
|
||||
throw ReachabilityError.unableToSetCallback
|
||||
}
|
||||
|
||||
if !SCNetworkReachabilitySetDispatchQueue(reachabilityRef!, reachabilitySerialQueue) {
|
||||
stopNotifier()
|
||||
throw ReachabilityError.UnableToSetDispatchQueue
|
||||
throw ReachabilityError.unableToSetDispatchQueue
|
||||
}
|
||||
|
||||
notifierRunning = true
|
||||
|
|
@ -222,9 +228,9 @@ public class Reachability: NSObject {
|
|||
|
||||
private var notifierRunning = false
|
||||
private var reachabilityRef: SCNetworkReachability?
|
||||
private let reachabilitySerialQueue = dispatch_queue_create("uk.co.ashleymills.reachability", DISPATCH_QUEUE_SERIAL)
|
||||
private let reachabilitySerialQueue = DispatchQueue(label: "uk.co.ashleymills.reachability", attributes: DispatchQueueAttributes.serial)
|
||||
|
||||
private func reachabilityChanged(flags: SCNetworkReachabilityFlags) {
|
||||
private func reachabilityChanged(_ flags: SCNetworkReachabilityFlags) {
|
||||
if isReachableWithFlags(flags) {
|
||||
if let block = whenReachable {
|
||||
block(self)
|
||||
|
|
@ -235,10 +241,10 @@ public class Reachability: NSObject {
|
|||
}
|
||||
}
|
||||
|
||||
notificationCenter.postNotificationName(ReachabilityChangedNotification, object:self)
|
||||
notificationCenter.post(name: Notification.Name(rawValue: ReachabilityChangedNotification), object:self)
|
||||
}
|
||||
|
||||
private func isReachableWithFlags(flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
private func isReachableWithFlags(_ flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
|
||||
let reachable = isReachable(flags)
|
||||
|
||||
|
|
@ -260,7 +266,7 @@ public class Reachability: NSObject {
|
|||
return true
|
||||
}
|
||||
|
||||
private func isReachableWithTest(test: (SCNetworkReachabilityFlags) -> (Bool)) -> Bool {
|
||||
private func isReachableWithTest(_ test: (SCNetworkReachabilityFlags) -> (Bool)) -> Bool {
|
||||
|
||||
if let reachabilityRef = reachabilityRef {
|
||||
|
||||
|
|
@ -303,43 +309,43 @@ public class Reachability: NSObject {
|
|||
})
|
||||
}
|
||||
|
||||
private func isOnWWAN(flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
private func isOnWWAN(_ flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
#if os(iOS)
|
||||
return flags.contains(.IsWWAN)
|
||||
return flags.contains(.iswwan)
|
||||
#else
|
||||
return false
|
||||
#endif
|
||||
}
|
||||
private func isReachable(flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
return flags.contains(.Reachable)
|
||||
private func isReachable(_ flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
return flags.contains(.reachable)
|
||||
}
|
||||
private func isConnectionRequired(flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
return flags.contains(.ConnectionRequired)
|
||||
private func isConnectionRequired(_ flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
return flags.contains(.connectionRequired)
|
||||
}
|
||||
private func isInterventionRequired(flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
return flags.contains(.InterventionRequired)
|
||||
private func isInterventionRequired(_ flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
return flags.contains(.interventionRequired)
|
||||
}
|
||||
private func isConnectionOnTraffic(flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
return flags.contains(.ConnectionOnTraffic)
|
||||
private func isConnectionOnTraffic(_ flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
return flags.contains(.connectionOnTraffic)
|
||||
}
|
||||
private func isConnectionOnDemand(flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
return flags.contains(.ConnectionOnDemand)
|
||||
private func isConnectionOnDemand(_ flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
return flags.contains(.connectionOnDemand)
|
||||
}
|
||||
func isConnectionOnTrafficOrDemand(flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
return !flags.intersect([.ConnectionOnTraffic, .ConnectionOnDemand]).isEmpty
|
||||
func isConnectionOnTrafficOrDemand(_ flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
return !flags.intersection([.connectionOnTraffic, .connectionOnDemand]).isEmpty
|
||||
}
|
||||
private func isTransientConnection(flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
return flags.contains(.TransientConnection)
|
||||
private func isTransientConnection(_ flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
return flags.contains(.transientConnection)
|
||||
}
|
||||
private func isLocalAddress(flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
return flags.contains(.IsLocalAddress)
|
||||
private func isLocalAddress(_ flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
return flags.contains(.isLocalAddress)
|
||||
}
|
||||
private func isDirect(flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
return flags.contains(.IsDirect)
|
||||
private func isDirect(_ flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
return flags.contains(.isDirect)
|
||||
}
|
||||
private func isConnectionRequiredOrTransient(flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
let testcase:SCNetworkReachabilityFlags = [.ConnectionRequired, .TransientConnection]
|
||||
return flags.intersect(testcase) == testcase
|
||||
private func isConnectionRequiredOrTransient(_ flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
let testcase:SCNetworkReachabilityFlags = [.connectionRequired, .transientConnection]
|
||||
return flags.intersection(testcase) == testcase
|
||||
}
|
||||
|
||||
private var reachabilityFlags: SCNetworkReachabilityFlags {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import Foundation
|
|||
|
||||
public enum ReachabilityStatus {
|
||||
case Reachable(viaWiFi: Bool)
|
||||
case Unreachable
|
||||
case unreachable
|
||||
}
|
||||
|
||||
extension ReachabilityStatus {
|
||||
|
|
@ -21,7 +21,7 @@ extension ReachabilityStatus {
|
|||
switch self {
|
||||
case .Reachable:
|
||||
return true
|
||||
case .Unreachable:
|
||||
case .unreachable:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -44,20 +44,20 @@ class DefaultReachabilityService
|
|||
|
||||
init() throws {
|
||||
let reachabilityRef = try Reachability.reachabilityForInternetConnection()
|
||||
let reachabilitySubject = BehaviorSubject<ReachabilityStatus>(value: .Unreachable)
|
||||
let reachabilitySubject = BehaviorSubject<ReachabilityStatus>(value: .unreachable)
|
||||
|
||||
// so main thread isn't blocked when reachability via WiFi is checked
|
||||
let backgroundQueue = dispatch_queue_create("reachability.wificheck", DISPATCH_QUEUE_SERIAL)
|
||||
let backgroundQueue = DispatchQueue(label: "reachability.wificheck", attributes: DispatchQueueAttributes.serial)
|
||||
|
||||
reachabilityRef.whenReachable = { reachability in
|
||||
dispatch_async(backgroundQueue) {
|
||||
reachabilitySubject.on(.Next(.Reachable(viaWiFi: reachabilityRef.isReachableViaWiFi())))
|
||||
backgroundQueue.async {
|
||||
reachabilitySubject.on(.next(.Reachable(viaWiFi: reachabilityRef.isReachableViaWiFi())))
|
||||
}
|
||||
}
|
||||
|
||||
reachabilityRef.whenUnreachable = { reachability in
|
||||
dispatch_async(backgroundQueue) {
|
||||
reachabilitySubject.on(.Next(.Unreachable))
|
||||
backgroundQueue.async {
|
||||
reachabilitySubject.on(.next(.unreachable))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ class DefaultReachabilityService
|
|||
}
|
||||
|
||||
extension ObservableConvertibleType {
|
||||
func retryOnBecomesReachable(valueOnFailure:E, reachabilityService: ReachabilityService) -> Observable<E> {
|
||||
func retryOnBecomesReachable(_ valueOnFailure:E, reachabilityService: ReachabilityService) -> Observable<E> {
|
||||
return self.asObservable()
|
||||
.catchError { (e) -> Observable<E> in
|
||||
reachabilityService.reachability
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ import UIKit
|
|||
extension Image {
|
||||
func forceLazyImageDecompression() -> Image {
|
||||
#if os(iOS)
|
||||
UIGraphicsBeginImageContext(CGSizeMake(1, 1))
|
||||
self.drawAtPoint(CGPointZero)
|
||||
UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
|
||||
self.draw(at: CGPoint.zero)
|
||||
UIGraphicsEndImageContext()
|
||||
#endif
|
||||
return self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,18 +20,18 @@ extension UIImageView{
|
|||
return self.rxex_downloadableImageAnimated(nil)
|
||||
}
|
||||
|
||||
func rxex_downloadableImageAnimated(transitionType:String?) -> AnyObserver<DownloadableImage> {
|
||||
func rxex_downloadableImageAnimated(_ transitionType:String?) -> AnyObserver<DownloadableImage> {
|
||||
return UIBindingObserver(UIElement: self) { imageView, image in
|
||||
for subview in imageView.subviews {
|
||||
subview.removeFromSuperview()
|
||||
}
|
||||
switch image {
|
||||
case .Content(let image):
|
||||
case .content(let image):
|
||||
imageView.rx_image.onNext(image)
|
||||
case .OfflinePlaceholder:
|
||||
case .offlinePlaceholder:
|
||||
let label = UILabel(frame: imageView.bounds)
|
||||
label.textAlignment = .Center
|
||||
label.font = UIFont.systemFontOfSize(35)
|
||||
label.textAlignment = .center
|
||||
label.font = UIFont.systemFont(ofSize: 35)
|
||||
label.text = "⚠️"
|
||||
imageView.addSubview(label)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,22 +18,22 @@ import Cocoa
|
|||
#endif
|
||||
|
||||
enum RetryResult {
|
||||
case Retry
|
||||
case Cancel
|
||||
case retry
|
||||
case cancel
|
||||
}
|
||||
|
||||
protocol Wireframe {
|
||||
func openURL(URL: NSURL)
|
||||
func promptFor<Action: CustomStringConvertible>(message: String, cancelAction: Action, actions: [Action]) -> Observable<Action>
|
||||
func openURL(_ URL: URL)
|
||||
func promptFor<Action: CustomStringConvertible>(_ message: String, cancelAction: Action, actions: [Action]) -> Observable<Action>
|
||||
}
|
||||
|
||||
|
||||
class DefaultWireframe: Wireframe {
|
||||
static let sharedInstance = DefaultWireframe()
|
||||
|
||||
func openURL(URL: NSURL) {
|
||||
func openURL(_ URL: Foundation.URL) {
|
||||
#if os(iOS)
|
||||
UIApplication.sharedApplication().openURL(URL)
|
||||
UIApplication.shared().openURL(URL)
|
||||
#elseif os(OSX)
|
||||
NSWorkspace.sharedWorkspace().openURL(URL)
|
||||
#endif
|
||||
|
|
@ -42,37 +42,37 @@ class DefaultWireframe: Wireframe {
|
|||
#if os(iOS)
|
||||
private static func rootViewController() -> UIViewController {
|
||||
// cheating, I know
|
||||
return UIApplication.sharedApplication().keyWindow!.rootViewController!
|
||||
return UIApplication.shared().keyWindow!.rootViewController!
|
||||
}
|
||||
#endif
|
||||
|
||||
static func presentAlert(message: String) {
|
||||
static func presentAlert(_ message: String) {
|
||||
#if os(iOS)
|
||||
let alertView = UIAlertController(title: "RxExample", message: message, preferredStyle: .Alert)
|
||||
alertView.addAction(UIAlertAction(title: "OK", style: .Cancel) { _ in
|
||||
let alertView = UIAlertController(title: "RxExample", message: message, preferredStyle: .alert)
|
||||
alertView.addAction(UIAlertAction(title: "OK", style: .cancel) { _ in
|
||||
})
|
||||
rootViewController().presentViewController(alertView, animated: true, completion: nil)
|
||||
rootViewController().present(alertView, animated: true, completion: nil)
|
||||
#endif
|
||||
}
|
||||
|
||||
func promptFor<Action : CustomStringConvertible>(message: String, cancelAction: Action, actions: [Action]) -> Observable<Action> {
|
||||
func promptFor<Action : CustomStringConvertible>(_ message: String, cancelAction: Action, actions: [Action]) -> Observable<Action> {
|
||||
#if os(iOS)
|
||||
return Observable.create { observer in
|
||||
let alertView = UIAlertController(title: "RxExample", message: message, preferredStyle: .Alert)
|
||||
alertView.addAction(UIAlertAction(title: cancelAction.description, style: .Cancel) { _ in
|
||||
observer.on(.Next(cancelAction))
|
||||
let alertView = UIAlertController(title: "RxExample", message: message, preferredStyle: .alert)
|
||||
alertView.addAction(UIAlertAction(title: cancelAction.description, style: .cancel) { _ in
|
||||
observer.on(.next(cancelAction))
|
||||
})
|
||||
|
||||
for action in actions {
|
||||
alertView.addAction(UIAlertAction(title: action.description, style: .Default) { _ in
|
||||
observer.on(.Next(action))
|
||||
alertView.addAction(UIAlertAction(title: action.description, style: .default) { _ in
|
||||
observer.on(.next(action))
|
||||
})
|
||||
}
|
||||
|
||||
DefaultWireframe.rootViewController().presentViewController(alertView, animated: true, completion: nil)
|
||||
DefaultWireframe.rootViewController().present(alertView, animated: true, completion: nil)
|
||||
|
||||
return AnonymousDisposable {
|
||||
alertView.dismissViewControllerAnimated(false, completion: nil)
|
||||
alertView.dismiss(animated:false, completion: nil)
|
||||
}
|
||||
}
|
||||
#elseif os(OSX)
|
||||
|
|
@ -85,10 +85,10 @@ class DefaultWireframe: Wireframe {
|
|||
extension RetryResult : CustomStringConvertible {
|
||||
var description: String {
|
||||
switch self {
|
||||
case .Retry:
|
||||
case .retry:
|
||||
return "Retry"
|
||||
case .Cancel:
|
||||
case .cancel:
|
||||
return "Cancel"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,6 @@ import Foundation
|
|||
|
||||
extension String {
|
||||
var URLEscaped: String {
|
||||
return self.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet()) ?? ""
|
||||
return self.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
|
||||
var window: UIWindow?
|
||||
|
||||
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ extension ObservableType {
|
|||
- returns: Subscription object used to unsubscribe from the observable sequence.
|
||||
*/
|
||||
@warn_unused_result(message: "http://git.io/rxs.ud")
|
||||
public func subscribe(_ onNext: ((E) -> Void)? = nil, onError: ((ErrorProtocol) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil)
|
||||
public func subscribe(onNext: ((E) -> Void)? = nil, onError: ((ErrorProtocol) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil)
|
||||
-> Disposable {
|
||||
|
||||
let disposable: Disposable
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ extension ObservableType {
|
|||
- returns: The source sequence prepended with the specified values.
|
||||
*/
|
||||
@warn_unused_result(message:"http://git.io/rxs.uo")
|
||||
public func startWith(elements: E ...)
|
||||
public func startWith(_ elements: E ...)
|
||||
-> Observable<E> {
|
||||
return StartWith(source: self.asObservable(), elements: elements)
|
||||
}
|
||||
|
|
@ -199,7 +199,7 @@ extension ObservableType {
|
|||
- returns: An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully.
|
||||
*/
|
||||
@warn_unused_result(message:"http://git.io/rxs.uo")
|
||||
public func retry(maxAttemptCount: Int)
|
||||
public func retry(_ maxAttemptCount: Int)
|
||||
-> Observable<E> {
|
||||
return CatchSequence(sources: repeatElement(self.asObservable(), count: maxAttemptCount))
|
||||
}
|
||||
|
|
@ -251,7 +251,7 @@ extension ObservableType {
|
|||
- returns: An observable sequence containing the accumulated values.
|
||||
*/
|
||||
@warn_unused_result(message:"http://git.io/rxs.uo")
|
||||
public func scan<A>(seed: A, accumulator: (A, E) throws -> A)
|
||||
public func scan<A>(_ seed: A, accumulator: (A, E) throws -> A)
|
||||
-> Observable<A> {
|
||||
return Scan(source: self.asObservable(), seed: seed, accumulator: accumulator)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue