CoreData always takes the same item

I have the problem that when I want to edit the item, the item that was created last is always changed.



//  EditSheet.swift

//  CoreData_T

//

//  Created by Janik Hartmann on 11.01.23.

//



import SwiftUI

import Foundation

import CoreData



struct EditSheet: View {

    @Environment(\.managedObjectContext) private var viewContext

    @Environment(\.dismiss) private var dismiss

    

    @State private var name: String = ""

    private var item: Item

    var itemid: UUID

   

    

    init(itemId: UUID, item: Item) {

                self.item = item

                self.itemid = item.id!

                self.name = item.name ?? ""

            }

        



    var body: some View {

        VStack{

            TextField("Text eingeben", text: $name)

                .padding()

            Button {

                

                update(item: item, itemid: item.id!)

                dismiss()

                

                

            } label: {

                Text("Save")

            }.padding()

        }.onAppear{

            name = item.name!

            

        }

    }

    func update(item:Item, itemid id: UUID){

        let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Item")

        fetchRequest.predicate = NSPredicate(format: "id = %@", itemid as CVarArg)

        

        do {

            let results = try viewContext.fetch(fetchRequest) // Rufe den Fetchrequest auf

            let objectToUpdate = results[0] // Nehme das erste

            objectToUpdate.setValue(name, forKey: "name") //Editiere den Key "name" zum übergebenen Wert



            do {

                try viewContext.save() // Speichern

                print("Erfolgreich gespeichert!")

            } catch let error as NSError {

                print("Fehler beim Speichern. \(error), \(error.userInfo)")

            }

        } catch let error as NSError {

            print("Fehler beim Fetchen. \(error), \(error.userInfo)")

        }

    }

}


Answered by Claude31 in 742097022

A possible reason is that by invoking the state var showeditsheet,

         HStack {
                Text(item.name + "\(showeditsheet ? "" : "") "?? "Not found")

it forces some reevaluation of views (such as the sheet to present or even the whole List view).

                        .sheet(isPresented: $showeditsheet){
                            EditSheet(itemId: item.id!, item:item)

So, don't forget to close this long thread on this answer. And good continuation.

You asked a very similar question 3 days ago. I did answer asking a few questions. Could you answer before opening a new thread ?

Changed the code again and tried other approaches. Have asked the question again because of this. I load the items from the Item entity. The items are added in the ContentView. Tested with a print-statement that there the Ids are created correctly and also each item has a different one. Only in the EditView it always takes the same ID of the last added item. Also was first the problem that the Ids are randomly selected. Now it always selects the one of the last added item.

Could you show where you call EditSheet, to understand how you set item ?

Please also show how Item is defined.

The ContentView, where the item is set:

//

//  ContentView.swift

//  CoreData_T

//

//  Created by Janik Hartmann on 31.12.22.

//



import SwiftUI

import CoreData

import Foundation



struct ContentView: View {

    @Environment(\.managedObjectContext) private var viewContext



    //Abfrage zum Anzeigen

    @FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Item.name, ascending: false)],

                  animation: .default)

    

  

   

    

    //Referenz zum Entity Item

    var items: FetchedResults<Item>

    

    

    @State private var name: String = ""

    

//     Test

    @State private var editName = ""

   

    @State private var showeditsheet = false

    

    

      



    var body: some View {

        NavigationView {

            VStack{

                TextField("Text eingeben", text: $name)

                    .padding()

                Button {

                    

                    addItem()

                    

                } label: {

                    Text("Save")

                }.padding()



                List {

                    ForEach(items, id: \.self) { item in

                        

                        NavigationLink(destination: ToDo(list:item)) {

                            HStack {

                                Text(item.name ?? "Not found")

                                Button {

                                

                                    self.showeditsheet = true

                                } label: {

                                    Text("Edit")

                                }.buttonStyle(.bordered)

                            }

                            

                            

                        }

                        .sheet(isPresented: $showeditsheet){

                            EditSheet(itemId: item.id!, item:item)

                        }

                     

                    }

                    .onDelete(perform: deleteItems)

                }

            }

            }

         

        }

    

//MARK: - Hinzufügen der Listen

    private func addItem() {

        withAnimation {

            let newItem = Item(context: viewContext)

            newItem.name = name

            newItem.id = UUID()



            do {

                try viewContext.save()

            } catch {

                // Replace this implementation with code to handle the error appropriately.

                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                let nsError = error as NSError

                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")

            }

        }

    }

    

   

    private func deleteItems(offsets: IndexSet) {

        withAnimation {

            offsets.map { items[$0] }.forEach(viewContext.delete)



            do {

                try viewContext.save()

            } catch {

                // Replace this implementation with code to handle the error appropriately.

                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                let nsError = error as NSError

                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")

            }

        }

    }

}

Let's reformat with code formatter to make it a bit more readable

import SwiftUI
import CoreData
import Foundation

struct EditSheet: View {

    @Environment(\.managedObjectContext) private var viewContext
    @Environment(\.dismiss) private var dismiss

    @State private var name: String = ""

    private var item: Item

    var itemid: UUID

    init(itemId: UUID, item: Item) {
                self.item = item
                self.itemid = item.id!
                self.name = item.name ?? ""
            }

    var body: some View {

        VStack {
            TextField("Text eingeben", text: $name)
                .padding()

            Button {
                update(item: item, itemid: item.id!)
                dismiss()
            } label: {
                Text("Save")
            }.padding()

        }.onAppear {
            name = item.name!
        }

    }

    func update(item:Item, itemid id: UUID){

        let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Item")
        fetchRequest.predicate = NSPredicate(format: "id = %@", itemid as CVarArg)

        do {
            let results = try viewContext.fetch(fetchRequest) // Rufe den Fetchrequest auf
            let objectToUpdate = results[0] // Nehme das erste
            objectToUpdate.setValue(name, forKey: "name") //Editiere den Key "name" zum übergebenen Wert

            do {
                try viewContext.save() // Speichern
                print("Erfolgreich gespeichert!")

            } catch let error as NSError {
                print("Fehler beim Speichern. \(error), \(error.userInfo)")
            }

        } catch let error as NSError {
            print("Fehler beim Fetchen. \(error), \(error.userInfo)")
        }
    }

}
import SwiftUI
import CoreData
import Foundation

struct ContentView: View {

    @Environment(\.managedObjectContext) private var viewContext
 
    //Abfrage zum Anzeigen
    @FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Item.name, ascending: false)], animation: .default)

    //Referenz zum Entity Item
    var items: FetchedResults<Item>

    @State private var name: String = ""

    //     Test
    @State private var editName = ""
    @State private var showeditsheet = false

    var body: some View {
        
        NavigationView {
            
            VStack{
                
                TextField("Text eingeben", text: $name)
                    .padding()
                
                Button {
                    addItem()
                } label: {
                    Text("Save")
                }.padding()
                
                List {
                    ForEach(items, id: \.self) { item in
                        
                        NavigationLink(destination: ToDo(list:item)) {
                            
                            HStack {
                                Text(item.name ?? "Not found")
                                
                                Button {
                                    self.showeditsheet = true
                                } label: {
                                    Text("Edit")
                                }.buttonStyle(.bordered)
                                
                            }
                        }
                        
                        .sheet(isPresented: $showeditsheet){
                            EditSheet(itemId: item.id!, item:item)
                        }
                        
                    }
                    .onDelete(perform: deleteItems)
                    
                }
            }
        }
    }

//MARK: - Hinzufügen der Listen

    private func addItem() {

        withAnimation {
            let newItem = Item(context: viewContext)
            newItem.name = name
            newItem.id = UUID()

            do {
                try viewContext.save()
            } catch {

                // Replace this implementation with code to handle the error appropriately.

                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")

            }
        }
    }

    private func deleteItems(offsets: IndexSet) {

        withAnimation {
            offsets.map { items[$0] }.forEach(viewContext.delete)

            do {
                try viewContext.save()
            } catch {

                // Replace this implementation with code to handle the error appropriately.

                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")

            }
        }
    }

}

A few points first:

  • name is not optional. You cannot unwrap.
  • where do you use editName ?
  • could you add print in several placed to see what item it is ?

Ok

import SwiftUI

import CoreData

import Foundation



struct ContentView: View {

    @Environment(\.managedObjectContext) private var viewContext

    

    //Abfrage zum Anzeigen

    @FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Item.name, ascending: false)],

                  animation: .default)

    

    

    

    

    //Referenz zum Entity Item

    var items: FetchedResults<Item>

    

    

    @State private var name: String = ""

    

    //     Test

    @State private var editName = ""

    

    @State private var showeditsheet = false

    

    

      

    

    var body: some View {

        NavigationView {

            VStack{

                TextField("Text eingeben", text: $name)

                    .padding()

                Button {

                    

                    addItem()

                    

                } label: {

                    Text("Save")

                }.padding()

                

                List {

                    ForEach(items, id: \.self) { item in

                        

                        NavigationLink(destination: ToDo(list:item)) {

                            HStack {

                                Text(item.name ?? "Not found")

                                Button {

                                    

                                    self.showeditsheet = true

                                } label: {

                                    Text("Edit")

                                }.buttonStyle(.bordered)

                            }

                            

                            

                        }

                        .sheet(isPresented: $showeditsheet){

                            EditSheet(itemId: item.id!, item:item)

                        }

                        

                    }

                    .onDelete(perform: deleteItems)

                }

            }

        }

        

    }

    

    //MARK: - Hinzufügen der Listen

    private func addItem() {

        withAnimation {

            let newItem = Item(context: viewContext)

            newItem.name = name

            newItem.id = UUID()

            

            do {

                try viewContext.save()

            } catch {

                // Replace this implementation with code to handle the error appropriately.

                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                let nsError = error as NSError

                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")

            }

        }

    }

    

    

    private func deleteItems(offsets: IndexSet) {

        withAnimation {

            offsets.map { items[$0] }.forEach(viewContext.delete)

            

            do {

                try viewContext.save()

            } catch {

                // Replace this implementation with code to handle the error appropriately.

                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                let nsError = error as NSError

                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")

            }

        }

    }

}


import SwiftUI

import Foundation

import CoreData



struct EditSheet: View {

    @Environment(\.managedObjectContext) private var viewContext

    @Environment(\.dismiss) private var dismiss

    

    @State private var name: String = ""

    private var item: Item

    var itemid: UUID

    

    

    init(itemId: UUID, item: Item) {

        self.item = item

        self.itemid = item.id!

        self.name = item.name ?? ""

    }

    

    

    var body: some View {

        VStack{

            TextField("Text eingeben", text: $name)

                .padding()

            Button {

                

                update(item: item, itemid: item.id!)

                dismiss()

                

                

            } label: {

                Text("Save")

            }.padding()

        }.onAppear{

            name = item.name!

            

        }

    }

    func update(item:Item, itemid id: UUID){

        let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Item")

        fetchRequest.predicate = NSPredicate(format: "id == %@", id as CVarArg)

        

        do {

            let results = try viewContext.fetch(fetchRequest) // Rufe den Fetchrequest auf

            if results.count > 0 {

                let objectToUpdate = results[0] // Nehme das erste

                if objectToUpdate.value(forKey: "id") as? UUID == id {

                    objectToUpdate.setValue(name, forKey: "name") //Editiere den Key "name" zum übergebenen Wert

                    

                    do {

                        try viewContext.save() // Speichern

                        print("Erfolgreich gespeichert!")

                    } catch let error as NSError {

                        print("Fehler beim Speichern. \(error), \(error.userInfo)")

                    }

                } else {

                    print("Fehler: das aktualisierte Element hat eine andere ID als die übergebene.")

                }

            } else {

                print("Fehler: keine Ergebnisse für die angegebene ID gefunden.")

            }

        } catch let error as NSError {

            print("Fehler beim Fetchen. \(error), \(error.userInfo)")

        }

    }

    

}


editname is from an earlier version. Here it is without meaning.

Id when I press the Edit button on the ContenView: 78AB2468-CA12-4A3B-8C55-67F366FDFE7D

Id when I press the Save button on the EditView: BCC1267C-ACF6-4926-9BE8-5C4D8062E108

Id the update func print: BCC1267C-ACF6-4926-9BE8-5C4D8062E108

objecttoupdate: <Item: 0x600001196bc0> (entity: Item; id: 0x81a3114acc5b93d0 x-coredata://8BCB76FB-F8F2-43CE-B703-1E6C1BB03811/Item/p3;

data: {     id = "BCC1267C-ACF6-4926-9BE8-5C4D8062E108";

    liste =( );

    name = Asdfasdf;

    timestamp = nil;

})

I therefore believe that the update func also works. But the func does not get the ID of the selected object. The first print ID in the ContentView always changes depending on the object. The other IDs are always the same, as if it always selects the same object only. Or does not get the correct ID transmitted.

Thanks for the feedback. But please, edit your code properly ; it is so hard to use with all those extra blank lines.

We do not see in code where are the print(id). So hard to analyse.

Sorry.


import SwiftUI

import CoreData

import Foundation



struct ContentView: View {

    @Environment(\.managedObjectContext) private var viewContext

    @FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Item.name, ascending: false)],

                  animation: .default)

    

    var items: FetchedResults<Item>

    
    @State private var name: String = ""

   
    @State private var showeditsheet = false


    var body: some View {

        NavigationView {

            VStack{

                TextField("Text eingeben", text: $name)

                    .padding()

                Button {

                    addItem()

                } label: {

                    Text("Save")

                }.padding()

                List {

                    ForEach(items, id: \.self) { item in

                        NavigationLink(destination: ToDo(list:item)) {

                            HStack {

                                Text(item.name ?? "Not found")

                                Button {

                                    print(item.id)

                                    self.showeditsheet = true

                                } label: {

                                    Text("Edit")

                                }.buttonStyle(.bordered)

                            }
                        }

                        .sheet(isPresented: $showeditsheet){

                            EditSheet(itemId: item.id!, item:item)

                        }
                    }

                    .onDelete(perform: deleteItems)
                }
            }
        }
    }

    //MARK: - Hinzufügen der Listen

    private func addItem() {

        withAnimation {

            let newItem = Item(context: viewContext)

            newItem.name = name

            newItem.id = UUID()

            do {

                try viewContext.save()

            } catch {

                let nsError = error as NSError

                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")

            }

        }

    }
    private func deleteItems(offsets: IndexSet) {

        withAnimation {

            offsets.map { items[$0] }.forEach(viewContext.delete)

            do {

                try viewContext.save()

            } catch {

                let nsError = error as NSError

                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")

            }

        }

    }

}



import SwiftUI

import Foundation

import CoreData



struct EditSheet: View {

    @Environment(\.managedObjectContext) private var viewContext

    @Environment(\.dismiss) private var dismiss

    @State private var name: String = ""

    private var item: Item

    var itemid: UUID


    init(itemId: UUID, item: Item) {

        self.item = item

        self.itemid = item.id!

        self.name = item.name ?? ""

    }

    var body: some View {

        VStack{

            TextField("Text eingeben", text: $name)

                .padding()

            Button {

                print(item.id)

                update(item: item, itemid: item.id!)

                dismiss()

            } label: {

                Text("Save")

            }.padding()

        }.onAppear{

            name = item.name!
        }

    }

    func update(item:Item, itemid id: UUID){

        let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Item")

        fetchRequest.predicate = NSPredicate(format: "id == %@", id as CVarArg)

        do {

            let results = try viewContext.fetch(fetchRequest)

            if results.count > 0 {

                let objectToUpdate = results[0]

                if objectToUpdate.value(forKey: "id") as? UUID == id {

                    objectToUpdate.setValue(name, forKey: "name")

                    do {

                        print (id)

                        print(objectToUpdate)

                        try viewContext.save()

                        print("Erfolgreich gespeichert!")

                    } catch let error as NSError {

                        print("Fehler beim Speichern. \(error), \(error.userInfo)")

                    }

                } else {

                    print("Fehler")

                }

            } else {

                print("Fehler")

            }

        } catch let error as NSError {

            print("Fehler beim Fetchen. \(error), \(error.userInfo)")

        }
    }
}

To avoid extra blank lines, paste with Paste and Match Style command from the Edit menu

  • How is Item struct defined ?

  • on line 29 of EditSheet, is name optional ?

  • You probably pass the wrong item

  • In ContentView

  • add a State var selectedItem: Item line 14

  • set it on line 37

  • you will have to reset to nil when done

  • Change lines 44 and 45

Tell what you get.

1. import SwiftUI
2. import CoreData
3. import Foundation
4. 
5. struct ContentView: View {
6. 
7.     @Environment(\.managedObjectContext) private var viewContext
8.     @FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Item.name, ascending: false)],animation: .default)
9. 
10.     var items: FetchedResults<Item>
11. 
12.     @State private var name: String = ""
13.     @State private var showeditsheet = false
14.     @State private var selectedItem : Item? = nil  // <<-- ADD THIS
15. 
16.     var body: some View {
17. 
18.         NavigationView {
19.             VStack {
20.                 TextField("Text eingeben", text: $name)
21.                     .padding()
22. 
23.                 Button {
24.                     addItem()
25.                 } label: {
26.                     Text("Save")
27.                 }.padding()
28. 
29.                 List {
30.                     ForEach(items, id: \.self) { item in
31.                         NavigationLink(destination: ToDo(list:item)) {
32. 
33.                             HStack {
34.                                 Text(item.name ?? "Not found")
35. 
36.                                 Button {
37.                                     print("item ID for Edit", item.id) ; self.selectedItem = item  // <<-- ADD THIS
38.                                     self.showeditsheet = true
39.                                 } label: {
40.                                     Text("Edit")
41.                                 }.buttonStyle(.bordered)
42.                             }
43.                         }
44.                         .sheet(isPresented: $showeditsheet && $selectedItem != nil) {  // <<-- CHANGE
45.                             EditSheet(itemId: selectedItem.id!, item:selectedItem) // CHANGE HERE     EditSheet(itemId: item.id!, item:item)
46.                         }
47.                     }
48.                     .onDelete(perform: deleteItems)
49.                 }
50.             }
51.         }
52.     }
53. 
54.     //MARK: - Hinzufügen der Listen
55. 
56.     private func addItem() {
57. 
58.         withAnimation {
59. 
60.             let newItem = Item(context: viewContext)
61.             newItem.name = name
62.             newItem.id = UUID()
63. 
64.             do {
65.                 try viewContext.save()
66.             } catch {
67.                 let nsError = error as NSError
68.                 fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
69.             }
70. 
71.         }
72. 
73.     }
74.     
75.     private func deleteItems(offsets: IndexSet) {
76. 
77.         withAnimation {
78.             offsets.map { items[$0] }.forEach(viewContext.delete)
79.             do {
80.                 try viewContext.save()
81.             } catch {
82.                 let nsError = error as NSError
83.                 fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
84.             }
85.         }
86.     }
87. }
1. struct EditSheet: View {
2. 
3.     @Environment(\.managedObjectContext) private var viewContext
4.     @Environment(\.dismiss) private var dismiss
5.     @State private var name: String = ""
6.     private var item: Item
7.     var itemid: UUID
8. 
9.     init(itemId: UUID, item: Item) {
10.         self.item = item
11.         self.itemid = item.id!
12.         self.name = item.name ?? ""
13.     }
14. 
15.     var body: some View {
16. 
17.         VStack {
18.             TextField("Text eingeben", text: $name)
19.                 .padding()
20. 
21.             Button {
22.                 print(item.id)
23.                 update(item: item, itemid: item.id!)
24.                 dismiss()
25.             } label: {
26.                 Text("Save")
27.             }.padding()
28.         }.onAppear{
29.             name = item.name!
30.         }
31. 
32.     }
33. 
34.     func update(item:Item, itemid id: UUID){
35. 
36.         let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Item")
37.         fetchRequest.predicate = NSPredicate(format: "id == %@", id as CVarArg)
38. 
39.         do {
40.             let results = try viewContext.fetch(fetchRequest)
41.             if results.count > 0 {
42.                 let objectToUpdate = results[0]
43.                 if objectToUpdate.value(forKey: "id") as? UUID == id {
44.                     objectToUpdate.setValue(name, forKey: "name")
45.                     do {
46.                         print("id to update", id)  // <<-- Add a label to print
47.                         print(objectToUpdate)
48.                         try viewContext.save()
49.                         print("Erfolgreich gespeichert!")
50.                     } catch let error as NSError {
51.                         print("Fehler beim Speichern. \(error), \(error.userInfo)")
52.                     }
53.                 } else {
54.                     print("Fehler")
55.                 }
56.             } else {
57.                 print("Fehler")
58.             }
59. 
60.         } catch let error as NSError {
61.             print("Fehler beim Fetchen. \(error), \(error.userInfo)")
62.         }
63.     }
64. }

I have no struct for item. Thought if I create it in the database and add attributes that it would be sufficiently defined with that. I pick Codegen: Class Definition

This is what I got:

Sorry, I could not test so I may have some typos.

The principle is to make sure you use the selectedItem as it seems, surprisingly, not to be the case presently.

  • remove && $selectedItem != nil on line 54 or test with parenthesis (isPresented: $showeditsheet && ($selectedItem != nil) )
  • replace selectedItem by $selectedItem! on line 55

Could you also print the results you get in update

 let results = try viewContext.fetch(fetchRequest)
print("results", results)

if you cannot do it in modified code, do it in older (without selectedItem)

I pick Codegen: Class Definition for Item.

I have tested a few things again. And according to the print statements the problem is that the EditSheet gets the wrong id passed and inside the EditSheet everything fits with the ID.

item ID for Edit: 2E5B0F87-362B-4E77-8AC0-95EF1ED18AC0

id during the press of the save button at the edit view: 66C8A3FD-0980-44BD-AE7D-8411D7A07A9C

id to update: 66C8A3FD-0980-44BD-AE7D-8411D7A07A9C

<Item: 0x600001eb87d0> (entity: Item; id: 0x9b04be453a90c036 x-coredata://8BCB76FB-F8F2-43CE-B703-1E6C1BB03811/Item/p11; data: {

    id = "66C8A3FD-0980-44BD-AE7D-8411D7A07A9C";

    liste =     (

    );

    name = D;

    timestamp = nil;

})

So, print items array in some func to understand which item is passed.

  • Is it the last on the list ?
  • Does it occur when you tap Edit first time ? Second time ?

If it occurs only on second tap, reason is probably that showeditsheet remains true once a button has been tapped (you never reset to false).

So you should also test for showeditsheet value somewhere. for instance here:

33.                             HStack {
34.                                 Text(item.name + "\(showeditsheet ? "T" : "F") "?? "Not found")
35. 

CoreData always takes the same item
 
 
Q