Reputation: 386
I have two entities: Item (which keeps track of lists) and Tasks (which are task items within lists). In one view of the app, there is a swipe to delete feature which removes the list. This works with the following code:
offsets.map { items[$0] }.forEach(viewContext.delete)
I would like to delete now obsolete tasks for the list being deleted. I first tried this code:
tasks.filter{$0.listID! == listsID}.forEach(viewContext.delete)
I was proud of myself. What an elegant solution. Swift hated it. I get an error that says
Reference to member 'listID' cannot be resolved without a contextual type
I Googled and SOd and got nowhere. I don't know what that error means and can't figure out how to fix it in XCode 12 / iOS 14. So then I came up with the following not so elegant code:
let listsID = offsets.map {items[$0].id!}
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = Tasks.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "listID == %@", listsID)
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
deleteRequest.resultType = .resultTypeObjectIDs
do {
let result = try viewContext.execute(deleteRequest)
guard
let deleteResult = result as? NSBatchDeleteResult,
let ids = deleteResult.result as? [NSManagedObjectID]
else {return}
let changes = [NSDeletedObjectsKey: ids]
NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes,
into: [viewContext])
}
catch {
print(error as Any)
}
listsID captures the UUID of the list and stores it as a variable. However, you'll note that it is stored as an ARRAY (ugh). The fetchRequest.predicate code filters the tasks so that only those that have the attribute of 'listID' (which helps connect the task to the list it belongs to) matching the id of the list being deleted is pulled.
The code compiles (yay!). Then I get the following error when trying to delete a list:
Exception NSException * "Unexpected or improperly formatted UUID parameter with type Swift.__SwiftDeferredNSArray, expected NSUUID or well-formed NSString" 0x0000600003145b30
I'm sure there is a simple way to do this. I played with inverse relationships in Core Data but got nowhere. I didn't know how to tell it that 'id' (UUID) in 'Item' == 'listID' (UUID) in 'Tasks', so deleting the list didn't do anything to the tasks that belong to it.
I tried to create a ForEach loop, but ran into various errors that I couldn't resolve. I'd prefer to use the elegant code that I wrote at the top to make the deletion happen. Any ideas?
Thank you.
Upvotes: 1
Views: 342
Reputation: 386
The credit for this goes to pbasdf for his super helpful responses. I used this link to learn about setting up relationships in Core Data: Seneca SDDS. I was missing one line in my persistence model:
newTask.list = newItem
How I solved this problem (in case it helps someone else) is that I created a one to many relationship for Item (the list of lists), and one to one relationship for Tasks (tasks are a subset of a list). Now, when I create a new task in the persistence script, I have a pointer back to the list it belongs to - hence the single line of code above.
The additional background that helped me understand Core Data relationships can be found in the link above. This snippet in particular was very helpful (in case the page goes 404):
Adding a new object, and setting the relationship Assume that you have a reference to a Company object already; its variable name is c. How do you add a new Employee or Product? Create the new object and set its relationship. The relationship can be configured from either direction. In this section, we will configure it from the perspective of the just-added new object. For example:
// As noted above, assume that you have a reference
// "c" to an existing Company object...
// Create and configure a new employee
let peter = Employee(context: m.ds_context)
peter.name = "Peter McIntyre"
peter.age = 23
// etc.
// Now, set the relationship
peter.company = c
m.ds_save()
That’s it. If it seems too easy, well, it is easy.
Upvotes: 2