Reputation: 1303
I have a core data app with the model setup just like the image below:
The relationships are setup like this:
Category<-->>Subcategory<-->>Item
Now, is it possible in one single fetch to get objects from Subcategory and Item entities that have the same attribute called categoryName? I think it can be done by using relationships, but the code I am using is not working which is:
let fetchNote = NSFetchRequest(entityName: "Category")
fetchNote.predicate = NSPredicate(format: "ANY subcategory.name == %@ AND item.name == %@", "Badaue", "Badaue")
let notes = try! appDel.managedObjectContext.executeFetchRequest(fetchNote) as? [Category]
print(notes)
Upvotes: 6
Views: 15953
Reputation: 584
Should try like this: I've two entities 1. Trigger 2. Numbers Relation is Many-to-Many :- A trigger could have one or many Mobile Numbers attached, whereas the number may be part of more than one triggers in the Trigger table.
Step: 1
Step: 2
Now you would have situation Like this:
The code generated by Xcode is like this.
import Foundation
import CoreData
extension MobileNumber {
@nonobjc public class func fetchRequest() -> NSFetchRequest<MobileNumber> {
return NSFetchRequest<MobileNumber>(entityName: "MobileNumber");
}
@NSManaged public var mNumber: String?
@NSManaged public var title: String?
@NSManaged public var triggers: NSSet?
}
// MARK: Generated accessors for triggers
extension MobileNumber {
@objc(addTriggersObject:)
@NSManaged public func addToTriggers(_ value: Trigger)
@objc(removeTriggersObject:)
@NSManaged public func removeFromTriggers(_ value: Trigger)
@objc(addTriggers:)
@NSManaged public func addToTriggers(_ values: NSSet)
@objc(removeTriggers:)
@NSManaged public func removeFromTriggers(_ values: NSSet)
}
Similarly, you will have generated classes for other entities as well. I have defined a function for fetching an Entity with UUID, which is always unique for all the rows of my Trigger table.
func fetchTriggerWithUUID(identifier: String) -> [Trigger]
{
let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Trigger")
let predicate = NSPredicate(format: "uuid = %@", identifier)
fetchRequest.predicate = predicate
do {
let results = try managedObjectContext.fetch(fetchRequest) as! [Trigger]
return results
} catch let error as NSError {
print(error)
}
return []
}
Now wherever I need to play with data of any specific [row] or you can even with a set of [rows], this is so simple here in my case.
let result = AppDelegate.getAppDelegate().fetchTriggerWithUUID(identifier: notifIdentifier).first
numbers.removeAll()
for num in (result?.numbers)!
{
let numHere = num as! MobileNumber
numbers.append(numHere.mNumber!)
}
let messageHere = "Wanted to inform you! \n" + (result?.title)! + "\n" + (result?.desc)!
You can do with as much entities you have in your [.xcdatamodelID] file.
Upvotes: 1
Reputation: 21536
If you fetch Categories
you will get an array of Category
objects. Each of those objects will have a property, subcategory
, which is a set containing all the related Subcategory
objects. And each of those objects will have a property, item
, which is a set containing all the Item
objects related to that Subcategory
. The related objects are "nested" within the Category
and Subcategory
objects.
When you fetch the Categories, you do not need to specify a predicate to get the related objects. They will be automatically fetched as soon as you access the subcategory
or item
properties. So, for example, if myCategory
is a Category
object you have fetched,
let mySubcategories = myCategory.subcategory
for subcat in mySubcategories {
print("\(subcat)")
}
should print each of the related subcategories.
If those properties are nil, it might be that you have not yet established the relationships. That is normally done when you first create the objects. For example, if you create a Subcategory
with categoryName = "Badaue"
, you might establish the relationship with the Category
with name = "Badaue"
like this:
let newSubcategory = ....
newSubcategory.categoryName = "Badaue"
let fetchCategory = NSFetchRequest(entityName: "Category")
fetchCategory.predicate = NSPredicate(format: "name == %@", newSubcategory.categoryName)
let categories = try! appDel.managedObjectContext.executeFetchRequest(fetchNote) as? [Category]
if (categories!.count > 0) {
let firstCategory = categories[0]
newSubCategory.category = firstCategory
} else {
// no existing Category with the correct name, so create one....
let newCategory = ....
// ... and then establish the relationship to the Subcategory
newSubCategory.category = newCategory
}
Upvotes: 1
Reputation: 5188
To avoid confusion, I'd suggest renaming item
to items
to reflect that it's a to-many relationship. Same goes for subcategory
.
EDIT2 - You are asking to get the Subcategory
and Item
objects that have a certain categoryName
, correct? The query you've written above doesn't do that at all.
First, yes, you will need 2 queries, as you can only fetch 1 Entity
type at a time.
Second, let's re-write your syntax because right now it's super dangerous.
do {
let predicate = NSPredicate(format: "categoryName == %@", "yourCategoryHere")
let fetchSubcategory = NSFetchRequest(entityName: "Subcategory")
fetchSubcategory.predicate = predicate
if let subCategoryResults = try context.executeFetchRequest(fetchSubcategory) as? [Subcategory] {
//do stuff
}
let fetchItem = NSFetchRequest(entityName: "Item")
fetchItem.predicate = predicate
if let itemResults = try context.executeFetchRequest(fetchItem) as? [Item] {
//do stuff
}
}catch {
print(error)
}
Upvotes: 2