Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested repeatative loop in swift

I am trying to parse a nested iterative loop in swift I am getting the response from web service in the following format

{
    "categories": [{
        "name": "Default Category",
        "id": "default_category",
        "children": [{
            "uuid": "783f491fef5041438fb7a2c3bf6a3650",
            "name": "Accessories",
            "children": [{
                "uuid": "d21b4491ff784a9bae88de279b99fac3",
                "name": "All Accessories",
                "children": [{
                        "uuid": "2b1a23c4107844ad8a7afc1b324d0ffd",
                        "name": "Belts",
                        "children": [{
                                "uuid": "2b1a23c4107844ad8a7afc1b324d0ffd",
                                "name": "Belts",
                                "children": []

                            },
                            {
                                "uuid": "2b1a23c4107844ad8a7afc1b324d0ffd",
                                "name": "Belts",
                                "children": []
                            }
                        ]
                    },
                    {
                        "uuid": "a1c2a64c36c2461cad3d5f850e4fd0f5",
                        "name": "Hats",
                        "children": []
                    },
                    {
                        "uuid": "8f26bc764b8342feaa0cb7f3b96adcae",
                        "name": "Scarves",
                        "children": []
                    },
                    {
                        "uuid": "aa1116d1a0254ecea836cc6b32eeb9e0",
                        "name": "Sunglasses",
                        "children": []
                    },
                    {
                        "uuid": "9d7033233e8f47eaa69eb1aaf2e98cdd",
                        "name": "Watches",
                        "children": []
                    }
                ]
            }]
        }],
        "uuid": "6a23415771064e7aaad59f84f8113561"
    }]
}

Inside, the categories, there is 'children' key which in turn can contain another children and so on. I want to continuously loop inside the children key until the children key is empty and insert the last child into database.

Following is the code which i have done

     for currentCategory in mainCategories {


    // guard against if there are child categories
            guard var children = currentCategory.children, children.count > 0 else {
                //  Save the context
                self.coreData.saveStore()
                continue
            }                    
            for thisChildCategory in children {

                if thisChildCategory.children?.count > 0 {
                    for innerChildCategory in thisChildCategory.children! {
                        print("innerChildCategory name \(String(describing: innerChildCategory.name))")
                    }
                }

                if let child = thisChildCategory.children {
                    children = child
                }

                //  Create new object
                if let currentChildCategory  = self.coreData.insertNewObject(CoreDataEntities.BijouCategories.rawValue,
                    keyValues: ["id" : thisChildCategory.id! as Optional<AnyObject>,
                        "uuid" : thisChildCategory.uuid as Optional<AnyObject>,
                        "name" : thisChildCategory.name! as Optional<AnyObject>,
                        "gender" : thisChildCategory.gender as Optional<AnyObject>!,
                        "active" : NSNumber(value: false)]) as? BijouCategories {

                    //  Set as parent category
                    currentChildCategory.parentCategory = parentCategory


                    //  Save the context
                    self.coreData.saveStore()

                }
            }

        }

But this is not saving all the last child category in database.

like image 659
Rani Avatar asked Aug 03 '26 04:08

Rani


2 Answers

Swift 4

You should let Swift 4's codable do the work for you.

You can use the following class as a struct but I find using a class is better if you plan on editing the data.

class Categories: Codable {
     var categories: [CategoryItems]
}

class CategoryItems: Codable {
    var name: String?
    var id: String?
    var uuid: String?
    var children: [CategoryItems]?

required init(from decoder: Decoder) throws {
    var container = try decoder.container(keyedBy: CodingKeys.self)
    name = try container.decodeIfPresent(String.self, forKey: CodingKeys.name)
    id = try container.decodeIfPresent(String.self, forKey: CodingKeys.id)
    uuid = try container.decodeIfPresent(String.self, forKey: CodingKeys.uuid)
    children = try container.decodeIfPresent([CategoryItems].self, forKey: CodingKeys.children)
    if children != nil, children!.count == 0 {
        children = nil
    }
}

You can see here we add create the root level class "Categories" that has an array of CategoryItems. CategoryItems has all the possible values within it, but each item in the array may or may not have all of the possible values, hence they are optional. The important one is the children which is optional. Then in the required init we only se the optional values if the key value pair is available when decoding. I also set the children to nil if there are zero items, this is optional but helps when doing if statements later.

Then to decode your json using these codable classes you use the following code.

func decode(jsonData data: Data) {
    let decoder = JSONDecoder()
    do {
        let decoded = try decoder.decode(Categories.self, from: data)
    }
    catch let error as NSError {
        print("JSON Decode error = ", error)
    }
}

If you want to do a quick test to see if you got the deeping children level which I did you can simply run the following on the decoded variable.

for i in decoded.categories.first!.children!.first!.children!.first!.children!.first!.children! {
     print(i.name)
     print(i.uuid)
}
like image 198
Justin Miller Avatar answered Aug 05 '26 17:08

Justin Miller


With more than 2 nested levels a recursive function is recommended. recursive means the function calls itself.

Here is an simple example assuming jsonString is the given JSON in the question.

The function parseCategory passes the children array and the UUID string as parent identifier. The print line is the place to save the object in Core Data and of course you can pass the created Core Data object as parent as well to set the relationship.

func parseCategory(children: [[String:Any]], parent: String) {
    for child in children {
        print("Save in Core Data", child["name"] as! String, parent)
        let descendants = child["children"] as! [[String:Any]]
        parseCategory(children:descendants, parent: child["uuid"] as! String)
    }
}

let data = Data(jsonString.utf8)
do {
    let json = try JSONSerialization.jsonObject(with: data) as! [String:Any]
    parseCategory(children: json["categories"] as! [[String:Any]], parent: "")
} catch { print(error)}

The output is

"Save in Core Data Default Category 
Save in Core Data Accessories 6a23415771064e7aaad59f84f8113561
Save in Core Data All Accessories 783f491fef5041438fb7a2c3bf6a3650
Save in Core Data Belts d21b4491ff784a9bae88de279b99fac3
Save in Core Data Belts 2b1a23c4107844ad8a7afc1b324d0ffd
Save in Core Data Belts 2b1a23c4107844ad8a7afc1b324d0ffd
Save in Core Data Hats d21b4491ff784a9bae88de279b99fac3
Save in Core Data Scarves d21b4491ff784a9bae88de279b99fac3
Save in Core Data Sunglasses d21b4491ff784a9bae88de279b99fac3
Save in Core Data Watches d21b4491ff784a9bae88de279b99fac3"
like image 20
vadian Avatar answered Aug 05 '26 17:08

vadian



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!