Hi, so I have a recipe app that works perfectly. Below you have the code of the AddRecipeView.
import CoreData
import SwiftUI
struct AddRecipeView: View {
@Environment(\.managedObjectContext) var moc //environment property to store our managed object context:
@Environment(\.dismiss) var dismiss
@State private var title = ""
@State private var type = ""
@State private var ingredients = ""
@State private var text = ""
@State private var author = ""
@State private var hardness = 3
@State private var diet = ""
@State private var occasion = ""
let diets = ["Vegetarian", "Vegan", "Gluten free", "Dairy free", "Pescatarian", "Omnivore"]
let types = ["Soup", "Salad", "Main", "Dessert", "Side", "Breakfast", "Lunch", "Dinner"]
let occasions = ["Christmas", "New Years", "Birthday", "Easter", "Everyday"]
var hasValidName: Bool {
if title.isEmpty || author.isEmpty {
return false
}
return true
}
var body: some View {
NavigationView {
Form { // this is where I was getting the error for the form
Section {
TextField("Recipe name", text: $title)
TextField("Author's name", text: $author)
Picker("Diet", selection: $diet) {
Text("").tag("")
ForEach(diets, id: \.self) {
Text($0)
}
}
Picker("Type", selection: $type) {
Text("").tag("")
ForEach(types, id: \.self) {
Text($0)
}
}
Picker("Occasion", selection: $occasion) {
Text("").tag("")
ForEach(occasions, id: \.self) {
Text($0)
}
}
}
Section {
HardnessView(hardness: $hardness)
} header: {
Text("How hard is this?")
}
Section {
TextEditor(text: $ingredients)
} header: {
Text("Write the ingredients")
}
Section {
TextEditor(text: $text)
} header: {
Text("recipe")
}
Section {
Button("Save") {
let newRecipe = Recipe(context: moc)
newRecipe.id = UUID()
newRecipe.title = title
newRecipe.author = author
newRecipe.hardness = Int16(hardness)
newRecipe.diet = diet
newRecipe.occasion = occasion
newRecipe.ingredients = ingredients
newRecipe.type = type
newRecipe.text = text
try? moc.save()
dismiss()
}
}
.disabled(hasValidName == false)
}
.toolbar {
ToolbarItemGroup(placement: .navigationBarLeading) {
Button {
dismiss()
} label: {
Label("Back", systemImage: "xmark")
}
Text("Add recipe".uppercased())
.padding(95)
}
}
}
}
}
Next I have the DetailView which populates everything that was saved in the add view.
import CoreData
import SwiftUI
struct DetailView: View {
@State var recipe: Recipe
@Environment(\.managedObjectContext) var moc
@Environment(\.dismiss) var dismiss
@State private var showingDeleteAlert = false
var body: some View {
ScrollView {
Text(recipe.author ?? "Unknown author")
.font(.headline)
.foregroundColor(.secondary)
HStack(alignment: .center) {
Text(recipe.type?.uppercased() ?? "SOUP")
.font(.caption)
.fontWeight(.black)
.padding(8)
.foregroundColor(.white)
.background(.black.opacity(0.75))
.clipShape(Capsule())
.offset(x: -5, y: -5)
Text(recipe.occasion?.uppercased() ?? "Unknown occasion")
.font(.caption)
.fontWeight(.black)
.padding(8)
.foregroundColor(.white)
.background(.black.opacity(0.75))
.clipShape(Capsule())
.offset(x: -5, y: -5)
Text(recipe.diet?.uppercased() ?? "Unknown diet")
.font(.caption)
.fontWeight(.black)
.padding(8)
.foregroundColor(.white)
.background(.black.opacity(0.75))
.clipShape(Capsule())
.offset(x: -5, y: -5)
}
Text(recipe.ingredients ?? "Unknown ingredients")
.foregroundColor(.secondary)
.padding()
Text(recipe.text ?? "No recipe text")
.foregroundColor(.secondary)
.padding()
HardnessView(hardness: .constant(Int(recipe.hardness)))
.font(.caption)
.navigationTitle(recipe.title?.uppercased() ?? "Unknown Recipe")
.navigationBarTitleDisplayMode(.inline)
.alert("Delete recipe", isPresented: $showingDeleteAlert) {
Button("Delete", role: .destructive, action: deleteRecipe)
Button("Cancel", role: .cancel) { }
} message: {
Text("Are you sure?")
}
.toolbar {
Button {
showingDeleteAlert = true
} label: {
Label("Delete this recipe", systemImage: "trash")
}
}
}
}
func deleteRecipe() {
moc.delete(recipe)
//comment this line if you want to make the deletion permanent
try? moc.save()
dismiss()
}
}
What I would like to do is to add an Edit button to the DetailView that takes me back to the AddRecipeView, or a view that looks identical and everything is populated as previously saved for that recipe, so it can be edited then saved again.
I don't know how to do that. I was told to just reference the previously saved entry, change whatever values I want and then resave the core data persistent container, but I have no clue how to do that either.
I have been researching all day long and I just can't wrap my head around it.
I did try to make an EditRecipeView that starts off looking identical to the AddRecipeView, but I added :
struct EditRecipeView: View {
@State var recipe: Recipe // added this
@Environment(\.managedObjectContext) var moc //environment property to store our managed object context:
@Environment(\.dismiss) var dismiss
and instead of all the binding I added things like this( so i can populate with the already saved data):
TextField("Recipe name", text: recipe.title)b // error: Cannot convert value of type 'String?' to expected argument type 'Binding<String>'
TextField("Author's name", text: recipe.author)
Picker("Diet", selection: recipe.diet) {
Text("").tag("")
ForEach(diets, id: \.self) {
Text($0)
}
}
this didnt work as I was getting errors like: Cannot convert value of type 'String?' to expected argument type 'Binding<String>' ,
and when I was trying to fix that this error keeps persisting on my Form: Trailing closure passed to parameter of type 'FormStyleConfiguration' that does not accept a closure.