Search code examples
iosswiftuiswiftui-navigationlink

Navigation using multi dimensional dictionary in swiftui


I am new to swift and find some problems in doing this: Let's say I have a multiDim dict ["A": ["Sub1_A1", ["Sub1_A2": ["Sub2_A1", "Sub2_A2"]], "B": ["Sub1_B1"]]. I wish to build a navigation list by using such dictionary, also together with a tabview showing which dictionary key I have chosen in the navigation view. For example:

1.

2. Then, once I have chosen women's clothing, I will be in this navigation page:

3. And if I chose skirt, I will be navigated to this page, will be the last page because it's the end of the multi-dim dictionary using the keys "Women", "Skirt", hence no navigation link is shown but just a list of Text() or Button(). And finally, if I tab on "Mini Skirt", I will be directed to another view showing the details of sequence of category keys I have chosen.

The tab name(s) that appear will be according to the category you chose. If you navigate backward from "Skirt" to "Women", the tab name "Skirt" will then disappear, showing just "Women" and a placeholder tag name "Please Choose".

Any idea how to do this?

Here's my initial attempt:

struct PlayingWithMultiDimNavLink: View {

//    let multiDimDict:[String:Any] = ["A": ["Sub1_A1", "Sub1_A1"], "B": ["Sub1_B1", "Sub1_B1"]]

    let multiDimDict = ["A": "1", "B": "2"]


    var body: some View {

        NavigationView{
            List {
                ForEach(multiDimDict.keys.sorted(), id: \.self) { key in

                    NavigationLink(destination: GenerateChildView(key: key, dict: self.multiDimDict)){

                        Text(key)
                    }

                }
            }
        }

    }
}





struct GenerateChildView: View {

var key: String
var dict: [String: Any]

init(key: String, dict: [String: Any] ){
    self.key = key
    self.dict = dict
}


var body: some View {
    VStack{// Unable to infer complex closure return type; add explicit type to disambiguate
        if isString(key:key, dict: dict){
            List{
                HStack{
                    Text(String(describing: dict[key]))
                }

            }
        }

        if isStringList(key:key, dict: dict){

            List{
                ForEach(dict[key], id: \.self){ content in
                    HStack{
                        Text(content)
                    }

                }

            }

        }



        else{

            Text("Bla Bla Bla")
        }

    }
}




func isStringList(key: String, dict: [String: Any])->Bool{
    if let stringList = dict[key] as? [String]{
        print(stringList)
        return true
    }
    else{
        return false
    }

}



func isString(key: String, dict: [String: Any])-> Bool{
    if let string = dict[key] as? String{
        print(string)
        return true
    }
    else{
        return false
    }
}

}

I declare multiDimDict:[String:Any] as multiDimDict because the dictionary can be a nested dictionary of any depth level.

However this won't compile, there's an error "Unable to infer complex closure return type; add explicit type to disambiguate" in generateChildView(). Any ideas what to do here?


Solution

  • SwiftUI does not work with Any type, so as soon as you detect your data type you have to cast it to let SwiftUI know how to proceed.

    Here is a fixed part of original code. Tested with Xcode 11.4 / iOS 13.4

    if isStringList(key:key, dict: dict) {
        List{
            // verified detected [String], so cast it explicitly to 
            // give chance to check types here
            ForEach(dict[key] as! [String], id: \.self) { content in
                HStack{
                    Text(content)
                }
            }
        }
    }