0

I have a 'Recipe' struct whose instances are created by parsing a JSON file. All the properties of Recipe map to the JSON, except for one: id.

I want each struct instance to be uniquely identifiable via this property, and it should be generated whenever the struct is instantiated, however, when instances of struct are created, all of their ids are nil.

Recipe.swift

import Foundation

struct Recipe: Identifiable, Decodable {
    var id = UUID()
    let name:String
    let featured:Bool
    let image:String
    let description:String
    let prepTime:String
    let cookTime:String
    let totalTime:String
    let servings:Int
    let ingredients:[String]
    let directions:[String]
}

RecipeMode.swift (instances of Recipe struct)

import Foundation
import SwiftUI

class RecipeModel {
    var recipes = loadRecipes()
}

func loadRecipes() -> [Recipe] {
    var recipes = [Recipe]()
    let fileURL = URL(fileURLWithPath: Bundle.main.path(forResource: "recipes", ofType: "json")!)
    do {
        let data = try Data(contentsOf: fileURL)
        recipes = try JSONDecoder().decode([Recipe].self, from: data)
    } catch {
        print(error)
    }
    return recipes
}
Sam
  • 1,130
  • 12
  • 36
  • Declaring such initializer and including the id property settter gives me a `Return from initializer without initializing all stored properties` error. – Sam Feb 05 '22 at 13:09
  • https://stackoverflow.com/questions/64348019/using-uuid-with-json-in-swift ? – Larme Feb 05 '22 at 13:11
  • That doesn't help. Implementing the `private enum CodingKeys` makes my `struct` non-conformant with the `Decodable` protocol – Sam Feb 05 '22 at 13:16

1 Answers1

3

use let id = UUID() instead of var id = UUID(), and the id will not be decoded.

If the Xcode warning scare you too much, you can also use this:

struct Recipe: Identifiable, Decodable {
    let id = UUID()
    let name:String
    let featured:Bool
    let image:String
    let description:String
    let prepTime:String
    let cookTime:String
    let totalTime:String
    let servings:Int
    let ingredients:[String]
    let directions:[String]
    
    // not including id
    enum CodingKeys: String, CodingKey {
        case name, featured, image, description, prepTime, cookTime
        case totalTime, servings, ingredients, directions
    }
}
  • Thank you, this works. I am however getting a warning saying `Immutable property will not be decoded because it is declared with an initial value which cannot be overwritten` where the suggested solution is to change change `let` to `var` (which I obviously don't want to - it breaks the code). Are you aware of any other way to silence this? – Sam Feb 05 '22 at 13:29
  • no need to silence this, Xcode is actually telling you something you need to be aware of, and the message is very clear `Immutable property will not be decoded because it is declared with an initial value which cannot be overwritten`, a great warning in my opinion. – workingdog support Ukraine Feb 05 '22 at 13:31
  • Ok, my understanding was that having any warning indicates suboptimal code, but my interpretations of warnings might be wrong. Thanks – Sam Feb 05 '22 at 13:39
  • updated my answer with another approach. – workingdog support Ukraine Feb 05 '22 at 13:56
  • You should `let` declare `id`. – Joakim Danielson Feb 05 '22 at 14:03
  • yes, forgot about the var, corrected my answer. – workingdog support Ukraine Feb 05 '22 at 14:08