Decoding JSON SwifUI

Hello
I'm trying to decode a JSON File but it's not working and I can't understand why

Code:

Code Block
import SwiftUI
struct Search: View {
    
    let sf: [SF] = Bundle.main.decode("sf.json")
    @State private var searchText: String = ""
    var body: some View {
        NavigationView{
            Form {
                Section(header: Text("Search bar")){
                TextField("Search", text: $searchText)
                    .padding(7)
                    .background(Color(.systemGray6))
                    .cornerRadius(8)
                }
                Section(){
                    List{
                        ForEach(sf) { item in
                            Text(item.title)
                        }
                    }
                }
            }
            .navigationTitle("SF")
        }
    }
    
}
struct SF: Codable, Identifiable {
    var id: String
    var title: String
    var items: [String]
}
extension Bundle {
  func decode<T: Codable>(_ file: String) -> T {
    
    guard let url = self.url(forResource: file, withExtension: nil) else {
      fatalError("Failed to locate \(file) in bundle.")
    }
    
    
    guard let data = try? Data(contentsOf: url) else {
      fatalError("Failed to load \(file) from bundle.")
    }
    
    
    let decoder = JSONDecoder()
    
   
    guard let loaded = try? decoder.decode(T.self, from: data) else {
      fatalError("Failed to decode \(file) from bundle.")
    }
    
   
    return loaded
  }
}

Here is the JSON file
Code Block
[
  {
    "id": "all"
    "title": "All",
    "items": [
      "square.and.arrow.up",
      "square.and.arrow.up.fill",
      "square.and.arrow.down"
      ]
   }
]


Thank you
Answered by OOPer in 654447022

This is the file now: 

Apparently, it does not have "id" in any elements.

You can remove id as a stored property from SF to make your file decodable,
and add another computed property id to make it conform to Identifiable:
Code Block
struct SF: Codable, Identifiable {
//var id: String //<- Remove this stored property
var id: String {title} //<- Add this computed property
var title: String
var items: [String]
}



And I have another question about the decimalPad

That is not for this thread. Please keep, one thread for one topic.
I'm trying to decode a JSON File but it's not working and I can't understand why

It is not working is not an information.
  • what do you get ? Is it a wrong result (which) or no result at all ? (what do you get in loaded ?

  • Does it crash ? If so, what is the crash log

I looked at the code:
Isn't there a comma missing at the end of line 5 in JSON file ?

Note: when you paste code, use Paste and Match Style, to avoid the extra blank lines that make code quasi impossible to analyse.

Code Block
import SwiftUI
struct Search: View {
let sf: [SF] = Bundle.main.decode("sf.json")
@State private var searchText: String = ""
var body: some View {
NavigationView{
Form {
Section(header: Text("Search bar")){
TextField("Search", text: $searchText)
.padding(7)
.background(Color(.systemGray6))
.cornerRadius(8)
}
Section(){
List{
ForEach(sf) { item in
Text(item.title)
}
}
}
}
.navigationTitle("SF")
}
}
}
struct SF: Codable, Identifiable {
var id: String
var title: String
var items: [String]
}
extension Bundle {
func decode<T: Codable>(_ file: String) -> T {
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("Failed to locate \(file) in bundle.")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(file) from bundle.")
}
let decoder = JSONDecoder()
guard let loaded = try? decoder.decode(T.self, from: data) else {
fatalError("Failed to decode \(file) from bundle.")
}
return loaded
}
}

I tried putting the comma but it's not working anyway, it gives me a fatalError("Failed to decode \(file) from bundle.")
Actually now it worked but I changed the JSON file (you can find it by searching SF Symbols JSON, I pasted the JSON file in my project) and it stopped working again.
I recommend NEVER to use try?, which throws away all the error info.

Change your line 79...83 as:
Code Block
let data: Data
do {
data = try Data(contentsOf: url) //<-`try`, not `try?`
} catch {
fatalError("Failed to load \(file) from bundle.: \(error)")
}

And line 95...99:
Code Block
let loaded: T
do {
loaded = try decoder.decode(T.self, from: data) //<-`try`, not `try?`
} catch {
fatalError("Failed to decode \(file) from bundle.: \(error)")
}


And please tell us what you get. That would show some info where to fix.
Hello
I tried your solution and got this:
Thread 1: Fatal error: Failed to decode sf.json from bundle.: keyNotFound(CodingKeys(stringValue: "id", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"id\", intValue: nil) (\"id\").", underlyingError: nil))

got this: 

Code Block
Thread 1: Fatal error: Failed to decode sf.json from bundle.: keyNotFound(CodingKeys(stringValue: "id", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"id\", intValue: nil) (\"id\").", underlyingError: nil))
Thanks for trying. And the most important part of the error is: keyNotFound and "id".
It represents your sf.json lacks "id" in the first element of the JSON array.

I have tested your code (including my suggestions) with this sf.json and it worked without runtime errors.
sf.json:
Code Block
[
{
"id": "all",
"title": "All",
"items": [
"square.and.arrow.up",
"square.and.arrow.up.fill",
"square.and.arrow.down"
]
}
]

(I filled comma at the end of Line 3, as you see.)

What is your current sf.json? Does it have an entry for "id"?

This is the file now:
[

  {

    "title": "Shapes",

    "items": [

      "rectangle",

      "rectangle.fill",

      "shield",

      "shield.fill"

    ]

  },

  {

    "title": "Arrows",

    "items": [

      "arrowshape.turn.up.left",

      "arrowshape.turn.up.left.fill",

      "arrowshape.turn.up.left.circle",

      "arrowshape.turn.up.left.circle.fill"

    ]

  }

]
Hello
And I have another question about the decimalPad

Code Block
@State var deci = 3
     var numberFormatter: NumberFormatter = {
         let nf = NumberFormatter()
         nf.locale = Locale.current
         nf.numberStyle = .decimal
         nf.maximumFractionDigits = deci
         return nf
     }()

How do I fix this at line 10:
Instance member 'deci' cannot be used on type 'BMIView'; did you mean to use a value of this type instead?

Thank you very much for your time

Accepted Answer

This is the file now: 

Apparently, it does not have "id" in any elements.

You can remove id as a stored property from SF to make your file decodable,
and add another computed property id to make it conform to Identifiable:
Code Block
struct SF: Codable, Identifiable {
//var id: String //<- Remove this stored property
var id: String {title} //<- Add this computed property
var title: String
var items: [String]
}



And I have another question about the decimalPad

That is not for this thread. Please keep, one thread for one topic.
It worked thank you
Decoding JSON SwifUI
 
 
Q