swiftcombine

Tap publisher execute twice in Combine


I'd like not to use saveButton.addTarget(:selector:event) that's why I try to use tap publisher from CombineCocoa framework like this

 saveButton.tapPublisher.sink {  _ in
        print("tap") // tap twice
    }
    .store(in: &subscriptions)

and when I tap saveButton then 'tap' printed twice.

Also I try to use from Combine framework

saveButton.publisher(for: .touchUpInside) 

but have the same result.

My code here

class ArticleVC: UIViewController {
    let saveButton: UIButton = {
        let btn = UIButton()
        btn.setTitle("Save", for: .normal)
        return btn
    }()
    
    var subscriptions: Set<AnyCancellable> = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // add to view
        view.addSubview(saveButton)
        didTapSave()
        saveButton.snp.makeConstraints {
            $0.centerX.centerY.equalToSuperview()
        }
        
        didTapSave()
    }
    
    func didTapSave() {
//        saveButton.tapPublisher.sink(receiveValue: { _ in
//            print("tap")  // twice printed 'tap'
//        })
//        .store(in: &subscriptions)
        /// OR
        saveButton.publisher(for: .touchUpInside).sink { _ in
            print("tap") // twice printed 'tap'
        }
        .store(in: &subscriptions)
    }
}

What the reason for it?


Solution

  • My guess is that you are creating the pipeline twice. But you have not shown where you create the pipeline, so that is just a guess. [EDIT: Well after I posted this answer, you posted your code in the question, and my guess turned out to be obviously correct, as we can now see you creating the pipeline twice.]

    To support this, I'll show what I did:

    import UIKit
    import Combine
    import CombineCocoa
    
    class ViewController: UIViewController {
        @IBOutlet weak var button: UIButton!
        var storage = Set<AnyCancellable>()
        override func viewDidLoad() {
            super.viewDidLoad()
            button.tapPublisher
                .sink { _ in print("tap") }
                .store(in: &storage)
        }
    }
    

    That is all the code there is. I run the project, I tap the button, I see "tap" once. The end.