Exposes `installForwardDelegate` and transforms `proxyForObject` into protocol extension.
This commit is contained in:
parent
9b6f069f81
commit
d197ce0fbb
|
|
@ -20,7 +20,7 @@ extension CLLocationManager {
|
|||
For more information take a look at `DelegateProxyType` protocol documentation.
|
||||
*/
|
||||
public var rx_delegate: DelegateProxy {
|
||||
return proxyForObject(RxCLLocationManagerDelegateProxy.self, self)
|
||||
return RxCLLocationManagerDelegateProxy.proxyForObject(self)
|
||||
}
|
||||
|
||||
// MARK: Responding to Location Events
|
||||
|
|
|
|||
|
|
@ -147,82 +147,100 @@ public protocol DelegateProxyType : AnyObject {
|
|||
func setForwardToDelegate(forwardToDelegate: AnyObject?, retainDelegate: Bool)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns existing proxy for object or installs new instance of delegate proxy.
|
||||
|
||||
- parameter object: Target object on which to install delegate proxy.
|
||||
- returns: Installed instance of delegate proxy.
|
||||
|
||||
|
||||
extension UISearchBar {
|
||||
|
||||
public var rx_delegate: DelegateProxy {
|
||||
return proxyForObject(RxSearchBarDelegateProxy.self, self)
|
||||
}
|
||||
|
||||
public var rx_text: ControlProperty<String> {
|
||||
let source: Observable<String> = self.rx_delegate.observe("searchBar:textDidChange:")
|
||||
...
|
||||
}
|
||||
}
|
||||
*/
|
||||
@available(*, deprecated=2.5, renamed="DelegateProxyType.proxyForObject", message="You can just use normal static protocol extension. E.g. `RxScrollViewDelegateProxy.proxyForObject`")
|
||||
public func proxyForObject<P: DelegateProxyType>(type: P.Type, _ object: AnyObject) -> P {
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
|
||||
let maybeProxy = P.assignedProxyFor(object) as? P
|
||||
|
||||
let proxy: P
|
||||
if maybeProxy == nil {
|
||||
proxy = P.createProxyForObject(object) as! P
|
||||
P.assignProxy(proxy, toObject: object)
|
||||
assert(P.assignedProxyFor(object) === proxy)
|
||||
}
|
||||
else {
|
||||
proxy = maybeProxy!
|
||||
}
|
||||
|
||||
let currentDelegate: AnyObject? = P.currentDelegateFor(object)
|
||||
|
||||
if currentDelegate !== proxy {
|
||||
proxy.setForwardToDelegate(currentDelegate, retainDelegate: false)
|
||||
P.setCurrentDelegate(proxy, toObject: object)
|
||||
assert(P.currentDelegateFor(object) === proxy)
|
||||
assert(proxy.forwardToDelegate() === currentDelegate)
|
||||
}
|
||||
|
||||
return proxy
|
||||
return P.proxyForObject(object)
|
||||
}
|
||||
|
||||
func installDelegate<P: DelegateProxyType>(proxy: P, delegate: AnyObject, retainDelegate: Bool, onProxyForObject object: AnyObject) -> Disposable {
|
||||
weak var weakDelegate: AnyObject? = delegate
|
||||
|
||||
assert(proxy.forwardToDelegate() === nil, "There is already a delegate set -> `\(proxy.forwardToDelegate()!)` for object -> `\(object)`.\nMaybe delegate was already set in `xib` or `storyboard` and now it's being overwritten in code.")
|
||||
|
||||
proxy.setForwardToDelegate(delegate, retainDelegate: retainDelegate)
|
||||
|
||||
// refresh properties after delegate is set
|
||||
// some views like UITableView cache `respondsToSelector`
|
||||
P.setCurrentDelegate(nil, toObject: object)
|
||||
P.setCurrentDelegate(proxy, toObject: object)
|
||||
|
||||
assert(proxy.forwardToDelegate() === delegate, "Setting of delegate failed")
|
||||
|
||||
return AnonymousDisposable {
|
||||
extension DelegateProxyType {
|
||||
/**
|
||||
Returns existing proxy for object or installs new instance of delegate proxy.
|
||||
|
||||
- parameter object: Target object on which to install delegate proxy.
|
||||
- returns: Installed instance of delegate proxy.
|
||||
|
||||
|
||||
extension UISearchBar {
|
||||
|
||||
public var rx_delegate: DelegateProxy {
|
||||
return proxyForObject(RxSearchBarDelegateProxy.self, self)
|
||||
}
|
||||
|
||||
public var rx_text: ControlProperty<String> {
|
||||
let source: Observable<String> = self.rx_delegate.observe("searchBar:textDidChange:")
|
||||
...
|
||||
}
|
||||
}
|
||||
*/
|
||||
public static func proxyForObject(object: AnyObject) -> Self {
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
|
||||
let maybeProxy = Self.assignedProxyFor(object) as? Self
|
||||
|
||||
let proxy: Self
|
||||
if maybeProxy == nil {
|
||||
proxy = Self.createProxyForObject(object) as! Self
|
||||
Self.assignProxy(proxy, toObject: object)
|
||||
assert(Self.assignedProxyFor(object) === proxy)
|
||||
}
|
||||
else {
|
||||
proxy = maybeProxy!
|
||||
}
|
||||
|
||||
let currentDelegate: AnyObject? = Self.currentDelegateFor(object)
|
||||
|
||||
if currentDelegate !== proxy {
|
||||
proxy.setForwardToDelegate(currentDelegate, retainDelegate: false)
|
||||
Self.setCurrentDelegate(proxy, toObject: object)
|
||||
assert(Self.currentDelegateFor(object) === proxy)
|
||||
assert(proxy.forwardToDelegate() === currentDelegate)
|
||||
}
|
||||
|
||||
let delegate: AnyObject? = weakDelegate
|
||||
return proxy
|
||||
}
|
||||
|
||||
/**
|
||||
Sets forward delegate for `DelegateProxyType` associated with a specific object and return disposable that can be used to unset the forward to delegate.
|
||||
Using this method will also make sure that potential original object cached selectors are cleared and will report any accidental forward delegate mutations.
|
||||
|
||||
- parameter forwardDelegate: Delegate object to set.
|
||||
- parameter retainDelegate: Retain `forwardDelegate` while it's being set.
|
||||
- parameter onProxyForObject: Object that has `delegate` property.
|
||||
- returns: Disposable object that can be used to clear forward delegate.
|
||||
*/
|
||||
public static func installForwardDelegate(forwardDelegate: AnyObject, retainDelegate: Bool, onProxyForObject object: AnyObject) -> Disposable {
|
||||
weak var weakForwardDelegate: AnyObject? = forwardDelegate
|
||||
|
||||
let proxy = Self.proxyForObject(object)
|
||||
|
||||
assert(delegate == nil || proxy.forwardToDelegate() === delegate, "Delegate was changed from time it was first set. Current \(proxy.forwardToDelegate()), and it should have been \(proxy)")
|
||||
assert(proxy.forwardToDelegate() === nil, "There is already a delegate set -> `\(proxy.forwardToDelegate()!)` for object -> `\(object)`.\nMaybe delegate was already set in `xib` or `storyboard` and now it's being overwritten in code.")
|
||||
|
||||
proxy.setForwardToDelegate(nil, retainDelegate: retainDelegate)
|
||||
proxy.setForwardToDelegate(forwardDelegate, retainDelegate: retainDelegate)
|
||||
|
||||
// refresh properties after delegate is set
|
||||
// some views like UITableView cache `respondsToSelector`
|
||||
Self.setCurrentDelegate(nil, toObject: object)
|
||||
Self.setCurrentDelegate(proxy, toObject: object)
|
||||
|
||||
assert(proxy.forwardToDelegate() === forwardDelegate, "Setting of delegate failed")
|
||||
|
||||
return AnonymousDisposable {
|
||||
MainScheduler.ensureExecutingOnScheduler()
|
||||
|
||||
let delegate: AnyObject? = weakForwardDelegate
|
||||
|
||||
assert(delegate == nil || proxy.forwardToDelegate() === delegate, "Delegate was changed from time it was first set. Current \(proxy.forwardToDelegate()), and it should have been \(proxy)")
|
||||
|
||||
proxy.setForwardToDelegate(nil, retainDelegate: retainDelegate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ObservableType {
|
||||
func subscribeProxyDataSourceForObject<P: DelegateProxyType>(object: AnyObject, dataSource: AnyObject, retainDataSource: Bool, binding: (P, Event<E>) -> Void)
|
||||
-> Disposable {
|
||||
let proxy = proxyForObject(P.self, object)
|
||||
let disposable = installDelegate(proxy, delegate: dataSource, retainDelegate: retainDataSource, onProxyForObject: object)
|
||||
let proxy = P.proxyForObject(object)
|
||||
let disposable = P.installForwardDelegate(dataSource, retainDelegate: retainDataSource, onProxyForObject: object)
|
||||
|
||||
let subscription = self.asObservable()
|
||||
// source can't ever end, otherwise it will release the subscriber
|
||||
|
|
|
|||
|
|
@ -93,14 +93,14 @@ extension NSTextField {
|
|||
For more information take a look at `DelegateProxyType` protocol documentation.
|
||||
*/
|
||||
public var rx_delegate: DelegateProxy {
|
||||
return proxyForObject(RxTextFieldDelegateProxy.self, self)
|
||||
return RxTextFieldDelegateProxy.proxyForObject(self)
|
||||
}
|
||||
|
||||
/**
|
||||
Reactive wrapper for `text` property.
|
||||
*/
|
||||
public var rx_text: ControlProperty<String> {
|
||||
let delegate = proxyForObject(RxTextFieldDelegateProxy.self, self)
|
||||
let delegate = RxTextFieldDelegateProxy.proxyForObject(self)
|
||||
|
||||
let source = Observable.deferred { [weak self] in
|
||||
delegate.textSubject.startWith(self?.stringValue ?? "")
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ extension NSTextStorage {
|
|||
For more information take a look at `DelegateProxyType` protocol documentation.
|
||||
*/
|
||||
public var rx_delegate:DelegateProxy {
|
||||
return proxyForObject(RxTextStorageDelegateProxy.self, self)
|
||||
return RxTextStorageDelegateProxy.proxyForObject(self)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ extension UICollectionView {
|
|||
For more information take a look at `DelegateProxyType` protocol documentation.
|
||||
*/
|
||||
public var rx_dataSource: DelegateProxy {
|
||||
return proxyForObject(RxCollectionViewDataSourceProxy.self, self)
|
||||
return RxCollectionViewDataSourceProxy.proxyForObject(self)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -125,8 +125,7 @@ extension UICollectionView {
|
|||
*/
|
||||
public func rx_setDataSource(dataSource: UICollectionViewDataSource)
|
||||
-> Disposable {
|
||||
let proxy = proxyForObject(RxCollectionViewDataSourceProxy.self, self)
|
||||
return installDelegate(proxy, delegate: dataSource, retainDelegate: false, onProxyForObject: self)
|
||||
return RxCollectionViewDataSourceProxy.installForwardDelegate(dataSource, retainDelegate: false, onProxyForObject: self)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import Foundation
|
|||
For more information take a look at `DelegateProxyType` protocol documentation.
|
||||
*/
|
||||
public var rx_delegate: DelegateProxy {
|
||||
return proxyForObject(RxImagePickerDelegateProxy.self, self)
|
||||
return RxImagePickerDelegateProxy.proxyForObject(self)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -31,14 +31,14 @@ extension UIScrollView {
|
|||
For more information take a look at `DelegateProxyType` protocol documentation.
|
||||
*/
|
||||
public var rx_delegate: DelegateProxy {
|
||||
return proxyForObject(RxScrollViewDelegateProxy.self, self)
|
||||
return RxScrollViewDelegateProxy.proxyForObject(self)
|
||||
}
|
||||
|
||||
/**
|
||||
Reactive wrapper for `contentOffset`.
|
||||
*/
|
||||
public var rx_contentOffset: ControlProperty<CGPoint> {
|
||||
let proxy = proxyForObject(RxScrollViewDelegateProxy.self, self)
|
||||
let proxy = RxScrollViewDelegateProxy.proxyForObject(self)
|
||||
|
||||
let bindingObserver = UIBindingObserver(UIElement: self) { scrollView, contentOffset in
|
||||
scrollView.contentOffset = contentOffset
|
||||
|
|
@ -57,8 +57,7 @@ extension UIScrollView {
|
|||
*/
|
||||
public func rx_setDelegate(delegate: UIScrollViewDelegate)
|
||||
-> Disposable {
|
||||
let proxy = proxyForObject(RxScrollViewDelegateProxy.self, self)
|
||||
return installDelegate(proxy, delegate: delegate, retainDelegate: false, onProxyForObject: self)
|
||||
return RxScrollViewDelegateProxy.installForwardDelegate(delegate, retainDelegate: false, onProxyForObject: self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ extension UISearchBar {
|
|||
For more information take a look at `DelegateProxyType` protocol documentation.
|
||||
*/
|
||||
public var rx_delegate: DelegateProxy {
|
||||
return proxyForObject(RxSearchBarDelegateProxy.self, self)
|
||||
return RxSearchBarDelegateProxy.proxyForObject(self)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ extension UISearchController {
|
|||
For more information take a look at `DelegateProxyType` protocol documentation.
|
||||
*/
|
||||
public var rx_delegate: DelegateProxy {
|
||||
return proxyForObject(RxSearchControllerDelegateProxy.self, self)
|
||||
return RxSearchControllerDelegateProxy.proxyForObject(self)
|
||||
}
|
||||
/**
|
||||
Reactive wrapper for `delegate` message.
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ extension UITableView {
|
|||
For more information take a look at `DelegateProxyType` protocol documentation.
|
||||
*/
|
||||
public var rx_dataSource: DelegateProxy {
|
||||
return proxyForObject(RxTableViewDataSourceProxy.self, self)
|
||||
return RxTableViewDataSourceProxy.proxyForObject(self)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -124,9 +124,7 @@ extension UITableView {
|
|||
*/
|
||||
public func rx_setDataSource(dataSource: UITableViewDataSource)
|
||||
-> Disposable {
|
||||
let proxy = proxyForObject(RxTableViewDataSourceProxy.self, self)
|
||||
|
||||
return installDelegate(proxy, delegate: dataSource, retainDelegate: false, onProxyForObject: self)
|
||||
return RxTableViewDataSourceProxy.installForwardDelegate(dataSource, retainDelegate: false, onProxyForObject: self)
|
||||
}
|
||||
|
||||
// events
|
||||
|
|
|
|||
|
|
@ -154,6 +154,10 @@ class UITableViewSubclass1
|
|||
.observe(#selector(TestDelegateProtocol.testEventHappened(_:)))
|
||||
.map { a in (a[0] as! NSNumber).integerValue }
|
||||
}
|
||||
|
||||
func setMineForwardDelegate(testDelegate: TestDelegateProtocol) -> Disposable {
|
||||
return RxScrollViewDelegateProxy.installForwardDelegate(testDelegate, retainDelegate: false, onProxyForObject: self)
|
||||
}
|
||||
}
|
||||
|
||||
class ExtendTableViewDataSourceProxy
|
||||
|
|
@ -183,6 +187,10 @@ class UITableViewSubclass2
|
|||
.observe(#selector(TestDelegateProtocol.testEventHappened(_:)))
|
||||
.map { a in (a[0] as! NSNumber).integerValue }
|
||||
}
|
||||
|
||||
func setMineForwardDelegate(testDelegate: TestDelegateProtocol) -> Disposable {
|
||||
return RxTableViewDataSourceProxy.installForwardDelegate(testDelegate, retainDelegate: false, onProxyForObject: self)
|
||||
}
|
||||
}
|
||||
|
||||
class ExtendCollectionViewDelegateProxy
|
||||
|
|
@ -212,6 +220,10 @@ class UICollectionViewSubclass1
|
|||
.observe(#selector(TestDelegateProtocol.testEventHappened(_:)))
|
||||
.map { a in (a[0] as! NSNumber).integerValue }
|
||||
}
|
||||
|
||||
func setMineForwardDelegate(testDelegate: TestDelegateProtocol) -> Disposable {
|
||||
return RxScrollViewDelegateProxy.installForwardDelegate(testDelegate, retainDelegate: false, onProxyForObject: self)
|
||||
}
|
||||
}
|
||||
|
||||
class ExtendCollectionViewDataSourceProxy
|
||||
|
|
@ -241,6 +253,10 @@ class UICollectionViewSubclass2
|
|||
.observe(#selector(TestDelegateProtocol.testEventHappened(_:)))
|
||||
.map { a in (a[0] as! NSNumber).integerValue }
|
||||
}
|
||||
|
||||
func setMineForwardDelegate(testDelegate: TestDelegateProtocol) -> Disposable {
|
||||
return RxCollectionViewDataSourceProxy.installForwardDelegate(testDelegate, retainDelegate: false, onProxyForObject: self)
|
||||
}
|
||||
}
|
||||
|
||||
class ExtendScrollViewDelegateProxy
|
||||
|
|
@ -270,6 +286,10 @@ class UIScrollViewSubclass
|
|||
.observe(#selector(TestDelegateProtocol.testEventHappened(_:)))
|
||||
.map { a in (a[0] as! NSNumber).integerValue }
|
||||
}
|
||||
|
||||
func setMineForwardDelegate(testDelegate: TestDelegateProtocol) -> Disposable {
|
||||
return RxScrollViewDelegateProxy.installForwardDelegate(testDelegate, retainDelegate: false, onProxyForObject: self)
|
||||
}
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
|
|
@ -301,6 +321,10 @@ class UISearchBarSubclass
|
|||
.observe(#selector(TestDelegateProtocol.testEventHappened(_:)))
|
||||
.map { a in (a[0] as! NSNumber).integerValue }
|
||||
}
|
||||
|
||||
func setMineForwardDelegate(testDelegate: TestDelegateProtocol) -> Disposable {
|
||||
return RxSearchBarDelegateProxy.installForwardDelegate(testDelegate, retainDelegate: false, onProxyForObject: self)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -331,6 +355,10 @@ class UITextViewSubclass
|
|||
.observe(#selector(TestDelegateProtocol.testEventHappened(_:)))
|
||||
.map { a in (a[0] as! NSNumber).integerValue }
|
||||
}
|
||||
|
||||
func setMineForwardDelegate(testDelegate: TestDelegateProtocol) -> Disposable {
|
||||
return RxScrollViewDelegateProxy.installForwardDelegate(testDelegate, retainDelegate: false, onProxyForObject: self)
|
||||
}
|
||||
}
|
||||
#if os(iOS)
|
||||
class UISearchControllerSubclass
|
||||
|
|
@ -346,5 +374,9 @@ class UISearchControllerSubclass
|
|||
.observe(#selector(TestDelegateProtocol.testEventHappened(_:)))
|
||||
.map { a in (a[0] as! NSNumber).integerValue }
|
||||
}
|
||||
|
||||
func setMineForwardDelegate(testDelegate: TestDelegateProtocol) -> Disposable {
|
||||
return RxSearchControllerDelegateProxy.installForwardDelegate(testDelegate, retainDelegate: false, onProxyForObject: self)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -20,10 +20,42 @@ import UIKit
|
|||
optional func testEventHappened(value: Int)
|
||||
}
|
||||
|
||||
@objc class MockTestDelegateProtocol:
|
||||
NSObject,
|
||||
TestDelegateProtocol,
|
||||
UICollectionViewDataSource,
|
||||
UIScrollViewDelegate,
|
||||
UITableViewDataSource,
|
||||
UITableViewDelegate {
|
||||
var numbers = [Int]()
|
||||
|
||||
func testEventHappened(value: Int) {
|
||||
numbers.append(value)
|
||||
}
|
||||
|
||||
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
protocol TestDelegateControl: NSObjectProtocol {
|
||||
func doThatTest(value: Int)
|
||||
|
||||
var test: Observable<Int> { get }
|
||||
|
||||
func setMineForwardDelegate(testDelegate: TestDelegateProtocol) -> Disposable
|
||||
}
|
||||
|
||||
// MARK: Tests
|
||||
|
|
@ -208,6 +240,18 @@ extension DelegateProxyTest {
|
|||
}
|
||||
XCTAssertEqual(receivedValue, 382763)
|
||||
|
||||
autoreleasepool {
|
||||
let mine = MockTestDelegateProtocol()
|
||||
let disposable = control.setMineForwardDelegate(mine)
|
||||
|
||||
XCTAssertEqual(mine.numbers, [])
|
||||
control.doThatTest(2)
|
||||
XCTAssertEqual(mine.numbers, [2])
|
||||
disposable.dispose()
|
||||
control.doThatTest(3)
|
||||
XCTAssertEqual(mine.numbers, [2])
|
||||
}
|
||||
|
||||
XCTAssertFalse(deallocated)
|
||||
XCTAssertFalse(completed)
|
||||
autoreleasepool {
|
||||
|
|
@ -280,7 +324,7 @@ class ThreeDSectionedViewDelegateProxy : DelegateProxy
|
|||
|
||||
extension ThreeDSectionedView {
|
||||
var rx_proxy: DelegateProxy {
|
||||
return proxyForObject(ThreeDSectionedViewDelegateProxy.self, self)
|
||||
return ThreeDSectionedViewDelegateProxy.proxyForObject(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue