Repository: ReactiveX/RxSwift
File: RxCocoa/iOS/UIBarButtonItem+Rx.swift (Swift)

Code before:
//
//  UIBarButtonItem+Rx.swift
//  RxCocoa
//
//  Created by Daniel Tartaglia on 5/31/15.
//  Copyright © 2015 Krunoslav Zaher. All rights reserved.
//

#if os(iOS) || os(tvOS)

import UIKit
#if !RX_NO_MODULE
import RxSwift
#endif

extension UIBarButtonItem {
    
	/**
	Bindable sink for `enabled` property.
	*/
	public var rx_enabled: AnyObserver<Bool> {
		return AnyObserver { [weak self] event in
			MainScheduler.ensureExecutingOnScheduler()
			
			switch event {
			case .Next(let value):
				self?.enabled = value
			case .Error(let error):
				bindingErrorToInterface(error)
				break
			case .Completed:
				break
			}
		}
	}
	
    /**
    Reactive wrapper for target action pattern on `self`.
    */
    public var rx_tap: ControlEvent<Void> {
        if let target = self.target as? BarButtonItemTarget {
            return target.event
        }
        return BarButtonItemTarget(barButtonItem: self).event
    }
}


@objc
class BarButtonItemTarget: RxTarget {
    typealias Callback = () -> Void
    
    weak var barButtonItem: UIBarButtonItem?
    var callback: Callback!
    
    var event: ControlEvent<Void>!
    
    init(barButtonItem: UIBarButtonItem) {
        self.barButtonItem = barButtonItem
        super.init()
        barButtonItem.target = self
        barButtonItem.action = Selector("action:")
        
        self.event = ControlEvent(events:
            Observable.create { [weak self] observer in
                guard let target = self else {
                    observer.on(.Completed)
                    return NopDisposable.instance
                }
                target.callback = {
                    observer.on(.Next())
                }
                return target
            }.takeUntil(barButtonItem.rx_deallocated).share()
        )
    }

    override func dispose() {
        super.dispose()
#if DEBUG
        MainScheduler.ensureExecutingOnScheduler()
#endif

        barButtonItem?.target = nil
        barButtonItem?.action = nil
        
        callback = nil
        event = nil
    }
    
    func action(sender: AnyObject) {
        callback()
    }
}

#endif


Review comment (bug):
Hi @tarunon ,

I think you are correct, we do have a sharing issue here. Thnx for reporting this.

Now that I'm going over our target action code, we repeat this sharing bug in multiple places :(

UIKit extensions
- rx_value
- rx_controlEvent 

Cocoa extensions
- rx_value
- rx_controlEvent

... although it seems to me that Cocoa extensions at least use associated object to store observable sequence.

I believe that solution should be simple. 
- We need to make `rx_lazyInstanceObservable` more generic, and move it from (`NSControl` to private `NSObject` extension).
- Then use that `rx_lazyInstanceObservable` to fetch cached source or create it
- In this case it should be something like

```
public var rx_tap: ControlEvent<Void> {
        let source = rx_lazyInstanceObservable(&tapKey) { () -> Observable<Void> in
               Observable.create { [weak self] observer in

                guard let control = self else {
                    observer.on(.Completed)
                    return NopDisposable.instance
               }

                let target = BarButtonItemTarget(barButtonItem: control) {
                   observer.on(.Next())
               }
               return target
           }
           .takeUntil(rx_deallocated)
           .share()
        }

        return ControlEvent(events: source)
    }
```

The reason why I'm suggesting this solution is because if you create `BarButtonItemTarget` like you are suggesting, control.target would be set during calling `rx_tap` and not when observer subscribes, and in this way, we are keeping logic consistent across project.

I can ofc make all of those changes if you like or if you want to do them, that's also great.

Thnx for reporting this.


Code after:
//
//  UIBarButtonItem+Rx.swift
//  RxCocoa
//
//  Created by Daniel Tartaglia on 5/31/15.
//  Copyright © 2015 Krunoslav Zaher. All rights reserved.
//

#if os(iOS) || os(tvOS)

import UIKit
#if !RX_NO_MODULE
import RxSwift
#endif

var rx_tap_key: UInt8 = 0

extension UIBarButtonItem {
    
	/**
	Bindable sink for `enabled` property.
	*/
	public var rx_enabled: AnyObserver<Bool> {
		return AnyObserver { [weak self] event in
			MainScheduler.ensureExecutingOnScheduler()
			
			switch event {
			case .Next(let value):
				self?.enabled = value
			case .Error(let error):
				bindingErrorToInterface(error)
				break
			case .Completed:
				break
			}
		}
	}
	
    /**
    Reactive wrapper for target action pattern on `self`.
    */
    public var rx_tap: ControlEvent<Void> {
        let source = rx_lazyInstanceObservable(&rx_tap_key) { () -> Observable<Void> in
            Observable.create { [weak self] observer in
                guard let control = self else {
                    observer.on(.Completed)
                    return NopDisposable.instance
                }
                let target = BarButtonItemTarget(barButtonItem: control) {
                    observer.on(.Next())
                }
                return target
            }
            .takeUntil(self.rx_deallocated)
            .share()
        }
        
        return ControlEvent(events: source)
    }
}


@objc
class BarButtonItemTarget: RxTarget {
    typealias Callback = () -> Void
    
    weak var barButtonItem: UIBarButtonItem?
    var callback: Callback!
    
    init(barButtonItem: UIBarButtonItem, callback: () -> Void) {
        self.barButtonItem = barButtonItem
        self.callback = callback
        super.init()
        barButtonItem.target = self
        barButtonItem.action = Selector("action:")
    }
    
    override func dispose() {
        super.dispose()
#if DEBUG
        MainScheduler.ensureExecutingOnScheduler()
#endif
        
        barButtonItem?.target = nil
        barButtonItem?.action = nil
        
        callback = nil
    }
    
    func action(sender: AnyObject) {
        callback()
    }
    
}

#endif