Refresh a fetched data with randomElement on tap

Viewed 32

I work on an app that displays random quotes from the JSON Bundle. I have an issue where I cannot refresh the displayed quote. Text(...) populates once the app loads and that's it. I'd appreciate suggestions on how can I refresh the quote and the author on the button tap.

Here is the minimum reproducible code:

JSON Structure:

[
  {
    "text": "Genius is one percent inspiration and ninety-nine percent perspiration.",
    "author": "Thomas Edison"
  },
  {
    "text": "You can observe a lot just by watching.",
    "author": "Yogi Berra"
  }
]

My struct:

struct Quote: Codable{
    
    var text: String?
    var author: String?
}

Here's the Bundle extension I am using:

    func decode<T: Decodable>(_ type: T.Type, from file: String, dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .deferredToDate, keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys) -> 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()
        decoder.dateDecodingStrategy = dateDecodingStrategy
        decoder.keyDecodingStrategy = keyDecodingStrategy

        do {
            return try decoder.decode(T.self, from: data)
        } catch DecodingError.keyNotFound(let key, let context) {
            fatalError("Failed to decode \(file) from bundle due to missing key '\(key.stringValue)' not found – \(context.debugDescription)")
        } catch DecodingError.typeMismatch(_, let context) {
            fatalError("Failed to decode \(file) from bundle due to type mismatch – \(context.debugDescription)")
        } catch DecodingError.valueNotFound(let type, let context) {
            fatalError("Failed to decode \(file) from bundle due to missing \(type) value – \(context.debugDescription)")
        } catch DecodingError.dataCorrupted(_) {
            fatalError("Failed to decode \(file) from bundle because it appears to be invalid JSON")
        } catch {
            fatalError("Failed to decode \(file) from bundle: \(error.localizedDescription)")
        }
    }
}

And a ContentView:

struct ContentView: View {
    let quotes = Bundle.main.decode([Quote].self, from: "quotes.json")
    var quote : Quote { quotes.randomElement()! }
    
    var body: some View {
        VStack {
            VStack {
                
                Text(quote.text!)
                    .font(.system(.title3))
                
                Text(quote.author!)
                    .font(.system(.title3))
                
                Button("refresh") {
                    quote = quotes.randomElement()! //-> Error here: Cannot assign to property: 'quote' is a get-only property
                    
                }
            }
        }
    }
}
0 Answers
Related