Search code examples
iosswiftdictionaryuikitclosures

Putting a closure into a dictionary


In an iOS app I have a popup button that contains a list of item names that can change. I am trying to make a dictionary of [String : (String)->Void] to store different actions to be performed based on the selected item.

The below function gives me the following error:

Cannot assign value of type '()' to subscript of type '(String) -> Void'

Isn't my closure of type (String)->Void, or have I declared it incorrectly?

func selectOption(action : UIAction) {
    var actionDict : [String : (String)->Void] = [:]
    var assetNames : [String] = []
    assetNames = assets.map { asset in
        return asset.components(separatedBy: "@@@")[0]
    }
    var logAction = { (assetName: String) in
        print(assetName)
    }
    

    assetNames.forEach { an in
        actionDict[an] = logAction(an)
    }
}

I have a big gap in my understanding. If I change my declaration of actionDict to be () then the code compiles but it just executes the closure when I run it and it hits the line actionDict[an] = logAction(an), it just runs the closure rather than assigning it to the dictionary:

func selectOption(action : UIAction) {
    var actionDict : [String : ()] = [:]
    var assetNames : [String] = []
    assetNames = assets.map { asset in
        return asset.components(separatedBy: "@@@")[0]
    }
    var logAction : (String)->() = { (assetName: String)->() in
        print(assetName)
    }
    assetNames.forEach { an in
        actionDict[an] = logAction(an)
    }
    
    actionDict[action.title]
}

Solution

  • var assets: [String] = ["abc@@@XYZ", "mno@@@PQR@@@123", "xyz", "$#$#$"]
    
    func selectOption(action : UIAction) {
        var actionDict : [String : ((String)->())] = [:]
        var assetNames : [String] = assets.map { $0.components(separatedBy: "@@@")[0] }
    
        var logAction : ((String)->()) = { assetName in
            print(assetName)
        }
        
        assetNames.forEach { an in
            // only logAction(an) will triggle/call stored closure 
                   
            // Instead of that, you can create new closure with parameter
            actionDict[an] = { str in logAction(str) }
        }
    }
    

    now it depends on when calling actionDict["abc"]("xyz").
    what you expected?


    1. If it should print "abc", then:

    Initialization:
    var actionDict : [String : (()->())] = [:]
    setup:
    actionDict[an] = { logAction(an) }
    call:
    actionDict["abc"]()


    1. If it should print "xyz", then:

    A dictionary with the same type of closure (with a String parameter) in every value is meaningless.


    1. It works if it varies by different functions.

    actionDict["print"]("xyz"), actionDict["dump"]("abc")
    actionDict["typecast"]("mno"), actionDict["split"]("$#$#$")

    So this third one is what you're looking for!