I am trying to extend my KeyboardView view with rx action with no success. According to debugging with breakpoints value is passed to the relay but extension is not called despite further subscription in a view controller. What might be a problem and how to fix it?
final class KeyboardView: UIView {
private let disposeBag = DisposeBag()
fileprivate let buttonTappedRelay = PublishRelay<ActionType>()
private let digitButtons: [KeyboardButton] = {
return stride(from: 0, through: 9, by: 1)
.compactMap { $0 }
.map { KeyboardButton(actionType: .digit($0)) }
}()
private let eraseButton: KeyboardButton = {
let button = KeyboardButton(actionType: .erase)
return button
}()
public override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
setupConstraints()
setupActions()
}
private func setupViews() { ... }
private func setupConstraints() { ... }
private func setupActions() {
eraseButton.rx.buttonTap
.asObservable()
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] actionType in
self?.buttonTappedRelay.accept(actionType)
}).disposed(by: self.disposeBag)
for button in digitButtons {
button.rx.buttonTap
.asObservable()
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] actionType in
self?.buttonTappedRelay.accept(actionType)
}).disposed(by: self.disposeBag)
}
}
}
extension Reactive where Base: KeyboardView {
internal var buttonTap: ControlEvent<ActionType> {
return ControlEvent<ActionType>(events: base.buttonTappedRelay.asObservable() )
}
}
Your problem is likely in code you haven't shown. Note that the below code compiles, runs and works:
final class KeyboardView: UIView {
private let disposeBag = DisposeBag()
fileprivate let buttonTappedRelay = PublishRelay<ActionType>()
private let digitButtons: [KeyboardButton] = {
return stride(from: 0, through: 9, by: 1)
.compactMap { $0 }
.map { KeyboardButton(actionType: .digit($0)) }
}()
private let eraseButton: KeyboardButton = {
let button = KeyboardButton(actionType: .erase)
return button
}()
public override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
setupActions()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupViews() {
let stack = UIStackView(frame: bounds)
stack.distribution = .equalSpacing
stack.addArrangedSubview(eraseButton)
for each in digitButtons {
stack.addArrangedSubview(each)
}
addSubview(stack)
}
private func setupActions() {
eraseButton.rx.buttonTap
.asObservable()
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] actionType in
self?.buttonTappedRelay.accept(actionType)
}).disposed(by: self.disposeBag)
for button in digitButtons {
button.rx.buttonTap
.asObservable()
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] actionType in
self?.buttonTappedRelay.accept(actionType)
}).disposed(by: self.disposeBag)
}
}
}
final class ViewController: UIViewController {
weak var keyboard: KeyboardView?
override func loadView() {
super.loadView()
let keyboard = KeyboardView(frame: view.bounds)
view.addSubview(keyboard)
self.keyboard = keyboard
}
override func viewDidLoad() {
super.viewDidLoad()
keyboard!.rx.buttonTap
.debug("🟣")
.subscribe()
}
}
extension Reactive where Base: KeyboardView {
internal var buttonTap: ControlEvent<ActionType> {
return ControlEvent<ActionType>(events: base.buttonTappedRelay.asObservable() )
}
}
enum ActionType {
case digit(Int)
case erase
}
class KeyboardButton: UIButton {
let actionType: ActionType
init(actionType: ActionType) {
self.actionType = actionType
super.init(frame: CGRect.zero)
backgroundColor = .red
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension Reactive where Base: KeyboardButton {
var buttonTap: Observable<ActionType> {
base.rx.tap.map { base.actionType }
}
}