I have (with some help) created a Struct called SleepModel with all of my sleep variables. I then have a view called AddEntryView for adding a sleep entry into the app. In that view are textfields and pickers that the user fills in and a button at the bottom. When the user clicks the button, it saves the inputs to CoreData. My problem is it keeps saying that 'Value of optional type 'Double?' must be unwrapped to a value of type 'Double'' for the variable being pulled from the textfield. It is because a textfield is a string and I'm trying to convert it to a double to save so that I can do mathematical stuff with it later. Ideas?
It keeps crashing also saying this is always Nil:
if newSleep != nil {
var hoursSleptDouble = Double(hoursSlept)
newSleep!.hoursSlept = hoursSleptDouble ?? 0.0
coreDataViewModel.saveRecord(sleepModel: newSleep!) {
print("Success")
}
}
My code is below:
I have a SleepContainer CoreData file with Entity called 'SleepEntity'. In that I have two variables (id as a UUID, and hoursSlept as a double).
ADDENTRYVIEW (there is more commented out for additional fields but I'm trying to figure out the textfield first)
import SwiftUI
struct AddEntryView: View {
// Call CoreData for saving/reading data
@StateObject var coreDataViewModel = CoreDataViewModel()
// New empty Sleep object for user input
@State var newSleep: SleepModel!
@State var hoursSlept = String()
var body: some View {
ZStack {
Rectangle()
.fill(Color(hue: 0.587, saturation: 0.619, brightness: 0.907)).ignoresSafeArea()
VStack {
Text("ADD SLEEP ENTRY")
.font(.system(size: 32.0)).fontWeight(.heavy)
.bold()
.foregroundColor(.black)
HStack {
DatePicker(selection: .constant(Date()), displayedComponents: .date, label: { Text("Date")})
}
Text("Sleep Basics")
.font(.system(size: 16.0)).fontWeight(.heavy)
.bold()
.foregroundColor(.black)
.padding()
Section {
HStack{
Text("Hours Slept")
Spacer()
TextField("Hours Slept", text:$hoursSlept).padding(.leading, 75)
}.multilineTextAlignment(.trailing)
Button(action: {
if newSleep != nil {
var hoursSleptDouble = Double(hoursSlept)
newSleep!.hoursSlept = hoursSleptDouble ?? 0.0
coreDataViewModel.saveRecord(sleepModel: newSleep!) {
print("Success")
}
}
})
{
Capsule()
.foregroundColor(Color.green)
.frame(height: 44)
.overlay(Text("Add Entry").foregroundColor(Color.white).bold())
}.padding()
}.padding()
.background(Color.white)
.clipShape(RoundedRectangle(cornerRadius: 25.0, style: .continuous))
.padding()
}
}
}
struct AddEntryView_Previews: PreviewProvider {
static var previews: some View {
AddEntryView()
}
}
SLEEPMODEL.swift (again, more variables are there but commented out until I figure this part out)
import Foundation
struct SleepModel: Identifiable, Hashable {
var id = UUID().uuidString // ID variable
var hoursSlept: Double // Hours slept variable
}
CodePudding user response:
The main issue here is that the code us that you are using the model object in the wrong scope IMO. You don't need to have it as a property at all but wait to create it until you actually need it.
So step 1 would be to remove the declaration of newSleep
and then step 2 would be to move the code in the button action into a separate function because it makes the intent of the code clearer. Step 3 would be to refactor the code so it behaves better
Here is the function that you then call from the button action
private func saveSleepModel() {
guard let hoursSleptDouble = Double(hoursSlept) else {
print("hours slept is invalid")
return
}
var sleepModel = SleepModel(hoursSlept: hoursSleptDouble)
coreDataViewModel.saveRecord(sleepModel: sleepModel) {
print("Success")
}
}
The following steps would be for you to handle the other attributes to the function in the same way as hoursSlept
and also some better feedback/error handling than using print