Skip to content

Commit

Permalink
Fixes issues with non open methods in DelegateProxy and table vie…
Browse files Browse the repository at this point in the history
…w delegate proxy responding to `#selector(UITableViewDataSource.tableView(_:commit:forRowAt:)`. ReactiveX#907 ReactiveX#884
  • Loading branch information
kzaher committed Oct 1, 2016
1 parent cb5ae2f commit 8eb3458
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 16 deletions.
24 changes: 16 additions & 8 deletions RxCocoa/Common/DelegateProxy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,20 @@ open class DelegateProxy : _RXDelegateProxy {
- parameter selector: Selector used to filter observed invocations of delegate methods.
- returns: Observable sequence of arguments passed to `selector` method.
*/
public func observe(_ selector: Selector) -> Observable<[AnyObject]> {
open func observe(_ selector: Selector) -> Observable<[AnyObject]> {
MainScheduler.ensureExecutingOnScheduler()

if hasWiredImplementation(for: selector) {
print("Delegate proxy is already implementing `\(selector)`, a more performant way of registering might exist.")
}

if !self.responds(to: selector) {
// It's important to see if super class reponds to selector and not self,
// because super class (_RxDelegateProxy) returns all methods delegate proxy
// can respond to.
// Because of https://github.com/ReactiveX/RxSwift/issues/907 , and possibly
// some other reasons, subclasses could overrride `responds(to:)`, but it shouldn't matter
// for this case.
if !super.responds(to: selector) {
rxFatalError("This class doesn't respond to selector \(selector)")
}

Expand All @@ -117,7 +125,7 @@ open class DelegateProxy : _RXDelegateProxy {
- returns: Associated object tag.
*/
public class func delegateAssociatedObjectTag() -> UnsafeRawPointer {
open class func delegateAssociatedObjectTag() -> UnsafeRawPointer {
return _pointer(&delegateAssociatedTag)
}

Expand All @@ -126,7 +134,7 @@ open class DelegateProxy : _RXDelegateProxy {
- returns: Initialized instance of `self`.
*/
public class func createProxyForObject(_ object: AnyObject) -> AnyObject {
open class func createProxyForObject(_ object: AnyObject) -> AnyObject {
return self.init(parentObject: object)
}

Expand All @@ -136,7 +144,7 @@ open class DelegateProxy : _RXDelegateProxy {
- parameter object: Object that can have assigned delegate proxy.
- returns: Assigned delegate proxy or `nil` if no delegate proxy is assigned.
*/
public class func assignedProxyFor(_ object: AnyObject) -> AnyObject? {
open class func assignedProxyFor(_ object: AnyObject) -> AnyObject? {
let maybeDelegate = objc_getAssociatedObject(object, self.delegateAssociatedObjectTag())
return castOptionalOrFatalError(maybeDelegate.map { $0 as AnyObject })
}
Expand All @@ -147,7 +155,7 @@ open class DelegateProxy : _RXDelegateProxy {
- parameter object: Object that can have assigned delegate proxy.
- parameter proxy: Delegate proxy object to assign to `object`.
*/
public class func assignProxy(_ proxy: AnyObject, toObject object: AnyObject) {
open class func assignProxy(_ proxy: AnyObject, toObject object: AnyObject) {
precondition(proxy.isKind(of: self.classForCoder()))

objc_setAssociatedObject(object, self.delegateAssociatedObjectTag(), proxy, .OBJC_ASSOCIATION_RETAIN)
Expand All @@ -160,7 +168,7 @@ open class DelegateProxy : _RXDelegateProxy {
- parameter forwardToDelegate: Reference of delegate that receives all messages through `self`.
- parameter retainDelegate: Should `self` retain `forwardToDelegate`.
*/
public func setForwardToDelegate(_ delegate: AnyObject?, retainDelegate: Bool) {
open func setForwardToDelegate(_ delegate: AnyObject?, retainDelegate: Bool) {
self._setForward(toDelegate: delegate, retainDelegate: retainDelegate)
}

Expand All @@ -170,7 +178,7 @@ open class DelegateProxy : _RXDelegateProxy {
- returns: Value of reference if set or nil.
*/
public func forwardToDelegate() -> AnyObject? {
open func forwardToDelegate() -> AnyObject? {
return self._forwardToDelegate
}

Expand Down
52 changes: 52 additions & 0 deletions RxCocoa/iOS/Proxies/RxTableViewDataSourceProxy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ public class RxTableViewDataSourceProxy
Typed parent object.
*/
public weak fileprivate(set) var tableView: UITableView?

// issue https://github.com/ReactiveX/RxSwift/issues/907
private var _commitForRowAtHasObservers = false
private var _commitForRowAtSequence: Observable<[AnyObject]>? = nil

fileprivate weak var _requiredMethodsDataSource: UITableViewDataSource? = tableViewDataSourceNotSet

Expand Down Expand Up @@ -112,6 +116,54 @@ public class RxTableViewDataSourceProxy
_requiredMethodsDataSource = requiredMethodsDataSource ?? tableViewDataSourceNotSet
super.setForwardToDelegate(forwardToDelegate, retainDelegate: retainDelegate)
}

override open func observe(_ selector: Selector) -> Observable<[AnyObject]> {
MainScheduler.ensureExecutingOnScheduler()

// This is special behavior for commit:forRowAt:
// If proxy data source responds to this selector then table view will show
// swipe to delete option even when nobody is observing.
// https://github.com/ReactiveX/RxSwift/issues/907
if selector == #selector(UITableViewDataSource.tableView(_:commit:forRowAt:)) {
guard let commitForRowAtSequence = _commitForRowAtSequence else {
let commitForRowAtSequence = super.observe(selector)
.do(onSubscribe: { [weak self] in
self?._commitForRowAtHasObservers = true
self?.refreshTableViewDataSource()
}, onDispose: { [weak self] in
self?._commitForRowAtHasObservers = false
self?.refreshTableViewDataSource()
})
.subscribeOn(MainScheduler())
.share()

_commitForRowAtSequence = commitForRowAtSequence

return commitForRowAtSequence
}

return commitForRowAtSequence
}

return super.observe(selector)
}

// https://github.com/ReactiveX/RxSwift/issues/907
private func refreshTableViewDataSource() {
if self.tableView?.dataSource === self {
self.tableView?.dataSource = nil
self.tableView?.dataSource = self
}
}

override open func responds(to aSelector: Selector!) -> Bool {
// https://github.com/ReactiveX/RxSwift/issues/907
if aSelector == #selector(UITableViewDataSource.tableView(_:commit:forRowAt:)) {
return _commitForRowAtHasObservers
}

return super.responds(to: aSelector)
}
}

#endif
1 change: 1 addition & 0 deletions RxCocoa/iOS/Proxies/RxTableViewDelegateProxy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class RxTableViewDelegateProxy
self.tableView = (parentObject as! UITableView)
super.init(parentObject: parentObject)
}

}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,6 @@ class SimpleTableViewExampleViewController : ViewController, UITableViewDelegate
})
.addDisposableTo(disposeBag)

// to prevent swipe to delete
tableView.rx.setDelegate(self)
.addDisposableTo(disposeBag)
}

// to prevent swipe to delete behavior
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
return .none
}
}
5 changes: 4 additions & 1 deletion RxSwift/Schedulers/MainScheduler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ public final class MainScheduler : SerialDispatchQueueScheduler {

var numberEnqueued: AtomicInt = 0

private init() {
/**
Initializes new instance of `MainScheduler`.
*/
public init() {
_mainQueue = DispatchQueue.main
super.init(serialQueue: _mainQueue)
}
Expand Down

0 comments on commit 8eb3458

Please sign in to comment.