am trying to load this json into my swiftui app
{
"id": "EF1CC5BB-4785-4M8E-AB98-5FA4E00B6A66",
"name" : "opening the box",
"group" : [
{
"name" : "Open box",
"windows" : "Double-click",
"mac" : "right-click"
},
{
"name" : "Duplicate",
"windows" : "right click and choose from options",
"mac" : "option drag"
}
]
},
and there are many more elements like this
and here are the structs that I made
struct topic: Codable, Identifiable {
var id: UUID
var name: String
var group: [shortcuts]
}
struct shortcuts: Codable, Equatable, Identifiable{
var id = UUID()
var name: String
var windows: String
var mac: String
}
and here is the code for decoding it
import UIKit
extension Bundle {
func decode<T: Decodable>(_ type: T.Type, from file: String) -> T {
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("failed")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("failed to do it")
}
let decoder = JSONDecoder()
guard let loaded = try? decoder.decode(T.self, from: data) else {
fatalError("failed")
}
return loaded
}
}
now when I create a constant like this inside my view
let Topic = Bundle.main.decode([topic].self, from: "main.json")
and then I try to load the items like this
NavigationView{
List{
ForEach(Topic) { section in
Section(header: Text(section.name)) {
ForEach(section.group) { item in
shortcutRow(item: item)
}
}
}
}.navigationBarTitle("Menu")
.listStyle(SidebarListStyle())
}
it doesn't work it shows fatal error that is present in the second last line of code of my bundle extension that I created for decoding this json
please help
CodePudding user response:
"...and there are many more elements like this", that means the json you show
is probably not the json you have. Most likely it is an array of what you show.
Thus, in Bundle decode
, you should decode and return [T]
not T
.
Note you should use the normal convention of using uppercased name for types,
such as Topic
, and lowercased name for instances, topic
. Given that, try this:
extension Bundle {
// todo deal with errors
func decode<T: Decodable>(from file: String) -> [T] { // <-- here
if let url = Bundle.main.url(forResource: file, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let loaded = try JSONDecoder().decode([T].self, from: data) // <-- here
return loaded
} catch {
// todo deal with errors
print("---> error: \(error)")
}
} else {
// todo deal with errors
print("---> error url: ")
}
return []
}
}
struct Topic: Codable, Identifiable {
var id, name: String
var group: [Shortcuts]
}
struct Shortcuts: Codable, Identifiable {
let id = UUID() // <-- here use let
var name, windows, mac: String
}
struct ContentView: View {
let topics: [Topic] = Bundle.main.decode(from: "main") // <-- here
var body: some View {
Text("testing topics: \(topics.count)")
.onAppear {
print("---> topics: \(topics)")
}
}
}
EDIT:
This is the json data I used to test my answer:
[
{
"id": "EF1CC5BB-4785-4M8E-AB98-5FA4E00B6A66",
"name" : "opening the box",
"group" : [
{
"name" : "Open box",
"windows" : "Double-click",
"mac" : "right-click"
},
{
"name" : "Duplicate",
"windows" : "right click and choose from options",
"mac" : "option drag"
}
]
}
]