Search code examples
iosswiftstructnsdictionarydecode

Decode NSDictionary with functions inside as values to swift object


any advices or suggestions will be appreciated! Thanks!

I have an issue to solve and it seems I can't find the answer anywhere. Some context: we are updating our ios app and the production version is written in obj-c, but new UI features we develop in Swift. Therefore we have a dictionary with some values for our Swift side of development which contains a lot of important data for the UI. As for now we are just looping the dict to fetch the values. I want to convert it to a swift object, so it would be easier to access the needed values. The problem is that the dictionary contains values that are function calls and UIImage type. What would be the smartest way to parse this dictionary to a swift object? The dictionary:

let dict : [String: Any] = [
    "keySelectedMenuButton" : 0,
    "keySettButtCallback"  : settTapCallback,
    "keyInfoButtCallback"  : logoTapCallback,
    "keyObjectMenuCallback": menuButtonTapCallback,
    "buttonListChanged"    : doneEditTapCallback,
    "keyObjectArray"      : [
        ["keyObjectTitle"    : "Home",
         //  "keyObjectLineImage": UIImage(named: "objektu atskyrimas_4x")!,
            "keyObjCtrlButtCollectionArray" : [
                
                ["keyCtrlItemTitle"      : "Smart Menu",
                 "keyCtrlItemSubtitle"   : "",
                 //"keyCtrlItemImage"      : UIImage(named: "ico_rocket")!,
                    "keyCtrlObjectAfterTap" : buttonTap1Callback
                ],
                ["keyCtrlItemTitle"      : "Lock",
                 "keyCtrlItemSubtitle"   : "doors",
                 //"keyCtrlItemImage"      : UIImage(named: "ico_lock_1@x3")!,
                    "keyCtrlObjectAfterTap" : buttonTapCallback
                ],
                ["keyCtrlItemTitle"      : "Unlock",
                 "keyCtrlItemSubtitle"   : "Gates",
                 // "keyCtrlItemImage"      : UIImage(named: "ico_lock_0@x3")!,
                    "keyCtrlObjectAfterTap" : buttonTapCallback
                ],
                ["keyCtrlItemTitle"      : "Ignore",
                 "keyCtrlItemSubtitle"   : "",
                 //  "keyCtrlItemImage"      : UIImage(named: "ico_shield@x3")!,
                    "keyCtrlObjectAfterTap" : buttonTap1Callback
                ],
                ["keyCtrlItemTitle"      : "Stop",
                 "keyCtrlItemSubtitle"   : "",
                 //  "keyCtrlItemImage"      : UIImage(named: "ico_stop")!
                    "keyCtrlObjectAfterTap" : buttonTap2Callback
                ],
                ["keyCtrlItemTitle"      : "State",
                 "keyCtrlItemSubtitle"   : "",
                 //   "keyCtrlItemImage"      : UIImage(named: "ico_query@x3")!,
                    "keyCtrlObjectAfterTap" : buttonTap3Callback
                ],
                ["keyCtrlItemTitle"      : "Controller",
                 "keyCtrlItemSubtitle"   : "",
                 //   "keyCtrlItemImage"      : UIImage(named: "ico_settings_lines")!,
                    "keyCtrlObjectAfterTap" : buttonTap1Callback
                ],
                ["keyCtrlItemTitle"      : "Logs",
                 "keyCtrlItemSubtitle"   : "",
                 //  "keyCtrlItemImage"      : UIImage(named: "ico_log@x3")!,
                    "keyCtrlObjectAfterTap" : buttonTap2Callback
                ],
                ["keyCtrlItemTitle"      : "Notifications",
                 "keyCtrlItemSubtitle"   : "",
                 //   "keyCtrlItemImage"      : UIImage(named: "ico_notifications")!,
                    "keyCtrlObjectAfterTap" : buttonTap3Callback
                ]]
        ],
        ["keyObjectTitle":"Auto",
         //"keyObjectLineImage": UIImage(named: "objektu atskyrimas_4x")!,
            "keyObjCtrlButtCollectionArray" : [
                ["keyCtrlItemTitle"      : "Smart Menu",
                 "keyCtrlItemSubtitle"   : "",
                 //  "keyCtrlItemImage"      : UIImage(named: "ico_rocket")!,
                    "keyCtrlObjectAfterTap" : buttonTap2Callback
                ],
                ["keyCtrlItemTitle"      : "Lock",
                 "keyCtrlItemSubtitle"   : "Backdoor",
                 //   "keyCtrlItemImage"      : UIImage(named: "ico_lock_1@x3")!,
                    "keyCtrlObjectAfterTap" : buttonTapCallback
                ],
                ["keyCtrlItemTitle"      : "Unlock",
                 "keyCtrlItemSubtitle"   : "Fence",
                 //    "keyCtrlItemImage"      : UIImage(named: "ico_lock_0@x3")!,
                    "keyCtrlObjectAfterTap" : buttonTapCallback
                ],
                ["keyCtrlItemTitle"      : "Ignore",
                 "keyCtrlItemSubtitle"   : "",
                 //    "keyCtrlItemImage"      : UIImage(named: "ico_shield@x3")!,
                    "keyCtrlObjectAfterTap" : buttonTap1Callback
                ],
                ["keyCtrlItemTitle"      : "Stop",
                 "keyCtrlItemSubtitle"   : "",
                 //   "keyCtrlItemImage"      : UIImage(named: "ico_stop")!,
                    "keyCtrlObjectAfterTap" : buttonTap3Callback
                ]]
        ],
        ["keyObjectTitle":"Basement",
         //   "keyObjectLineImage": UIImage(),
            "keyObjCtrlButtCollectionArray" : [
                ["keyCtrlItemTitle"      : "Smart Menu",
                 "keyCtrlItemSubtitle"   : "",
                 //    "keyCtrlItemImage"      : UIImage(named: "ico_rocket")!,
                    "keyCtrlObjectAfterTap" : buttonTap1Callback
                ],
                ["keyCtrlItemTitle"      : "Lock",
                 "keyCtrlItemSubtitle"   : "",
                 //      "keyCtrlItemImage"      : UIImage(named: "ico_lock_1@x3")!,
                    "keyCtrlObjectAfterTap" : buttonTapCallback
                ],
                ["keyCtrlItemTitle"      : "Unlock",
                 "keyCtrlItemSubtitle"   : "",
                 //         "keyCtrlItemImage"      : UIImage(named: "ico_lock_0@x3")!,
                    "keyCtrlObjectAfterTap" : buttonTapCallback
                ]]
        ]
    ]
]


func logoTapCallback(){
    print("logo tapped")

}

func menuButtonTapCallback() {
    print("menu button tapped ")
    
}

func buttonTapCallback(){
    print("button tapped")
    
}

func buttonTap1Callback(){
    print("button tapped 1")
}

func buttonTap2Callback(){
    print("button tapped 2")
}

func buttonTap3Callback(){
    print("button tapped 3")
}

func settTapCallback(){
    print("settTap")
}

func doneEditTapCallback(){
    print("doneEditTap")
}


// Dictionary structure I would like to use:

struct InitialDictionaryStructure {
    var keySelectedMenuButton            : Int
    var keySettButtCallback              : ()->Void
    var keyInfoButtCallback              : ()->Void
    var keyObjectMenuCallback            : ()->Void
    var buttonListChanged                : ()->Void
    var keyObjectArray                   : [[Object]]
    
}

struct Object {
    var keyObjectTitle                   : String
    //var keyObjectLineImage               : UIImage
    var keyObjCtrlButtCollectionArray    : [Button]
    
    
}

struct Button {
    var keyCtrlItemTitle                 : String
    var keyCtrlItemSubtitle              : String
    //var keyCtrlItemImage                 : UIImage
    var keyCtrlObjectAfterTap            : ()-> Void
}

Solution

  • There is no smart way, you have to add initializers.

    If the values are not going to be modified declare the struct members as constant and I shortened the member names slightly omitting redundant information.

    struct InitialDictionaryStructure {
        let menuButton            : Int
        let settButtCallback      : ()->Void
        let infoButtCallback      : ()->Void
        let objectMenuCallback    : ()->Void
        let buttonListChanged     : ()->Void
        let objects               : [Object]
        
        init(dictionary : [String:Any]) {
            menuButton = dictionary["keySelectedMenuButton"] as! Int
            settButtCallback = dictionary["keySettButtCallback"] as! ()-> Void
            infoButtCallback = dictionary["keyInfoButtCallback"] as! ()-> Void
            objectMenuCallback = dictionary["keyObjectMenuCallback"] as! ()-> Void
            buttonListChanged = dictionary["buttonListChanged"] as! ()-> Void
            let objectData = dictionary["keyObjectArray"] as! [[String:Any]]
            objects = objectData.map(Object.init)
        }
    }
    
    struct Object {
        let title                  : String
        // let lineImage           : UIImage
        let buttons                : [Button]
        
        init(dictionary : [String:Any]) {
            title = dictionary["keyObjectTitle"] as! String
            // lineImage = dictionary["keyObjectLineImage"] as! UIImage
            let buttonData = dictionary["keyObjCtrlButtCollectionArray"] as! [[String:Any]]
            buttons = buttonData.map(Button.init)
        }
    }
    
    struct Button {
        let title                 : String
        let subtitle              : String
        // let image                 : UIImage
        let objectAfterTap        : ()-> Void
        
        init(dictionary : [String:Any]) {
            title = dictionary["keyCtrlItemTitle"] as! String
            subtitle = dictionary["keyCtrlItemSubtitle"] as! String
            // image = dictionary["keyCtrlItemImage"] as! UIImage
            objectAfterTap = dictionary["keyCtrlObjectAfterTap"] as! ()-> Void
        }
    }
    

    Please consider that this is a simple example without any type and nil checks. If the dictionaries don't contain all keys with the proper types the code will crash.

    To create an item write

    let initialDictionaryStructure = InitialDictionaryStructure(dictionary: dict)
    print(initialDictionaryStructure)