Dynamically Generate UITableView Cells and Headrs
Asked Answered
M

3

7

Project files:

https://jumpshare.com/v/Otai3BBXYwfvyz8jb53k

(Would be wise to view these to see structure of project)

Problem:

Ok, so i'm following a tutorial that creates a UITableView with headers and then cell content.

The code worked and runs fine, Now I want to extend beyond that tutorial and dynamically load that content using alamofire and SwiftyJSON.

In the tutorial, the code used is like so:

func getSectionsFromData() -> [Sections] {
    
    var sectionsArray = [Sections]()
    
    let animals = Sections(title: "Animals", objects: ["Cats", "Dogs", "Birds", "Lions"])


    sectionsArray.append(animals)

    return sectionsArray


}

What I tried to do was:

Alamofire.request(.GET, url).validate().responseJSON { response in
        switch response.result {
        case .Success:
            if let value = response.result.value {
                let json = JSON(value)
                
                for (_, subJson) in json {
                    for (year, content) in subJson {
                        let title = year
                        let objects = content
                        
                        sectionsArray.append(Sections(title: title, objects: objects))
                       
                    }
                    
                }
            }
        case .Failure(let error):
            print(error)
        }
    }

If I print out the results they show in the console - so I know the getting and looping of the JSON works. I then added in

let title = year
let objects = content
                        
sectionsArray.append(Sections(title: title, objects: objects))

But on this line:

sectionsArray.append(Sections(title: title, objects: objects))

I get this error:

cannot convert value of type 'JSON' to expected argument type '[String]'

Here is the JSON I am using:

 {"posts": [
 {
    "Category1": [
        "Post1cat1"
    ],
    "Category2": [
        "Post1cat2",
        "Post2cat2"
    ]
 }
 ]}

Can someone help me? I might be going in the wrong direction here I want to loop through the JSON and display the categories as headers and the posts in a cell of a table.

edit: 1/29/2016

so, I changed the loop to:

for (_, subJson) in json {
                for (index, data) in subJson {
                    for (title, objects) in data {
                        sectionsArray.append(Sections(title: title, objects: objects.self.arrayValue.map { $0.string!}))


                    }

                }

            }

Still no luck. When I add in some prints (under: sectionsArray.append) to test if there is data:

print("--")
print(title)
print(objects.self.arrayValue.map { $0.string!})
print(Sections(title: title, objects: objects.self.arrayValue.map { $0.string!}))

I get this result in the console:

--

Category1

["Post1cat1"]

Sections(headings: "Category1", items: ["Post1cat1"])

--

Category2

["Post1cat2", "Post2cat2"]

Sections(headings: "Category2", items: ["Post1cat2", "Post2cat2"])

Which shows that the information is there, however when I run the app there are still no results form he JSON just the originally defined section and cells above.

Mauretta answered 29/1, 2016 at 1:26 Comment(0)
M
3

In second parsing method (after edit), you're iterating on array in last loop, so either you could create array there and add each element separately, as in example:

for (title, data) in subJson {
    var elements: [String] = []

    for (_, object) in data {
        if let stringELement = object.rawString() {
            elements.append(stringELement)
        }
    }

    sectionsArray.append(Sections(title: title, objects: elements))
}

or if you prefer you can use casted raw array from JSON object as in this example:

for (_, subJson) in json["posts"] {
    for (title, data) in subJson {
        let optionalCastedObjects = data.arrayObject as? [String]
        let unwrappedObjects = optionalCastedObjects ?? []
        let section = Sections(title: title, objects: unwrappedObjects)

        sectionsArray.append(section)                        
    }
}

That should fix mentioned compilation issue.

But in the end remember that you're using async callback (in your GET request) in synchronous getSectionsFromData method. And you're always will return array before the values from that callback (clojure) will append new data. That will cause, that you're never display the data that you fetched that way.

UPDATE

To do that you should refactor your getSectionsFromData method as below.

func getSectionsFromData(completion: ([Sections]) -> ()) {
    var sectionsArray = [Sections]()

    Alamofire.request(.GET, url).validate().responseJSON { response in
        switch response.result {
        case .Success:
            if let value = response.result.value {
                let json = JSON(value)

                for (_, subJson) in json["posts"] {
                    for (title, data) in subJson {
                        let optionalCastedObjects = data.arrayObject as? [String]
                        let unwrappedObjects = optionalCastedObjects ?? []
                        let section = Sections(title: title, objects: unwrappedObjects)

                        sectionsArray.append(section)
                    }
                }

                completion(sectionsArray)
            }
        case .Failure(let error):
            print(error)
        }
    }
}

And relevant parts in your UITableViewController class.

var sections: [Sections] = []

override func viewDidLoad() {
    super.viewDidLoad()

    SectionsData().getSectionsFromData { [weak self](sections: [Sections]) -> () in
        self?.sections = sections
        self?.tableView.reloadData()
    }
}
Marra answered 31/1, 2016 at 17:17 Comment(2)
The issue wasnt with the parsing, that was working fine. the issue was getting them to display in he tableview. it's not updating.Mauretta
Please, try also the part after UPDATE headline. Works for me :)Lipson
S
1

Your second loop is on the array object hence in that loop year is index value and content is the object at that index.

You need to implement an additional loop to fix the problem i.e:

for (_, subJson) in json {
    for (index, data) in subJson {
        for (title, objects) in data {
            sectionsArray.append(Sections(title: title, objects: objects.arrayValue.map { $0.string!}))

        }

    }

}
Sensibility answered 29/1, 2016 at 14:31 Comment(3)
So, I added that in. Nothing happens. Out of all the loops when I print (sectionsArray) I get: [sections.Sections(headings: "Animals", items: ["Cats", "Dogs", "Birds", "Lions"])] no new itemsMauretta
Did you work on the project files? could you put the file up for download?Mauretta
Also, When I change your sections.array(...) to a basic one: sectionsArray.append(Sections(title: "test", objects: ["one","two"])) with hard coded values, it also doesn't work. :SMauretta
B
-1

From the SwiftyJSON documentation:

for (key,subJson):(String, JSON) in json {
   //Do something you want
}

This indicates that subJson is of type JSON. However, your Sections constructor in the first example is:

Sections(title: "Animals", objects: ["Cats", "Dogs", "Birds", "Lions"])

In your second example, you're calling it as:

Sections(title: title, objects: objects)

Unless you have changed the constructor, it's expecting objects to be an array of strings, not JSON. This is why you're getting an error saying Swift can't convert JSON to String. In your case, the objects JSON is actually an array of strings, so you need to use something like:

Sections(title: title, objects: objects.arrayValue.map { $0.string!})
Brucite answered 29/1, 2016 at 3:30 Comment(3)
soo I tried: sectionsArray.append(Sections(title: title, objects: objects.arrayValue.map { $0.string!})) and ran the simulator - all that showed was the animals cells not any of the new items, there are no errorsMauretta
I have added the project files if that helps.Mauretta
I'm at work and that site is blocked. I'll take a look when I get home. Have you used the debugger to step through the code and see how often the append is called?Brucite

© 2022 - 2024 — McMap. All rights reserved.