diff --git a/ReactiveUIKit.podspec b/ReactiveUIKit.podspec index 8a2a9db..bcc0a43 100644 --- a/ReactiveUIKit.podspec +++ b/ReactiveUIKit.podspec @@ -1,11 +1,11 @@ Pod::Spec.new do |s| s.name = "ReactiveUIKit" - s.version = "2.0.0-beta5" + s.version = "2.0.0" s.summary = "Reactive extensions for UIKit framework." s.homepage = "https://github.com/ReactiveKit/ReactiveUIKit" s.license = 'MIT' s.author = { "Srdan Rasic" => "srdan.rasic@gmail.com" } - s.source = { :git => "https://github.com/ReactiveKit/ReactiveUIKit.git", :tag => "v2.0.0-beta5" } + s.source = { :git => "https://github.com/ReactiveKit/ReactiveUIKit.git", :tag => "v2.0.0" } s.ios.deployment_target = '8.0' s.tvos.deployment_target = '9.0' diff --git a/ReactiveUIKit.xcodeproj/project.pbxproj b/ReactiveUIKit.xcodeproj/project.pbxproj index f23c26d..82c68ed 100644 --- a/ReactiveUIKit.xcodeproj/project.pbxproj +++ b/ReactiveUIKit.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 165F031D1CEFA7A900FCE1C9 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165F031C1CEFA7A900FCE1C9 /* Protocols.swift */; }; + 165F031E1CEFA7A900FCE1C9 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165F031C1CEFA7A900FCE1C9 /* Protocols.swift */; }; + 165F03201CEFA7D600FCE1C9 /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165F031F1CEFA7D600FCE1C9 /* UIApplication.swift */; }; + 165F03211CEFA7D600FCE1C9 /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165F031F1CEFA7D600FCE1C9 /* UIApplication.swift */; }; EC5C668F1CB3D64B00C5F3C3 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC8A9A2B1CABED670042A6AD /* UITableView.swift */; }; EC5C66901CB3D64C00C5F3C3 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC8A9A2B1CABED670042A6AD /* UITableView.swift */; }; EC83BAF51CB00A5B007D7E47 /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC83BAF41CB00A5B007D7E47 /* UIBarButtonItem.swift */; }; @@ -62,6 +66,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 165F031C1CEFA7A900FCE1C9 /* Protocols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Protocols.swift; path = Sources/Protocols.swift; sourceTree = SOURCE_ROOT; }; + 165F031F1CEFA7D600FCE1C9 /* UIApplication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UIApplication.swift; path = Sources/UIApplication.swift; sourceTree = SOURCE_ROOT; }; 16E125A11BF5056C00543EFB /* ReactiveUIKit.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = ReactiveUIKit.podspec; sourceTree = ""; }; 16E125A21BF5056C00543EFB /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 16F69F6A1CB462D400FD2B5F /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; @@ -146,11 +152,11 @@ children = ( ECB7A5441BEB71E10034053A /* ReactiveUIKit.h */, ECB7A5461BEB71E10034053A /* Info.plist */, + 165F031F1CEFA7D600FCE1C9 /* UIApplication.swift */, EC8A9A1D1CABED670042A6AD /* UIActivityIndicatorView.swift */, EC8A9A1E1CABED670042A6AD /* UIBarItem.swift */, EC83BAF41CB00A5B007D7E47 /* UIBarButtonItem.swift */, EC8A9A1F1CABED670042A6AD /* UIButton.swift */, - EC8A9A201CABED670042A6AD /* UICollectionView.swift */, EC8A9A211CABED670042A6AD /* UIControl.swift */, EC8A9A221CABED670042A6AD /* UIDatePicker.swift */, EC8A9A231CABED670042A6AD /* UIImageView.swift */, @@ -162,9 +168,11 @@ EC8A9A291CABED670042A6AD /* UISlider.swift */, EC8A9A2A1CABED670042A6AD /* UISwitch.swift */, EC8A9A2B1CABED670042A6AD /* UITableView.swift */, + EC8A9A201CABED670042A6AD /* UICollectionView.swift */, EC8A9A2C1CABED670042A6AD /* UITextField.swift */, EC8A9A2D1CABED670042A6AD /* UITextView.swift */, EC8A9A2E1CABED670042A6AD /* UIView.swift */, + 165F031C1CEFA7A900FCE1C9 /* Protocols.swift */, ); path = ReactiveUIKit; sourceTree = ""; @@ -332,12 +340,14 @@ EC8A9A3C1CABED670042A6AD /* UIImageView.swift in Sources */, EC8A9A401CABED670042A6AD /* UINavigationItem.swift in Sources */, EC8A9A301CABED670042A6AD /* UIActivityIndicatorView.swift in Sources */, + 165F031E1CEFA7A900FCE1C9 /* Protocols.swift in Sources */, EC8A9A441CABED670042A6AD /* UIRefreshControl.swift in Sources */, EC8A9A3A1CABED670042A6AD /* UIDatePicker.swift in Sources */, EC5C66901CB3D64C00C5F3C3 /* UITableView.swift in Sources */, EC83BAF61CB00A5B007D7E47 /* UIBarButtonItem.swift in Sources */, EC8A9A421CABED670042A6AD /* UIProgressView.swift in Sources */, EC8A9A3E1CABED670042A6AD /* UILabel.swift in Sources */, + 165F03211CEFA7D600FCE1C9 /* UIApplication.swift in Sources */, EC8A9A341CABED670042A6AD /* UIButton.swift in Sources */, EC8A9A481CABED670042A6AD /* UISlider.swift in Sources */, EC8A9A521CABED670042A6AD /* UIView.swift in Sources */, @@ -358,12 +368,14 @@ EC8A9A3B1CABED670042A6AD /* UIImageView.swift in Sources */, EC8A9A3F1CABED670042A6AD /* UINavigationItem.swift in Sources */, EC8A9A2F1CABED670042A6AD /* UIActivityIndicatorView.swift in Sources */, + 165F031D1CEFA7A900FCE1C9 /* Protocols.swift in Sources */, EC8A9A431CABED670042A6AD /* UIRefreshControl.swift in Sources */, EC8A9A391CABED670042A6AD /* UIDatePicker.swift in Sources */, EC5C668F1CB3D64B00C5F3C3 /* UITableView.swift in Sources */, EC83BAF51CB00A5B007D7E47 /* UIBarButtonItem.swift in Sources */, EC8A9A411CABED670042A6AD /* UIProgressView.swift in Sources */, EC8A9A3D1CABED670042A6AD /* UILabel.swift in Sources */, + 165F03201CEFA7D600FCE1C9 /* UIApplication.swift in Sources */, EC8A9A331CABED670042A6AD /* UIButton.swift in Sources */, EC8A9A471CABED670042A6AD /* UISlider.swift in Sources */, EC8A9A511CABED670042A6AD /* UIView.swift in Sources */, diff --git a/Sources/Protocols.swift b/Sources/Protocols.swift new file mode 100644 index 0000000..c6b7774 --- /dev/null +++ b/Sources/Protocols.swift @@ -0,0 +1,34 @@ +// +// The MIT License (MIT) +// +// Copyright (c) 2015 Srdan Rasic (@srdanrasic) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +public protocol ArrayConvertible { + associatedtype Element + func toArray() -> [Element] +} + +extension Array: ArrayConvertible { + public func toArray() -> [Element] { + return self + } +} diff --git a/Sources/UIApplication.swift b/Sources/UIApplication.swift new file mode 100644 index 0000000..8fc9622 --- /dev/null +++ b/Sources/UIApplication.swift @@ -0,0 +1,17 @@ +// +// UIApplication.swift +// ReactiveUIKit +// +// Created by Srdan Rasic on 20/05/16. +// Copyright © 2016 Srdan Rasic. All rights reserved. +// + +import UIKit +import ReactiveKit + +extension UIApplication { + + public var rNetworkActivityIndicatorVisible: Property { + return rAssociatedPropertyForValueForKey("networkActivityIndicatorVisible") + } +} diff --git a/Sources/UICollectionView.swift b/Sources/UICollectionView.swift index c382638..6c2e92f 100644 --- a/Sources/UICollectionView.swift +++ b/Sources/UICollectionView.swift @@ -25,114 +25,88 @@ import ReactiveKit import UIKit -extension UICollectionView { - private struct AssociatedKeys { - static var DataSourceKey = "r_DataSourceKey" +private func applyRowUnitChangeSet(changeSet: C, collectionView: UICollectionView, sectionIndex: Int) { + if changeSet.inserts.count > 0 { + let indexPaths = changeSet.inserts.map { NSIndexPath(forItem: $0, inSection: sectionIndex) } + collectionView.insertItemsAtIndexPaths(indexPaths) } -} -extension StreamType where Event.Element: CollectionChangesetType, Event.Element.Collection.Index == Int { - public func bindTo(collectionView: UICollectionView, animated: Bool = true, proxyDataSource: RKCollectionViewProxyDataSource? = nil, createCell: (NSIndexPath, Event.Element.Collection, UICollectionView) -> UICollectionViewCell) -> Disposable { + if changeSet.updates.count > 0 { + let indexPaths = changeSet.updates.map { NSIndexPath(forItem: $0, inSection: sectionIndex) } + collectionView.reloadItemsAtIndexPaths(indexPaths) + } - let dataSource = RKCollectionViewDataSource(stream: self, collectionView: collectionView, animated: animated, proxyDataSource: proxyDataSource, createCell: createCell) - objc_setAssociatedObject(collectionView, &UICollectionView.AssociatedKeys.DataSourceKey, dataSource, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) - - return BlockDisposable { [weak collectionView] in - if let collectionView = collectionView { - objc_setAssociatedObject(collectionView, &UICollectionView.AssociatedKeys.DataSourceKey, nil, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - } + if changeSet.deletes.count > 0 { + let indexPaths = changeSet.deletes.map { NSIndexPath(forItem: $0, inSection: sectionIndex) } + collectionView.deleteItemsAtIndexPaths(indexPaths) } } -@objc public protocol RKCollectionViewProxyDataSource { - optional func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView - optional func collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool - optional func collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) +extension StreamType where Element: ArrayConvertible { + + public func bindTo(collectionView: UICollectionView, animated: Bool = true, createCell: (NSIndexPath, [Element.Element], UICollectionView) -> UICollectionViewCell) -> Disposable { + return map { CollectionChangeset.initial($0.toArray()) }.bindTo(collectionView, animated: animated, createCell: createCell) + } } -public class RKCollectionViewDataSource: NSObject, UICollectionViewDataSource { - - private typealias Collection = S.Event.Element.Collection - - private let stream: S - private var sourceCollection: Collection? = nil - private weak var collectionView: UICollectionView! - private let createCell: (NSIndexPath, Collection, UICollectionView) -> UICollectionViewCell - private weak var proxyDataSource: RKCollectionViewProxyDataSource? - private let animated: Bool - - public init(stream: S, collectionView: UICollectionView, animated: Bool = true, proxyDataSource: RKCollectionViewProxyDataSource?, createCell: (NSIndexPath, Collection, UICollectionView) -> UICollectionViewCell) { - self.collectionView = collectionView - self.createCell = createCell - self.proxyDataSource = proxyDataSource - self.stream = stream - self.animated = animated - super.init() - - collectionView.dataSource = self +extension StreamType where Element: CollectionChangesetType, Element.Collection.Index == Int, Event.Element == Element { + + public func bindTo(collectionView: UICollectionView, animated: Bool = true, createCell: (NSIndexPath, Element.Collection, UICollectionView) -> UICollectionViewCell) -> Disposable { + + typealias Collection = Element.Collection + + let dataSource = collectionView.rDataSource + let numberOfItems = Property(0) + let collection = Property(nil) + + dataSource.feed( + collection, + to: #selector(UICollectionViewDataSource.collectionView(_:cellForItemAtIndexPath:)), + map: { (value: Collection!, collectionView: UICollectionView, indexPath: NSIndexPath) -> UICollectionViewCell in + return createCell(indexPath, value, collectionView) + }) + + dataSource.feed( + numberOfItems, + to: #selector(UICollectionViewDataSource.collectionView(_:numberOfItemsInSection:)), + map: { (value: Int, _: UICollectionView, _: Int) -> Int in value } + ) + + dataSource.feed( + Property(1), + to: #selector(UICollectionViewDataSource.numberOfSectionsInCollectionView(_:)), + map: { (value: Int, _: UICollectionView) -> Int in value } + ) + collectionView.reloadData() - stream.observeNext { [weak self] event in - if let uSelf = self { - let justReload = uSelf.sourceCollection == nil - uSelf.sourceCollection = event.collection - if justReload || !animated { - uSelf.collectionView.reloadData() + let serialDisposable = SerialDisposable(otherDisposable: nil) + serialDisposable.otherDisposable = observeNext { [weak collectionView] event in + ImmediateOnMainExecutionContext { + guard let collectionView = collectionView else { serialDisposable.dispose(); return } + let justReload = collection.value == nil + collection.value = event.collection + numberOfItems.value = event.collection.count + if justReload || !animated || event.inserts.count + event.deletes.count + event.updates.count == 0 { + collectionView.reloadData() } else { - uSelf.collectionView.performBatchUpdates({ - RKCollectionViewDataSource.applyRowUnitChangeSet(event, collectionView: uSelf.collectionView, sectionIndex: 0, dataSource: uSelf.proxyDataSource) - }, completion: nil) + collectionView.performBatchUpdates({ + applyRowUnitChangeSet(event, collectionView: collectionView, sectionIndex: 0) + }, completion: nil) } } - }.disposeIn(rBag) - } - - private class func applyRowUnitChangeSet(changeSet: S.Event.Element, collectionView: UICollectionView, sectionIndex: Int, dataSource: RKCollectionViewProxyDataSource?) { - - if changeSet.inserts.count > 0 { - let indexPaths = changeSet.inserts.map { NSIndexPath(forItem: $0, inSection: sectionIndex) } - collectionView.insertItemsAtIndexPaths(indexPaths) - } - - if changeSet.updates.count > 0 { - let indexPaths = changeSet.updates.map { NSIndexPath(forItem: $0, inSection: sectionIndex) } - collectionView.reloadItemsAtIndexPaths(indexPaths) } - - if changeSet.deletes.count > 0 { - let indexPaths = changeSet.deletes.map { NSIndexPath(forItem: $0, inSection: sectionIndex) } - collectionView.deleteItemsAtIndexPaths(indexPaths) - } - } - - /// MARK - UICollectionViewDataSource - - @objc public func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { - return 1 + return serialDisposable } - - @objc public func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return sourceCollection?.count ?? 0 - } - - @objc public func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { - return createCell(indexPath, sourceCollection!, collectionView) - } - - @objc public func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView { - if let view = proxyDataSource?.collectionView?(collectionView, viewForSupplementaryElementOfKind: kind, atIndexPath: indexPath) { - return view - } else { - fatalError("Dear Sir/Madam, your collection view has asked for a supplementary view of a \(kind) kind. Please provide a proxy data source object in bindTo() method that implements `collectionView(collectionView:viewForSupplementaryElementOfKind:atIndexPath)` method!") - } - } - - @objc public func collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool { - return proxyDataSource?.collectionView?(collectionView, canMoveItemAtIndexPath: indexPath) ?? false +} + +extension UICollectionView { + + public var rDelegate: ProtocolProxy { + return protocolProxyFor(UICollectionViewDelegate.self, setter: NSSelectorFromString("setDelegate:")) } - - @objc public func collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) { - proxyDataSource?.collectionView?(collectionView, moveItemAtIndexPath: sourceIndexPath, toIndexPath: destinationIndexPath) + + public var rDataSource: ProtocolProxy { + return protocolProxyFor(UICollectionViewDataSource.self, setter: NSSelectorFromString("setDataSource:")) } } diff --git a/Sources/UITableView.swift b/Sources/UITableView.swift index f4ae570..200a109 100644 --- a/Sources/UITableView.swift +++ b/Sources/UITableView.swift @@ -25,145 +25,88 @@ import ReactiveKit import UIKit -extension UITableView { - private struct AssociatedKeys { - static var DataSourceKey = "r_DataSourceKey" +private func applyRowUnitChangeSet(changeSet: C, tableView: UITableView, sectionIndex: Int) { + if changeSet.inserts.count > 0 { + let indexPaths = changeSet.inserts.map { NSIndexPath(forItem: $0, inSection: sectionIndex) } + tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic) } -} - -extension StreamType where Event.Element: CollectionChangesetType, Event.Element.Collection.Index == Int { - public func bindTo(tableView: UITableView, animated: Bool = true, proxyDataSource: RKTableViewProxyDataSource? = nil, createCell: (NSIndexPath, Event.Element.Collection, UITableView) -> UITableViewCell) -> Disposable { - let dataSource = RKTableViewDataSource(stream: self, tableView: tableView, animated: animated, proxyDataSource: proxyDataSource, createCell: createCell) - objc_setAssociatedObject(tableView, &UITableView.AssociatedKeys.DataSourceKey, dataSource, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) + if changeSet.updates.count > 0 { + let indexPaths = changeSet.updates.map { NSIndexPath(forItem: $0, inSection: sectionIndex) } + tableView.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic) + } - return BlockDisposable { [weak tableView] in - if let tableView = tableView { - objc_setAssociatedObject(tableView, &UITableView.AssociatedKeys.DataSourceKey, nil, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - } + if changeSet.deletes.count > 0 { + let indexPaths = changeSet.deletes.map { NSIndexPath(forItem: $0, inSection: sectionIndex) } + tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic) } } -@objc public protocol RKTableViewProxyDataSource { - optional func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? - optional func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? - optional func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool - optional func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool - optional func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? - optional func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int - optional func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) - optional func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) - - /// Override to specify custom row animation when row is being inserted, deleted or updated - optional func tableView(tableView: UITableView, animationForRowAtIndexPaths indexPaths: [NSIndexPath]) -> UITableViewRowAnimation - - /// Override to specify custom row animation when section is being inserted, deleted or updated - optional func tableView(tableView: UITableView, animationForRowInSections sections: Set) -> UITableViewRowAnimation +extension StreamType where Element: ArrayConvertible { + + public func bindTo(tableView: UITableView, animated: Bool = true, createCell: (NSIndexPath, [Element.Element], UITableView) -> UITableViewCell) -> Disposable { + return map { CollectionChangeset.initial($0.toArray()) }.bindTo(tableView, animated: animated, createCell: createCell) + } } -public class RKTableViewDataSource: NSObject, UITableViewDataSource { - - private typealias Collection = S.Event.Element.Collection - - private let stream: S - private var sourceCollection: Collection? = nil - private weak var tableView: UITableView! - private let createCell: (NSIndexPath, Collection, UITableView) -> UITableViewCell - private weak var proxyDataSource: RKTableViewProxyDataSource? - private let animated: Bool - - public init(stream: S, tableView: UITableView, animated: Bool, proxyDataSource: RKTableViewProxyDataSource?, createCell: (NSIndexPath, Collection, UITableView) -> UITableViewCell) { - self.tableView = tableView - self.createCell = createCell - self.proxyDataSource = proxyDataSource - self.stream = stream - self.animated = animated - super.init() - - tableView.dataSource = self +extension StreamType where Element: CollectionChangesetType, Element.Collection.Index == Int, Event.Element == Element { + + public func bindTo(tableView: UITableView, animated: Bool = true, createCell: (NSIndexPath, Element.Collection, UITableView) -> UITableViewCell) -> Disposable { + + typealias Collection = Element.Collection + + let dataSource = tableView.rDataSource + let numberOfItems = Property(0) + let collection = Property(nil) + + dataSource.feed( + collection, + to: #selector(UITableViewDataSource.tableView(_:cellForRowAtIndexPath:)), + map: { (value: Collection!, tableView: UITableView, indexPath: NSIndexPath) -> UITableViewCell in + return createCell(indexPath, value, tableView) + }) + + dataSource.feed( + numberOfItems, + to: #selector(UITableViewDataSource.tableView(_:numberOfRowsInSection:)), + map: { (value: Int, _: UITableView, _: Int) -> Int in value } + ) + + dataSource.feed( + Property(1), + to: #selector(UITableViewDataSource.numberOfSectionsInTableView(_:)), + map: { (value: Int, _: UITableView) -> Int in value } + ) + tableView.reloadData() - stream.observeNext { [weak self] event in - if let uSelf = self { - let justReload = uSelf.sourceCollection == nil - uSelf.sourceCollection = event.collection - if justReload || !animated { - uSelf.tableView.reloadData() + let serialDisposable = SerialDisposable(otherDisposable: nil) + serialDisposable.otherDisposable = observeNext { [weak tableView] event in + ImmediateOnMainExecutionContext { + guard let tableView = tableView else { serialDisposable.dispose(); return } + let justReload = collection.value == nil + collection.value = event.collection + numberOfItems.value = event.collection.count + if justReload || !animated || event.inserts.count + event.deletes.count + event.updates.count == 0 { + tableView.reloadData() } else { - uSelf.tableView.beginUpdates() - RKTableViewDataSource.applyRowUnitChangeSet(event, tableView: uSelf.tableView, sectionIndex: 0, dataSource: uSelf.proxyDataSource) - uSelf.tableView.endUpdates() + tableView.beginUpdates() + applyRowUnitChangeSet(event, tableView: tableView, sectionIndex: 0) + tableView.endUpdates() } } - }.disposeIn(rBag) - } - - private class func applyRowUnitChangeSet(changeSet: S.Event.Element, tableView: UITableView, sectionIndex: Int, dataSource: RKTableViewProxyDataSource?) { - - if changeSet.inserts.count > 0 { - let indexPaths = changeSet.inserts.map { NSIndexPath(forItem: $0, inSection: sectionIndex) } - tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: dataSource?.tableView?(tableView, animationForRowAtIndexPaths: indexPaths) ?? .Automatic) - } - - if changeSet.updates.count > 0 { - let indexPaths = changeSet.updates.map { NSIndexPath(forItem: $0, inSection: sectionIndex) } - tableView.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: dataSource?.tableView?(tableView, animationForRowAtIndexPaths: indexPaths) ?? .Automatic) } - - if changeSet.deletes.count > 0 { - let indexPaths = changeSet.deletes.map { NSIndexPath(forItem: $0, inSection: sectionIndex) } - tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: dataSource?.tableView?(tableView, animationForRowAtIndexPaths: indexPaths) ?? .Automatic) - } - } - - /// MARK - UITableViewDataSource - - @objc public func numberOfSectionsInTableView(tableView: UITableView) -> Int { - return 1 - } - - @objc public func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return sourceCollection?.count ?? 0 - } - - @objc public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { - return createCell(indexPath, sourceCollection!, tableView) - } - - @objc public func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { - return proxyDataSource?.tableView?(tableView, titleForHeaderInSection: section) - } - - @objc public func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? { - return proxyDataSource?.tableView?(tableView, titleForFooterInSection: section) - } - - @objc public func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { - return proxyDataSource?.tableView?(tableView, canEditRowAtIndexPath: indexPath) ?? true - } - - @objc public func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool { - return proxyDataSource?.tableView?(tableView, canMoveRowAtIndexPath: indexPath) ?? false + return serialDisposable } - - @objc public func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? { - return proxyDataSource?.sectionIndexTitlesForTableView?(tableView) - } - - @objc public func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int { - if let section = proxyDataSource?.tableView?(tableView, sectionForSectionIndexTitle: title, atIndex: index) { - return section - } else { - fatalError("Dear Sir/Madam, your table view has asked for section for section index title \(title). Please provide a proxy data source object in bindTo() method that implements `tableView(tableView:sectionForSectionIndexTitle:atIndex:)` method!") - } - } - - @objc public func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { - proxyDataSource?.tableView?(tableView, commitEditingStyle: editingStyle, forRowAtIndexPath: indexPath) +} + +extension UITableView { + + public var rDelegate: ProtocolProxy { + return protocolProxyFor(UITableViewDelegate.self, setter: NSSelectorFromString("setDelegate:")) } - - @objc public func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) { - proxyDataSource?.tableView?(tableView, moveRowAtIndexPath: sourceIndexPath, toIndexPath: destinationIndexPath) + + public var rDataSource: ProtocolProxy { + return protocolProxyFor(UITableViewDataSource.self, setter: NSSelectorFromString("setDataSource:")) } } diff --git a/Sources/UITextField.swift b/Sources/UITextField.swift index a88e35d..de81eef 100644 --- a/Sources/UITextField.swift +++ b/Sources/UITextField.swift @@ -22,7 +22,6 @@ // THE SOFTWARE. // - import ReactiveKit import UIKit diff --git a/Sources/UITextView.swift b/Sources/UITextView.swift index bfc7ba7..04d4261 100644 --- a/Sources/UITextView.swift +++ b/Sources/UITextView.swift @@ -22,7 +22,6 @@ // THE SOFTWARE. // - import ReactiveKit import UIKit