简体   繁体   中英

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.

Due to the popularity of this answer, I have written an article about it: Convert a Swift Delegate to RxSwift Observables

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

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.

Assuming your DetailInputTextField class has a property delegate of type DetailInputTextFieldDelegate here is the example:

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:

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM