Can anyone help solve the error here? I continue to get the fatal error in the ModelData.swift file: "Couldn't parse quotes.json as Array". I have validated the JSON file on JSONLint as well.
Here are the project files: LINK
I am new to SwiftUI so any help is very appreciated.
Model Data:
import Foundation
import Combine
final class ModelData: ObservableObject {
@Published var quotes: [Quote] = load("quotes.json")
}
func load<T: Decodable>(_ filename: String) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
else {
fatalError("Couldn't find \(filename) in main bundle")
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
}
}
Model:
import SwiftUI
struct Quote: Codable, Identifiable, Hashable {
var id: Int
var readDate: Date?
var quote: String
var author: String
var isFavorited: Bool
var displayAuthor: String {
"- \(author)"
}
var formattedDate: String {
readDate?.formatted(date: .numeric, time: .omitted) ?? "N/A"
}
var dayDate: String {
readDate?.formatted(.dateTime.day()) ?? "N/A"
}
}
JSON File:
[
{
"id": 1,
"readDate": "11.12.2022",
"quote": "If you judge a fish by its ability to climb a tree then it will go its whole live beliving it's stupid.",
"author": "Albert Einstein",
"isFavorited": false
},
{
"id": 2,
"readDate": "11.13.2022",
"quote": "Do or do not. There is no try.",
"author": "Master Yoda",
"isFavorited": true
},
{
"id": 3,
"readDate": "11.14.2022",
"quote": "Learn as if you will live forever, live like you will die tomorrow.",
"author": "Mahatma Gandhi",
"isFavorited": false
},
{
"id": 4,
"readDate": "11.15.2022",
"quote": "When you change your thoughts, remember to also change your world.",
"author": "Norman Vincent Peale",
"isFavorited": false
},
{
"id": 5,
"readDate": "11.16.2022",
"quote": "Success is not final; failure if not fatal: It is the courage to continue that counts.",
"author": "Winston S. Churchill",
"isFavorited": false
},
{
"id": 6,
"readDate": "11.17.2022",
"quote": "Success is getting what you want, happiness is wanting what you get.",
"author": "W. P. Kinsella",
"isFavorited": false
},
{
"id": 7,
"readDate": "11.18.2022",
"quote": "Successs usually comes to those who are too busy looking for it.",
"author": "Henry David Thoreau",
"isFavorited": false
},
{
"id": 8,
"readDate": "11.19.2022",
"quote": "Don't let yesterday take up too much of today.",
"author": "Will Rogers",
"isFavorited": false
},
{
"id": 9,
"readDate": "11.20.2022",
"quote": "You miss 100% of the shots you don't take.",
"author": "Wayne Gretzky",
"isFavorited": true
}
]
Post
Replies
Boosts
Views
Activity
I'm working on an exercise app and have been running into the error "Cannot assign to property: "weightArray" is a get-only property".
I want to have the textfield be able to enter a different numerical value for each set (0..<4)
I believe I need to use { get set } in weightArray in my model data but I can't seem to figure out exactly how to implement it.
View:
@Binding var data: Exercise
var body: some View {
List {
VStack(alignment: .leading) {
ForEach(0..<4, id: \.self) { sets in
ZStack {
RoundedRectangle(cornerRadius: 8)
.fill(.opacity(0.1))
TextField("0", text: $data.weightArray[sets])
.multilineTextAlignment(.center)
.keyboardType(.decimalPad)
}
.frame(width: 65, height: 30)
Text("lbs")
Divider()
}
}
}
}
}
Model:
struct Exercise: Identifiable, Codable {
let id: UUID
var weight: [String]
var weightArray: [String] {
let array: Array<String> = Array(repeating: "0", count: numericalSets)
return array
}
Any help is appreciated.
I'm working on a calculator app but I'm having trouble displaying the results of a function. When I press a button in ButtonView, a function runs to calculate the result but that result does not display in CalculationCircleView.
Main View:
struct ScoreCalculatorView: View {
@StateObject private var viewModel = ViewModel()
var body: some View {
VStack {
Spacer()
HStack {
CalculationCircleView(calculation: viewModel.grossScore, label: "Score")
Spacer()
CalculationCircleView(calculation: viewModel.plusMinus, label: "+/-")
}
.padding(.horizontal, 30)
Spacer()
LazyVGrid(columns: viewModel.columns, spacing: 20) {
ForEach(0..<4, id: \.self) { index in
ZStack {
DataCellsView(labels: viewModel.scoreLabels[index])
TextField("\(viewModel.scoreLabels[index])", text: $viewModel.scoreData[index])
.font(.largeTitle)
.fontWeight(.light)
.frame(maxWidth: 80)
.multilineTextAlignment(.center)
.keyboardType(.numberPad)
}
}
}
.frame(width: 250)
Spacer()
ButtonView(data: viewModel.scoreData)
.padding(.bottom, 20)
}
}
}
CalculationCircleView:
struct CalculationCircleView: View {
@StateObject private var viewModel = ViewModel()
@State var result = ""
let calculation: Double
let label: String
var body: some View {
VStack {
ZStack {
Circle()
.stroke(lineWidth: 8)
.foregroundColor(.green)
.frame(height: 150)
Text("\(viewModel.grossScore, specifier: "%.1f")")
.font(.largeTitle)
.fontWeight(.light)
}
Text(label)
}
}
}
ButtonView:
struct ButtonView: View {
@StateObject private var viewModel = ViewModel()
let data: [String]
var body: some View {
Button(action: {
print("***** Score before func: \(viewModel.grossScore)")
viewModel.calculateGrossScore(data: data)
print("***** Score after func: \(viewModel.grossScore)")
// viewModel.calculatePlusMinus(data: data)
// viewModel.calculateDifferential(data: data)
}, label: {
ZStack {
RoundedRectangle(cornerRadius: 4)
.frame(width: 200, height: 50)
.foregroundColor(.green)
Text("Calculate")
.bold()
.foregroundColor(.white)
}
})
.padding(.bottom, 20)
}
}
I believe that the issue is the variable grossScore is being updated after CalculationCircleView has been called.
Is there anyway to update a view that is higher in the view hierarchy?
Note: There are some other bugs that I have not gotten to yet. Just wanted to focus on this one first.
Project Files: LINK
I'm working on a workout app which has an array within an array and takes user input to populate each array.
In the first array, I can add an item to plans using the addPlan function. However, I cannot seem to figure out how to add items to the next level in the addExercise function.
final class ViewModel: ObservableObject {
@Published var plans: [Plan] = []
func addPlan(_ item: String) {
let plan = Plan(name: item, exercise: [])
plans.insert(plan, at: 0)
}
func addExercise() {
}
}
Essentially, Plan holds an array of Exercise. Here is the model for Plan
struct Plan: Identifiable {
let id = UUID()
let name: String
var exercise: [Exercise]
static let samplePlans = Plan(name: "Chest", exercise: [Exercise(name: "Bench"), Exercise(name: "Incline Bench")])
}
These two functions should behave the same but just appending the items into different arrays.
The end result would be:
Plan 1: [Exercise1, Exercise2, Exercise3]
Plan 2: [Exercise4, Exercise5, Exercise6] etc.
Project files: LINK