Skip to content

Commit f61ecc7

Browse files
committed
Merge branch 'feature/type_safe_delegateproxy' of https://github.com/tarunon/RxSwift into tarunon-feature/type_safe_delegateproxy
2 parents c79b00f + fc26b0d commit f61ecc7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+524
-473
lines changed

RxCocoa/Common/DelegateProxy.swift

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,20 @@ let dataSourceAssociatedTag: UnsafeRawPointer = UnsafeRawPointer(UnsafeMutablePo
2121
/// Base class for `DelegateProxyType` protocol.
2222
///
2323
/// This implementation is not thread safe and can be used only from one thread (Main thread).
24-
open class DelegateProxy : _RXDelegateProxy {
25-
24+
open class DelegateProxy<P: AnyObject, D: NSObjectProtocol>: _RXDelegateProxy {
25+
public typealias ParentObject = P
26+
public typealias Delegate = D
27+
2628
private var sentMessageForSelector = [Selector: MessageDispatcher]()
2729
private var methodInvokedForSelector = [Selector: MessageDispatcher]()
2830

2931
/// Parent object associated with delegate proxy.
30-
weak private(set) var parentObject: AnyObject?
32+
weak private(set) var parentObject: ParentObject?
3133

3234
/// Initializes new instance.
3335
///
3436
/// - parameter parentObject: Optional parent object that owns `DelegateProxy` as associated object.
35-
public required init(parentObject: AnyObject) {
37+
public required init(parentObject: ParentObject) {
3638
self.parentObject = parentObject
3739

3840
MainScheduler.ensureExecutingOnScheduler()
@@ -73,7 +75,7 @@ open class DelegateProxy : _RXDelegateProxy {
7375

7476
// reactive property implementation in a real class (`UIScrollView`)
7577
public var property: Observable<CGPoint> {
76-
let proxy = RxScrollViewDelegateProxy.proxyForObject(base)
78+
let proxy = RxScrollViewDelegateProxy.proxy(for: base)
7779
return proxy.internalSubject.asObservable()
7880
}
7981

@@ -131,7 +133,7 @@ open class DelegateProxy : _RXDelegateProxy {
131133

132134
// reactive property implementation in a real class (`UIScrollView`)
133135
public var property: Observable<CGPoint> {
134-
let proxy = RxScrollViewDelegateProxy.proxyForObject(base)
136+
let proxy = RxScrollViewDelegateProxy.proxy(for: base)
135137
return proxy.internalSubject.asObservable()
136138
}
137139

@@ -192,7 +194,7 @@ open class DelegateProxy : _RXDelegateProxy {
192194
///
193195
/// - parameter object: Object that can have assigned delegate proxy.
194196
/// - returns: Assigned delegate proxy or `nil` if no delegate proxy is assigned.
195-
open class func assignedProxyFor(_ object: AnyObject) -> AnyObject? {
197+
open class func assignedProxy(for object: ParentObject) -> Delegate? {
196198
let maybeDelegate = objc_getAssociatedObject(object, self.delegateAssociatedObjectTag())
197199
return castOptionalOrFatalError(maybeDelegate.map { $0 as AnyObject })
198200
}
@@ -201,18 +203,45 @@ open class DelegateProxy : _RXDelegateProxy {
201203
///
202204
/// - parameter object: Object that can have assigned delegate proxy.
203205
/// - parameter proxy: Delegate proxy object to assign to `object`.
204-
open class func assignProxy(_ proxy: AnyObject, toObject object: AnyObject) {
205-
precondition(proxy.isKind(of: self.classForCoder()))
206-
206+
open class func assignProxy(_ proxy: Delegate, toObject object: ParentObject) {
207207
objc_setAssociatedObject(object, self.delegateAssociatedObjectTag(), proxy, .OBJC_ASSOCIATION_RETAIN)
208208
}
209209

210+
211+
/// Returns designated delegate property for object.
212+
///
213+
/// Objects can have multiple delegate properties.
214+
///
215+
/// Each delegate property needs to have it's own type implementing `DelegateProxyType`.
216+
///
217+
/// It's abstract method.
218+
///
219+
/// - parameter object: Object that has delegate property.
220+
/// - returns: Value of delegate property.
221+
open class func currentDelegate(for object: ParentObject) -> Delegate? {
222+
rxAbstractMethod()
223+
}
224+
225+
/// Sets designated delegate property for object.
226+
///
227+
/// Objects can have multiple delegate properties.
228+
///
229+
/// Each delegate property needs to have it's own type implementing `DelegateProxyType`.
230+
///
231+
/// It's abstract method.
232+
///
233+
/// - parameter toObject: Object that has delegate property.
234+
/// - parameter delegate: Delegate value.
235+
open class func setCurrentDelegate(_ delegate: Delegate?, toObject: ParentObject) {
236+
rxAbstractMethod()
237+
}
238+
210239
/// Sets reference of normal delegate that receives all forwarded messages
211240
/// through `self`.
212241
///
213242
/// - parameter forwardToDelegate: Reference of delegate that receives all messages through `self`.
214243
/// - parameter retainDelegate: Should `self` retain `forwardToDelegate`.
215-
open func setForwardToDelegate(_ delegate: AnyObject?, retainDelegate: Bool) {
244+
open func setForwardToDelegate(_ delegate: Delegate?, retainDelegate: Bool) {
216245
#if DEBUG // 4.0 all configurations
217246
MainScheduler.ensureExecutingOnScheduler()
218247
#endif
@@ -224,8 +253,8 @@ open class DelegateProxy : _RXDelegateProxy {
224253
/// through `self`.
225254
///
226255
/// - returns: Value of reference if set or nil.
227-
open func forwardToDelegate() -> AnyObject? {
228-
return self._forwardToDelegate
256+
open func forwardToDelegate() -> Delegate? {
257+
return castOptionalOrFatalError(self._forwardToDelegate)
229258
}
230259

231260
private func hasObservers(selector: Selector) -> Bool {
@@ -240,20 +269,15 @@ open class DelegateProxy : _RXDelegateProxy {
240269
}
241270

242271
internal func reset() {
243-
guard let delegateProxySelf = self as? DelegateProxyType else {
244-
rxFatalErrorInDebug("\(self) doesn't implement delegate proxy type.")
245-
return
246-
}
247-
248272
guard let parentObject = self.parentObject else { return }
249273

250-
let selfType = type(of: delegateProxySelf)
274+
let selfType = type(of: self)
251275

252-
let maybeCurrentDelegate = selfType.currentDelegateFor(parentObject)
276+
let maybeCurrentDelegate = selfType.currentDelegate(for: parentObject)
253277

254278
if maybeCurrentDelegate === self {
255279
selfType.setCurrentDelegate(nil, toObject: parentObject)
256-
selfType.setCurrentDelegate(self, toObject: parentObject)
280+
selfType.setCurrentDelegate(castOrFatalError(self), toObject: parentObject)
257281
}
258282
}
259283

@@ -276,7 +300,7 @@ fileprivate final class MessageDispatcher {
276300
private let dispatcher: PublishSubject<[Any]>
277301
private let result: Observable<[Any]>
278302

279-
init(delegateProxy _delegateProxy: DelegateProxy) {
303+
init<P, D>(delegateProxy _delegateProxy: DelegateProxy<P, D>) {
280304
weak var weakDelegateProxy = _delegateProxy
281305

282306
let dispatcher = PublishSubject<[Any]>()

RxCocoa/Common/DelegateProxyFactory.swift

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,49 +18,73 @@ For example, in RxScrollViewDelegateProxy
1818

1919

2020
class RxScrollViewDelegateProxy: DelegateProxy {
21-
static var factory = DelegateProxyFactory { (parentObject: UIScrollView) in
22-
RxScrollViewDelegateProxy(parentObject: parentObject)
21+
static var factory: DelegateProxyFactory {
22+
return DelegateProxyFactory.sharedFactory(for: RxScrollViewDelegateProxy.self)
2323
}
2424
...
2525

2626

27-
If need to extend them, chain `extended` after DelegateProxyFactory.init
27+
If need to extend them, call `DelegateProxySubclass.register()` in `knownImplementations`.
2828

2929
class RxScrollViewDelegateProxy: DelegateProxy {
30-
static var factory = DelegateProxyFactory { (parentObject: UIScrollView) in
31-
RxScrollViewDelegateProxy(parentObject: parentObject)
32-
}
33-
.extended { (parentObject: UITableView) in
34-
RxTableViewDelegateProxy(parentObject: parentObject)
35-
}
30+
static func knownImplementations() {
31+
RxTableViewDelegateProxy.register(for: UITableView)
32+
}
33+
34+
static var factory: DelegateProxyFactory {
35+
return DelegateProxyFactory.sharedFactory(for: RxScrollViewDelegateProxy.self)
36+
}
3637
...
3738

38-
39+
3940
*/
4041
public class DelegateProxyFactory {
42+
private static var _sharedFactories: [ObjectIdentifier: DelegateProxyFactory] = [:]
43+
44+
/**
45+
Shared instance of DelegateProxyFactory, if isn't exist shared instance, make DelegateProxyFactory instance for proxy type and extends.
46+
DelegateProxyFactory have a shared instance per Delegate type.
47+
- parameter proxyType: DelegateProxy type. Should use concrete DelegateProxy type, not generic.
48+
- returns: DelegateProxyFactory shared instance.
49+
*/
50+
public static func sharedFactory<DelegateProxy: DelegateProxyType>(for proxyType: DelegateProxy.Type) -> DelegateProxyFactory {
51+
MainScheduler.ensureExecutingOnScheduler()
52+
if let factory = _sharedFactories[ObjectIdentifier(DelegateProxy.Delegate.self)] {
53+
return factory
54+
}
55+
let factory = DelegateProxyFactory(for: proxyType)
56+
_sharedFactories[ObjectIdentifier(DelegateProxy.Delegate.self)] = factory
57+
DelegateProxy.knownImplementations()
58+
return factory
59+
}
60+
4161
private var _factories: [ObjectIdentifier: ((AnyObject) -> AnyObject)]
42-
public init<Object: AnyObject>(factory: @escaping (Object) -> AnyObject) {
43-
_factories = [ObjectIdentifier(Object.self): { factory(castOrFatalError($0)) }]
62+
63+
private init<DelegateProxy: DelegateProxyType>(for proxyType: DelegateProxy.Type) {
64+
_factories = [:]
65+
self.extend(with: proxyType, for: DelegateProxy.ParentObject.self)
4466
}
45-
67+
4668
/**
4769
Extend DelegateProxyFactory for specific object class and delegate proxy.
48-
Define object class on closure argument.
70+
- parameter proxyType: The DelegateProxy type that subclass of factorys owner.
71+
- parameter parentObjectType: Parent object type of DelegateProxy.
4972
*/
50-
public func extended<Object: AnyObject>(factory: @escaping (Object) -> AnyObject) -> DelegateProxyFactory {
73+
internal func extend<DelegateProxy: DelegateProxyType>(with proxyType: DelegateProxy.Type, for parentObjectType: DelegateProxy.ParentObject.Type) {
5174
MainScheduler.ensureExecutingOnScheduler()
52-
guard _factories[ObjectIdentifier(Object.self)] == nil else {
53-
rxFatalError("The factory of \(Object.self) is duplicated. DelegateProxy is not allowed of duplicated base object type.")
75+
assert((DelegateProxy.self as? DelegateProxy.Delegate) != nil, "DelegateProxy subclass should be as a Delegate")
76+
guard _factories[ObjectIdentifier(parentObjectType)] == nil else {
77+
rxFatalError("The factory of \(parentObjectType) is duplicated. DelegateProxy is not allowed of duplicated base object type.")
5478
}
55-
_factories[ObjectIdentifier(Object.self)] = { factory(castOrFatalError($0)) }
56-
return self
79+
_factories[ObjectIdentifier(parentObjectType)] = { proxyType.init(parentObject: castOrFatalError($0)) }
5780
}
5881

5982
/**
6083
Create DelegateProxy for object.
6184
DelegateProxyFactory should have a factory of object class (or superclass).
85+
Should not call this function directory, use 'DelegateProxy.proxy(for:)'
6286
*/
63-
public func createProxy(for object: AnyObject) -> AnyObject {
87+
internal func createProxy(for object: AnyObject) -> AnyObject {
6488
MainScheduler.ensureExecutingOnScheduler()
6589
var mirror: Mirror? = Mirror(reflecting: object)
6690
while mirror != nil {
@@ -69,7 +93,7 @@ public class DelegateProxyFactory {
6993
}
7094
mirror = mirror?.superclassMirror
7195
}
72-
rxFatalError("DelegateProxy has no factory of \(object). Call 'DelegateProxy.extend' first.")
96+
rxFatalError("DelegateProxy has no factory of \(object). Implement DelegateProxy subclass for \(object) first.")
7397
}
7498
}
7599

0 commit comments

Comments
 (0)