neo
neo

Reputation: 1404

How to pass closure as a variable?

I have following code from playground:

class MyButton {
    var pressed: (() -> Void)?
    
    func press() {
        pressed?()
    }
}

class SomeView {
    var buttonDidPressed: (() -> Void)?
    
    let button = MyButton()
    
    init() {
        button.pressed = buttonDidPressed
    }
    
    func press() {
        button.press()
    }
}

let view = SomeView()

view.buttonDidPressed = {
    print("pressed")
}

view.press()

MyButton has closure pressed which is executed when press is called. In SomeViews init, I assign value to that pressed closure. Of course, at the init, it is nil. But after initialization of SomeView, I assign value to that buttonDidPressed closure. Because, closures are reference type, when view.press is called, buttonDidPressed should not be nil. But, anyway, my print is not executed. Why?

Upvotes: 3

Views: 354

Answers (3)

New Dev
New Dev

Reputation: 49610

Here's what I think you want to achieve:

class SomeView {
    var buttonDidPressed: (() -> Void)? = nil {
       didSet {
          button.pressed = buttonDidPressed
       }
    }
    
    let button = MyButton()
    
    func press() {
        button.press()
    }
}

The reason your code isn't working is because in the SomeView.init, buttonDidPressed is nil - i.e. it's a lack of reference. So:

buttonDidPressed = nil
button.pressed = buttonDidPressed 
// is equivalent to
button.pressed = nil

Assigning a new (or different) reference to buttonDidPressed later doesn't change the button.pressed property.

Upvotes: 1

gcharita
gcharita

Reputation: 8347

You first initialize SomeView (the init function gets called) and then you set the buttonDidPressed property. So, the buttonDidPressed property has your closure but never passed it to MyButton instance.

You can use didSet to update it immediately:

class SomeView {
    var buttonDidPressed: (() -> Void)? {
        didSet {
            button.pressed = buttonDidPressed
        }
    }
    
    let button = MyButton()
    
    func press() {
        button.press()
    }
}

Update: You can also pass your closure in the initializer of SomeView like this:

class SomeView {
    let button = MyButton()
    
    init(buttonDidPressed: (() -> Void)?) {
        button.pressed = buttonDidPressed
    }
    
    func press() {
        button.press()
    }
}

let view = SomeView {
    print("pressed")
}

view.press()

Upvotes: 2

roadRunner
roadRunner

Reputation: 201

Actually it is better to explain with this issue by using binding, Let`s say :

class Sekoya<T> {
typealias Listener = (T) -> Void
var listenerHolder: Listener?
var value: T {
    didSet {
        listenerHolder?(value) // when value change listenerHolder triger
    }
}

init(_ value: T) {
    self.value = value
}

func bind(listener: Listener?) {
    
    self.listenerHolder = listener // referencing listener my closure variable in SomeView Class
    listener?(value)
    
}
}

Lets use this binding at the another class:

 let sekoyaInt = Sekoya(25)
    sekoyaInt.bind {
            print("value changed: \($0)")
        }

Whenever you change the value your reference closure will be trigger and value will be change and it prints value

Upvotes: 1

Related Questions