Manuel
Manuel

Reputation: 15082

How to pass closure with argument as argument and execute it?

Initializer of class A takes an optional closure as argument:

class A {
   var closure: ()?

   init(closure: closure()?) {
      self.closure = closure
      self.closure()
   }
}

I want to pass a function with an argument as the closure:

class B {
    let a = A(closure: action(1)) // This throws the error: Cannot convert value of type '()' to expected argument type '(() -> Void)?'

    func action(_ i: Int) {
       //...
    }
}

Class A should execute the closure action with argument i.

I am not sure about how to write this correctly, see error in code comment above. What has to be changed?

Upvotes: 8

Views: 13534

Answers (4)

OOPer
OOPer

Reputation: 47906

Please make your "what-you-have-now" code error free.

Assuming your class A like this:

class A {
    typealias ClosureType = ()->Void

    var closure: ClosureType?

    init(closure: ClosureType?) {
        self.closure = closure
        //`closure` would be used later.
    }

    //To use the closure in class A
    func someMethod() {
        //call the closure
        self.closure?()
    }
}

With A given above, you need to rewrite your class B as:

class B {
    private(set) var a: A!
    init() {
        //initialize all instance properties till here
        a = A(closure: {[weak self] in self?.action(1)})
    }

    func action(i: Int) {
        //...
    }
}

Upvotes: 7

Matthew Seaman
Matthew Seaman

Reputation: 8092

Types as Parameters

In Swift, every object has a type. For example, Int, String, etc. are likely all types you are extremely familiar with.

So when you declare a function, the explicit type (or sometimes protocols) of any parameters should be specified.

func swallowInt(number: Int) {}

Compound Types

Swift also has a concept of compound types. One example of this is Tuples. A Tuple is just a collection of other types.

let httpStatusCode: (Int, String) = (404, "Not Found")

A function could easily take a tuple as its argument:

func swallowStatusCode(statusCode: (Int, String)) {}

Another compound type is the function type. A function type consists of a tuple of parameters and a return type. So the swallowInt function from above would have the following function type: (Int) -> Void. Similarly, a function taking in an Int and a String and returning a Bool would have the following type: (Int, String) -> Bool.

Function Types As Parameters

So we can use these concepts to re-write function A:

class A {
    var closure: (() -> Void)?

    init(closure: (() -> Void)?) {
        self.closure = closure
        self.closure()
    }
}

Passing an argument would then just be:

func foo(closure: (Int) -> Void) {
    // Execute the closure
    closure(1)
}

Upvotes: 3

ZGski
ZGski

Reputation: 2548

One way to do what I think you're attempting is with the following code:

class ViewController: UIViewController {

    override func viewDidLoad() {
        let _  = A.init(){Void in self.action(2)}
    }

    func action(i: Int) {
        print(i)
    }
}


class A: NSObject {
    var closure : ()?

    init(closure: (()->Void)? = nil) {
        // Notice how this is executed before the  closure
        print("1")
        // Make sure closure isn't nil
        self.closure = closure?()
    }
}

Upvotes: 2

matt
matt

Reputation: 536028

The problem is that closure()? is not a type. And ()? is a type, but it is probably not the type you want.

If you want var closure to have as its value a certain kind of function, you need to use the type of that function in the declaration, e.g.

var closure: (Int) -> Void

Similarly, if you want init(closure:) to take as its parameter a certain kind of function, you need to use the type of that function in the declaration, e.g.

init(closure: (Int) -> Void) {

Upvotes: 7

Related Questions