Show JSON as TableView Section and TableView row in Swift 4.2 without Decodable?

后端 未结 2 2006
南旧
南旧 2021-01-27 09:06

I have below JSON response array with key \"events\"

{
  \"events\": [
 {
  \"name\": \"event foo\",
  \"date\": \"2020-05-22\",
  \"time\": \"7:00\",
  \"am_or_         


        
2条回答
  •  北荒
    北荒 (楼主)
    2021-01-27 09:46

    First things first, you'll need to create a Model, so that you can transform your JSON into usable objects. To do that, good practice is to use the Codable protocol, which will automatically map your JSON keys to a struct/class variable

    struct Event: Codable {
        var name: String!
        var date: String!
        var time: String!
        var am_or_pm: String!
        var day: String!
        var description: String!
    }
    
    struct Events: Codable {
        var events: [Event]!
    }
    

    Now that you have the model that will be generated from the JSON, you need to decode your JSON into your model. There are plenty of ways to do it, I like to use JSONEncoder/JSONDecoder. So, let's say your JSON string is stored in the variable myJsonString, you would need to

    if let jsonData = myJsonString.data(using: .utf8) {
        do {
            let events = try JSONDecoder().decode(Events.self, from: jsonData)
        } catch {
            print("Error decoding json!", error.localizedDescription)
        }
    } else {
        print("Failed to get bytes from string!")
    }
    

    We can, now turn that code block into a function that returns Events, or nil, if it fails to decode the string.

    func getEvents(from jsonString: String) -> Events? {
        guard let jsonData = myJsonString.data(using: .utf8) else { return nil }
        return try? JSONDecoder().decode(Events.self, from: jsonData)
    }
    

    Cool! We can easily get our events based on a JSON string now! All we gotta do next is populate the tableView! To do that, we can create a few functions in our Events struct that will return just what we need to populate our section. The first function will return the sections for our tableView, and the second will return all items for a given section. Let's modify the Events struct

    struct Events: Codable {
        var events: [Event]!
    
        func getSections() -> [String] {
            return Array(Set(self.events.map { $0.date }))
        }
    
        func getItems(forSection dateSection: String) -> [Section] {
            return self.events.filter {$0.date == dateSection}
        }
    }
    

    Now, in your TableView datasource class, you need to use the model we created. I`ll do an example for you

    class YourTableView: UIViewController, UITableViewDataSource, UITableViewDelegate {
        // This is loaded from the getEvents function!
        var events: Events!
    
       func numberOfSections (in tableView: UITableView) -> Int {
            return self.events.getSections().count
        }
    
       func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            let dateSection = self.events.getSections()[section]
            return self.events.getItems(forSection: dateSection ).count
        }
    
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let dateSection = self.events.getSections()[indexPath.section]
            let currentEvent = self.getItems(forSection: dateSection)
    
             // Build your cell using currentEvent
        }
    }
    

    You are probably getting those JSONs from the web, so you have to handle a "loading" state, where you are returning the JSON from the API. That can easily be done by turning the events var into a optional, setting it when you get your JSON and reloading data from your tableView

提交回复
热议问题