Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Store a Swift function and its parameter values and call it later

Tags:

swift

There are multiple functions in my app, where each have a different number of parameters

func functionOne(foo: Foo, bar: Bar)

func functionTwo(foo: Foo, bar: Bar, baz: Baz, quux: Quux)

func functionThree(foo: Foo)

The parameter values can vary.

My requirement is to press a button which will run whichever function of the above was run most recently, including its parameter values.

Storing the whole thing (function and parameters) in a variable did not work.

like image 768
chuckbass Avatar asked Jan 09 '19 20:01

chuckbass


2 Answers

A function and its parameters are stored in a closure. For example:

func f(_ x: Int) {}
func g(_ x: Int, _ y: Int) {}

var saved: () -> Void = { f(1) }

saved() // this executes f(1)

saved = { g(2, 3) }

saved() // now this executes g(2, 3)
like image 85
Rob Napier Avatar answered Nov 15 '22 07:11

Rob Napier


You can use @escaping and @autoclosure to store a function and its parameters as a closure in a property of your class and then call it.

Add this class to your project:

// Stored Function Class
class SFC {
    static var calledFunc: (() -> Void)?

    static func call(_ function: @escaping @autoclosure () -> Void) {
        // Store the function
        calledFunc = function

        // Call it
        function()
    }

    static func reCall() {
        // Called the stored function
        calledFunc?()
    }

    // Call this when you no longer want SFC to hold onto your function.
    // Your class will not deallocate if you passed in `self` to `call()`
    // as long as `calledFunc` retains your function.  Setting it to `nil`
    // frees it.
    static func forget() {
        calledFunc = nil
    }
}

This is how you use it:

Wrap any function call that you want to repeat with SFC.call(). Call SFC.reCall() to call that function again.

Example:

func add(_ a: Int, _ b: Int) {
    print("\(a) + \(b) = \(a + b)")
}

SFC.call(print("hello", "bye"))
SFC.reCall()
SFC.reCall()
SFC.call(add(2, 3))
SFC.reCall()

Output:

hello bye
hello bye
hello bye
2 + 3 = 5
2 + 3 = 5

How does this work?

The contents of the call to call() are automatically wrapped in a closure (that is what @autoclosure does) and passed as function. The @escaping means that you'll be hanging onto that closure after call() returns.

That closure is then assigned to the calledFunc property so that it can be called again later from reCall().

Note: If the function you are passing to call() is a member function, you'll need to explicitly specify self. For example: SFC.call(self.functionThree(foo: fooVar)). Be sure to call SFC.forget() when it's time for your class to be freed so that SFC doesn't hold onto your class instance.

like image 45
vacawama Avatar answered Nov 15 '22 07:11

vacawama