Guillaume
Guillaume

Reputation: 191

How can I call a function with a dictionary?

I try to create a Dictionary to call some functions.

My array takes a key Int, a String and a function, like this:

let list_weapons: [Int: [Any]]  = [1: ["Sword", attack_sword],
                                   2: ["Magic wand", heal_magic_wand],
                                   3: ["Hammer", attack_hammer],
                                   4: ["Axe", attack_axe]]

These functions take a Class as a parameter, like this:

func attack_sword(character: Character)

I try to call my function like that but it doesn't work.

list_weapons[1]![1](character: Character) 

Cannot call value of non-function type 'Any'

If you have some ideas or advise me with another container

Thank you

Upvotes: 2

Views: 76

Answers (4)

Yury Imashev
Yury Imashev

Reputation: 2128

You don't need to store methods in a dictionary to solve your problem. In my opinion, an architecture solution would be better here. You can play with selectors if you like, but I guess things will be much easier if you just do something like that

// Here you can specify all common thing about your weapons
protocol Weapon: class {
    var name: String { get }
    func attack(character: Character)
}

// Each weapon has its own class which can contain eveything you need
class Sword: Weapon { }
class MagicWand: Weapon { }
class Hammer: Weapon { }
class Axe: Weapon { }

// That's how you can store you weapons list
// You can use a dictionary if you like
let weaponsArray: [Weapon]  = [Sword(),
                               MagicWand(),
                               Hammer(),
                               Axe()]

// And that's how you can use them
weaponsArray[0].attack(character: character)

Upvotes: 2

Code Different
Code Different

Reputation: 93161

You can cast the element to a function type:

if let attack = list_weapons[1]![1] as? ((Character) -> Void) {
    attack(...)
} else {
    print("Cannot attack")
}

But you better rework your data model. Using dictionaries can get confusing very quickly. Here's one way to do it:

protocol Weapon {
    var name: String { get }
    func attack(character: Character)
}

struct Sword: Weapon {
    let name = "Sword"
    func attack(character: Character) { ... }
}

struct MagicWand: Weapon {
    let name = "Magic Wand"
    func attack(character: Character) { ... }
}

struct Hammer: Weapon {
    let name = "Hammer"
    func attack(character: Character) { ... }
}

struct Axe: Weapon {
    let name = "Axe"
    func attack(character: Character) { ... }
}

let weapons: [Weapon] = [Sword(), MagicWand(), Hammer(), Axe()]
weapons[1].attack(character: ...)

You extend each weapon with different damages, unique features, levels, etc.

Upvotes: 0

rmaddy
rmaddy

Reputation: 318814

Using an array of Any to store a string and a closure (function) with a specific signature is a poor choice. It would be better to declare a struct with two properties of the correct type. Then store those structures in your dictionary.

struct Action {
    let name: String
    let function: (Character) -> ()
}

let list_weapons: [Int: Action] = [
    1: Action(name: "Sword", function: attack_sword),
    // and the rest
]

list_weapons[1]!.function(someCharacter)

Upvotes: 0

Dinesh Balasubramanian
Dinesh Balasubramanian

Reputation: 21728

You have to cast Any to the Function before using it.

let f = (list_weapons[1]![1] as! (Character) -> Void)
f("a") //it will invoke attack_sword("a")

And without temp variable,

(list_weapons[1]![1] as! (Character) -> Void)("a")

Upvotes: 0

Related Questions