Search code examples
arraysswiftfunctionswift2function-parameter

How do I make a function that takes any function that's defined in a Protocol as its parameter?


I'm working on a card game and would like to make a function that will loop through my array of Players and perform some function for each player, so I do not have tons and tons of

for var player in Players {
    player.doSomeFunction()
}

All over my code. Instead, I'd like something like the below:

func everyone(doThis: func) {
    for var player in Players {
        player.doThis(<params, etc…>)
    }
}

So that I can call Players.everyone(doThis(params, etc…)) in other places of the code instead of having to loop through the players each and every time I need to make all players do something.

How do I make a function that takes any function that's defined in a Protocol as its parameter? The trouble I'm having is that it seems that when you use functions as parameters in Swift, you have to define the parameters and return type of the function parameter in the function declaration. Alternately, is there a built-in way to call Players.forAllElements(doThisFunction) (where Players is an Array<Player>)?

In case it helps, I have two Classes that conform to the Player protocol: ComputerPlayer and HumanPlayer.


Solution

  • There seems to be no way to express any-function-in-a-protocol in swift, as to use for a sort of definition of parameter type.

    However, with currying and function-types you can do something that may be similar: . You can target all functions with the same signature:

    class Player {
        var name: String
        init(_ name: String) {
            self.name = name
        }
    
        // Both actions, even if they have different parameter names,
        // have the same function signature
        func actionOne(foo: String, count:Int) {
            print("actionOne:\(name):\(foo):\(count)")
        }
    
        func actionTwo(bar: String, amount: Int) {
            print("actionTwo:\(name):\(bar):\(amount)")
        }
    }
    
    let players = [Player("Primero"), Player("Segundo")]
    
    // A function type that receives a Player object, and returns a
    // function with the `String, Int` signature
    typealias PlayerActionType = (Player) -> (String, Int) -> ()
    
    // This is the function that takes the curried function, and can
    // run any function sharing the same signature
    func performAction(curriedFunction: PlayerActionType) {
        for currentPlayer in players {
            let actionFunction = curriedFunction(currentPlayer)
            actionFunction("string", 3)
        }
    }
    
    performAction(Player.actionOne)
    performAction(Player.actionTwo)
    
    // Prints:
    // actionOne:Primero:string:3
    // actionOne:Segundo:string:3
    // actionTwo:Primero:string:3
    // actionTwo:Segundo:string:3