How to convert Delegate to Observable RxSwift?

Clash Royale CLAN TAG#URR8PPP
How to convert Delegate to Observable RxSwift?
I have delegate methods, which I need to wrap by Delegate Proxy in RxSwift. I have done it using Bond and Reactive, but here, in RxSwift, I am not able to find the proper way to convert it.
Follow is Protocols
import UIKit
/**
A protocol for the delegate of a `DetailInputTextField`.
*/
@objc
public protocol CardInfoTextFieldDelegate
/**
Called whenever valid information was entered into `textField`.
- parameter textField: The text field whose information was updated and is valid.
- parameter didEnterValidInfo: The valid information that was entered into `textField`.
*/
func textField(_ textField: UITextField, didEnterValidInfo: String)
/**
Called whenever partially valid information was entered into `textField`.
- parameter textField: The text field whose information was updated and is partially valid.
- parameter didEnterPartiallyValidInfo: The partially valid information that was entered.
*/
func textField(_ textField: UITextField, didEnterPartiallyValidInfo: String)
/**
Called whenever more text was entered into `textField` than necessary. This can be used to provide this overflow as text in the next text field in the responder chain.
- parameter textField: The text field which received more information than required.
- parameter overFlowDigits: The overflow of text which does not fit into `textField` and might be entered into the next receiver in the responder chain.
*/
func textField(_ textField: UITextField, didEnterOverflowInfo overFlowDigits: String)
What I did earlier is
import Foundation
import Bond
import Caishen
extension DetailInputTextField
var bnd_cardInfoDelegate: ProtocolProxy
return protocolProxy(for: CardInfoTextFieldDelegate.self, setter: NSSelectorFromString("setCardInfoTextFieldDelegate:"))
var bnd_didEnterValidInfo: StreamSignal<NSString>
return bnd_cardInfoDelegate.signal(for: #selector(CardInfoTextFieldDelegate.textField(_:didEnterValidInfo:)))
(s: PublishSignal<NSString>, _: UITextField, info: NSString) in
s.next(info)
var bnd_didEnterPartiallyValidInfo: StreamSignal<NSString>
return bnd_cardInfoDelegate.signal(for: #selector(CardInfoTextFieldDelegate.textField(_:didEnterPartiallyValidInfo:)))
(s: PublishSignal<NSString>, _: UITextField, info: NSString) in
s.next(info)
var bnd_didEnterOverflowInfo: StreamSignal<NSString>
return bnd_cardInfoDelegate.signal(for: #selector(CardInfoTextFieldDelegate.textField(_:didEnterOverflowInfo:)))
(s: PublishSignal<NSString>, _: UITextField, info: NSString) in
s.next(info)
How can I do same exercise in RxSwift.
I tried DelegateProxy but its unclear how it properly wrap it.
So, where delegate methods of some class with be called or assigned?
– Sam Shaikh
Aug 13 at 9:15
if you observer Q, there are four delegates, and need them to be used, in main controller, by wrapping, but how we can do same here in Rx?
– Sam Shaikh
Aug 13 at 9:19
2 Answers
2
I believe this is the official way of converting a delegate into RxObservables:
class CardInfoTextField: NSObject
weak var delegate: CardInfoTextFieldDelegate? = nil
@objc
protocol CardInfoTextFieldDelegate
@objc optional func textField(_ textField: CardInfoTextField, didEnterValidInfo: String)
@objc optional func textField(_ textField: CardInfoTextField, didEnterPartiallyValidInfo: String)
@objc optional func textField(_ textField: CardInfoTextField, didEnterOverflowInfo overFlowDigits: String)
extension CardInfoTextField: HasDelegate
public typealias Delegate = CardInfoTextFieldDelegate
class CardInfoTextFieldDelegateProxy
: DelegateProxy<CardInfoTextField, CardInfoTextFieldDelegate>
, DelegateProxyType
, CardInfoTextFieldDelegate
//#MARK: DelegateProxy
init(parentObject: CardInfoTextField)
super.init(parentObject: parentObject, delegateProxy: CardInfoTextFieldDelegateProxy.self)
public static func registerKnownImplementations()
self.register CardInfoTextFieldDelegateProxy(parentObject: $0)
extension Reactive where Base: CardInfoTextField
var delegate: CardInfoTextFieldDelegateProxy
return CardInfoTextFieldDelegateProxy.proxy(for: base)
var didEnterValidInfo: Observable<String>
return delegate.methodInvoked(#selector(CardInfoTextFieldDelegate.textField(_:didEnterValidInfo:)))
.map $0[1] as! String
var didEnterPartiallyValidInfo: Observable<String>
return delegate.methodInvoked(#selector(CardInfoTextFieldDelegate.textField(_:didEnterPartiallyValidInfo:)))
.map $0[1] as! String
var didEnterOverflowInfo: Observable<String>
return delegate.methodInvoked(#selector(CardInfoTextFieldDelegate.textField(_:didEnterOverflowInfo:)))
.map $0[1] as! String
Once you have the above, you should be able to:
let validInfo: Observable<String> = myCardInfoTextField.rx.didEnterValidInfo
Bloody hell. The key to this is to define the delegate method as optional, it also relies on objective-c run time so that means you can't use enums in your method.
– Pavan
Aug 23 at 11:15
EDIT
I've removed my previous code and adjusted it to your desired solution. In order to "wrap" delegates of various classes (mostly UI) into observables you can use DelegateProxy class which is a part of RxCocoa framework.
DelegateProxy
RxCocoa
Assuming your DetailInputTextField class has a property delegate of type DetailInputTextFieldDelegate here is the example:
DetailInputTextField
delegate
DetailInputTextFieldDelegate
First the custom proxy:
import RxSwift
import RxCocoa
extension DetailInputTextField: HasDelegate
public typealias Delegate = DetailInputTextFieldDelegate
open class DetailInputTextFieldDelegateProxy
: DelegateProxy<DetailInputTextField, DetailInputTextFieldDelegate>
, DelegateProxyType
, DetailInputTextFieldDelegate
/// Typed parent object.
public weak private(set) var textField: DetailInputTextField?
/// - parameter webView: Parent object for delegate proxy.
public init(textField: ParentObject)
self.textField = textField
super.init(parentObject: textField, delegateProxy: DetailInputTextFieldDelegateProxy.self)
// Register known implementations
public static func registerKnownImplementations()
self.register DetailInputTextFieldDelegateProxy(textField: $0)
Then you need to extend Reactive where you can add all the observables corresponding to delegate methods:
Reactive
extension Reactive where Base: DetailInputTextField
public var delegate: DelegateProxy<DetailInputTextField, DetailInputTextFieldDelegate>
return DetailInputTextFieldDelegateProxy.proxy(for: base)
public var didEnterValidInfo: Observable<(UITextField,String)>
return delegate
.methodInvoked(#selector(DetailInputTextFieldDelegate.textField(_:didEnterValidInfo:)))
.map params in
// Parameters is an array, be sure you cast them correctly
return (params[0] as! UITextField, params[1] as! String)
When you have the "wrapper" implemented, you can call it like this:
let textField = DetailInputTextField()
textField.rx.didEnterValidInfo
.asObservable()
.subscribe(onNext: (textField: UITextField, string: String) in
print("Test (string)")
)
.disposed(by: disposeBag)
I hope it answers your question.
This DetailInputTextField is not mine class. Its declared already somewhere in library, so won't it effect? secondly what is delegate, its not identifying it
– Sam Shaikh
Aug 13 at 10:26
If
DetailInputTextField is not your class then you should not add code to it. I will update my answer.– Au Ris
Aug 13 at 11:13
DetailInputTextField
Sure, I am waiting, I made something like extension Reactive where Base: DetailInputTextField public var dataSource: DelegateProxy<DetailInputTextField, CardInfoTextFieldDelegate> return RxDetailInputTextFieldProxy.proxy(for: base) /// For more information take a look at
DelegateProxyType. open class RxDetailInputTextFieldProxy : DelegateProxy<DetailInputTextField, CardInfoTextFieldDelegate> , DelegateProxyType , CardInfoTextFieldDelegate { But it seems unclear yet.– Sam Shaikh
Aug 13 at 11:19
DelegateProxyType
Please check my updated answer. Initially I had a another way in mind, but this was an interesting exercise for me too.
– Au Ris
Aug 13 at 13:02
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
If you're using RxSwift (observation) why do you want to keep on using delegation at all?
– Au Ris
Aug 13 at 8:12