BHendricks
BHendricks

Reputation: 4493

Swift array of closures, 'Int' is not subtype of '()'

I have an array of closures, within a class (I'm declaring this inside a UIViewController that is a table view), and want to set actions for one of my cells based on a closure. This is my code:

        var actionItem : (Int)->Void = {
            (index: Int)->Void in
            if(self.pickedRoles[index] == "___") {
                self.pickedRoles[index] = self.roles[index];
            } else {
                self.pickedRoles[index] = "___";
            }
        }

        var roleActions : Array<(Int)->Void> = [{
                actionItem(0);
            }, {
                actionItem(1);
            }, {
                actionItem(2);
            }, {
                actionItem(3);
            }, {
                actionItem(4);
        }];

actionItem is my closure, pickedRoles is a class variable which is an array of Strings, similarly with roles. I want roleActions to represent what happens when a user selects a role, but at the line declaring roleActions, I get an error stating:

'Int' is not a subtype of '()'

What do I do to solve this?

Upvotes: 0

Views: 229

Answers (3)

matt
matt

Reputation: 535304

Well, think about it:

  • actionItem is a function that takes an Int and returns Void.

  • actionItem(0) is a call to that function, so it is a Void.

  • So {actionItem(0)} is an anonymous function (a closure, as you call it) that takes Void and returns Void.

Well, you are trying to put that into an array of (Int)->Void. Clearly that's a typological mismatch: an Int input is not a Void input. And that's exactly what the compiler is telling you.

Frankly I don't see what any of this has to do with closures anyway! Your goal, as you have agree in a comment, is: "I've got 5 buttons. I want the first button, when tapped, to toggle the first item in an array, the second button to toggle the second item in an array, and so on."

So what I would do is: I would simply attach tags to each button - say, 100, 101, 102, 103, 104. I would give them each the same action method. When that method is called, we subtract 100 from the tag of the sender. Now we have the index! Now we toggle the value of that index of the array.

func doButton(sender:UIView) {
    let index = view.tag - 100
    if(self.pickedRoles[index] == "___") {
        self.pickedRoles[index] = self.roles[index];
    } else {
        self.pickedRoles[index] = "___";
    }
}

Upvotes: 3

BHendricks
BHendricks

Reputation: 4493

As @matt and @Antonio suggested, my initial approach was faulty, but to get the desired behavior in the end, I had to go a step further than either of their solutions.

To accomplish what @matt commented, which was my goal (to have 5 buttons where each button toggles an element in the array), I had to go with the following code:

         var actionItem : ((Int)->(Void->Void)) = {
            (index: Int) in
            return {
                if(self.pickedRoles[index] == "___") {
                    self.pickedRoles[index] = self.roles[index];
                } else {
                    self.pickedRoles[index] = "___";
                }
            };
        }

        var roleActions : [Void->Void] = [actionItem(0), actionItem(1), actionItem(2), actionItem(3), actionItem(4)];

What this gives me is an array of closures, where each closure can be called with no parameters to simply toggle it's respective element in the pickedRoles array.

Upvotes: 0

Antonio
Antonio

Reputation: 72760

There are 2 possible ways to fix the compilation error, but I don't know which one is the correct one, depending in what you are trying to do.

The array of closures is filled in with parameterless closures:

{ Void -> Void in actionItem(0) }

So the array declaration is incorrect, the contained type should be Void -> Void - the fixed code is:

var roleActions : Array<Void -> Void> = [{
        actionItem(0);
    }, {
        actionItem(1);
    }, {
        actionItem(2);
    }, {
        actionItem(3);
    }, {
        actionItem(4);
}]

Alternatively, if the type of the element contained in the array is correct, basing on your app logic, then you just have to skip the integer parameter that's passed to each closure, using _ in:

var roleActions : Array<Int -> Void> = [{
        _ in actionItem(0);
    }, {
        _ in actionItem(1);
    }, {
        _ in actionItem(2);
    }, {
        _ in actionItem(3);
    }, {
        _ in actionItem(4);
}]

Upvotes: 1

Related Questions