O P
O P

Reputation: 2365

Use keys from JSON object to parse data and display as a list

I have an API that returns a JSON object with keys: http://acnhapi.com/v1/bugs

Since it's not an array, I'd like to understand how to traverse it. I am trying to get a list of the bug names "common butterfly" and "yellow butterfly" etc. by using their keys common_butterfly and yellow_butterfly etc.

I want to display the value of common_butterfly.name.name-USen, but for each bug. So my list view should ultimately be displayed as:

(Alphabetical would be a bonus)

data

import SwiftUI

struct Bugs: Codable, Identifiable {
    let id = UUID()
    var name: String
}

class FetchBugs: ObservableObject {
  @Published var bugs = [Bugs]()
     
    init() {
        let url = URL(string: "http://acnhapi.com/v1/bugs")!
        URLSession.shared.dataTask(with: url) {(data, response, error) in
            do {
                if let bugsData = data {
                    let decodedData = try JSONDecoder().decode([Bugs].self, from: bugsData)
                    DispatchQueue.main.async {
                        self.bugs = decodedData
                    }
                } else {
                    print("No data")
                }
            } catch {
                print("Error")
            }
        }.resume()
    }
}

list

import SwiftUI

struct BugList: View {
    @ObservedObject var fetch = FetchBugs()
    var body: some View {
        VStack {
            List(fetch.bugs) { bug in
                VStack(alignment: .leading) {
                    Text(bug.name)
                }
            }
        }
    }
}

struct BugList_Previews: PreviewProvider {
    static var previews: some View {
        BugList()
    }
}

Upvotes: 0

Views: 855

Answers (2)

pawello2222
pawello2222

Reputation: 54426

With this solution you can decode all your localised names:

struct Bug: Decodable, Identifiable {
    enum CodingKeys: String, CodingKey { case name }
    
    let id = UUID()
    var localizedNames: [String: String] = [:]
    
    var nameUSen: String {
        localizedNames["name-USen"] ?? "error"
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let names = try container.decode([String: String].self, forKey: .name)
        for (key, value) in names {
            localizedNames[key] = value
        }
    }
}

Use .sorted { $0.nameUSen < $1.nameUSen } to sort your data:

class FetchBugs: ObservableObject {
    @Published var bugs = [Bug]()

    init() {
        let url = URL(string: "http://acnhapi.com/v1/bugs")!
        URLSession.shared.dataTask(with: url) { data, response, error in
            do {
                if let bugsData = data {
                    let decodedData = try JSONDecoder().decode([String: Bug].self, from: bugsData)
                    DispatchQueue.main.async {
                        self.bugs = Array(decodedData.values).sorted { $0.nameUSen < $1.nameUSen }
                    }
                } else {
                    print("No data")
                }
            } catch {
                print(error)
            }
        }.resume()
    }
}

And display the USen name:

struct BugList: View {
    @ObservedObject var fetch = FetchBugs()
    var body: some View {
        VStack {
            List(fetch.bugs) { bug in
                VStack(alignment: .leading) {
                    Text(bug.nameUSen)
                }
            }
        }
    }
}

If you'd ever want to access any other name you can use:

bug.localizedNames["name-EUde"]!

Upvotes: 1

Gereon
Gereon

Reputation: 17844

Here's a playground that illustrates getting all bug's names in alphabetical order:

struct Bug: Decodable, Identifiable {
    let id: Int
    let name: Name

    struct Name: Decodable {
        let nameUSen: String

        enum CodingKeys: String, CodingKey {
            case nameUSen = "name-USen"
        }
    }
}

do {
    let butterflies = try Data(contentsOf: URL(string: "http://acnhapi.com/v1/bugs")!)
    let allBugs = try JSONDecoder().decode([String: Bug].self, from: butterflies)

    let bugs = Array(allBugs.values.sorted { $0.name.nameUSen < $1.name.nameUSen })
    bugs.forEach { print($0.name.nameUSen) }
} catch {
    print(error)
}

Upvotes: 0

Related Questions