I have a fetched results controller which isn't calling its' delegate when it has a predicate. The predicate is to only include Conversation objects which have events:
let predicate = NSPredicate(format: "events.#count > 0")
let fetchRequest = Conversation.MR_requestAllSortedBy(
"mostRecentMessage.eventDate",
ascending: false,
withPredicate: predicate)
self.fetchedResultsController = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: MagicalRecordStack.defaultStack()!.context,
sectionNameKeyPath: nil,
cacheName: nil)
self.fetchedResultsController.delegate = self
self.fetchedResultsController.MR_performFetch()
This works for an initial fetch, but upon data being added, it doesn't call the delegate. Upon removing the predicate, it does call the delegate.
Is there another way I should be doing this?
The fetch predicate can only check against Core Data attributes because the objects themselves remain as faults. The fetch predicate doesn't get all of the objects of the requested entity and then filter them, it just fetches the ones that pass the filter predicate.
As a work around you could fetch the Event entity. Then use the section key to group by Conversation.
Related
I have a method that will fetch a batch of categories from persistent storage. I have added a sort descriptor that sorts the results alphabetically by name (attribute), which works perfectly. But I have 1 item that I would like to show up on top of the list which is "Uncategorized" and another one that I want to show at the bottom of the list which is "Other category". Can anyone help me on how I can pull this off?
What I've tried so far is add another sort descriptor to sort the items by "priority". Priority is an Int16 attribute in the Categories entity. But that didn't achieve what I wanted.
Here's the code to the method.
private func fetchCategories(predicate: NSPredicate, sortDescriptors: [NSSortDescriptor]) -> NSFetchedResultsController<Category> {
let fetchedResultsController: NSFetchedResultsController<Category>
let fetchRequest: NSFetchRequest<Category> = Category.fetchRequest()
fetchRequest.predicate = predicate
fetchRequest.sortDescriptors = sortDescriptors
//fetch
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
do {
try fetchedResultsController.performFetch()
return fetchedResultsController
} catch {
debug.log(error.localizedDescription)
}
return fetchedResultsController
}
And here's the code calling that method:
func categories(ofType type: TransactionType) -> NSFetchedResultsController<Category> {
let predicate = NSPredicate(format: "tType == %# || tType == \(TransactionType.none.rawValue)", "\(type.rawValue)")
let nameSort = NSSortDescriptor(key: "name", ascending: true)
let fetchedCategories = fetchCategories(predicate: predicate, sortDescriptors: [nameSort])
return fetchedCategories
}
Update:
The priority + name sort solution did not work for me because I think the alphabetical takes precedence. Here's an example case.
Category name -> Priority
Travel -> 4
Food -> 3
Other category -> 255
Entertainment -> 1
Uncategorized -> 0
Expected result
Uncategorized
Entertainment
Food
Travel
Other category
Actual result
Entertainment
Food
Other category
Travel
Uncategorized
So basically, I wanted the ordering that's shown in the expected results above. Where Uncategorized, having a priority of 0 will be shown first, and the Other category item is shown last, having a priority of 255. Everything in between should be in alphabetical order.
In Swift, when you declare a NSFetchedResultsController like below, you have to state the type of result that the NSFetchedResultsController will be returning, e.g. NSFetchedResultsController<Quote>:
fileprivate lazy var fetchedResultsController: NSFetchedResultsController<Quote> = {
// Create Fetch Request
let fetchRequest: NSFetchRequest<Quote> = Quote.fetchRequest()
// Configure Fetch Request
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
// Create Fetched Results Controller
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
// Configure Fetched Results Controller
fetchedResultsController.delegate = self
return fetchedResultsController
}()
If I wanted to use the same tableview with two different Core Data Entities and I injected a value for the entity into the UITableViewController as either a String or an Enum how would I go about rewriting the code to reflect the injected value?
There is nothing to inject. You get automatic type inference throughout.
When you say Quote.fetchRequest(), you automatically get a NSFetchRequest<Quote> as its result. You then use that fetch request in the initializer of an NSFetchedResultsController and you automatically get a NSFetchedResultsController<Quote>. One entity type, one fetch request type, one result controller type.
If you have another entity type you just use another fetched results controller.
I'm trying to perform a fetch for an entity in coreData. This entity has a one to one relation with another entity. I want to only get the items in the first entity that have a relation to a particular item in the second entity. I'm trying this predicate which I know is incorrect:
let fetchRequest: NSFetchRequest<ItemA> = NSFetchRequest(entityName: "ItemA")
fetchRequest.predicate = NSPredicate(format: "itemA.itemB.itemBId == %d", Int(itemB.itemBId))
Currently you are using itemA.itemB.itemBId , here you are using a new entity of itemA , where as you dont need to give its name, since this predicate will be applied on itemA entity, so either you can use only itemB.itemBId inside predicate or you can use SELF.itemB.itemBId (I am not sure about SELF or self, obviously you can look it up).
So I think you can get the items of type itemA whose relation itemB has an id of itemBId like this:
let fetchRequest: NSFetchRequest<ItemA> = NSFetchRequest(entityName: "ItemA")
fetchRequest.predicate = NSPredicate(format: "itemB.itemBId == %d", Int(itemB.itemBId))
I'm trying to do a fetch request that checks for 2 things.
Here is my Data:
Person - entity
Statement - entity
The Person entity has a relationship to statements as To Many. The statement entity has an attribute called amountOwed. This is the property I want to check in the predicate.
EDIT
What I am trying to do is this.
Check all of my Persons entities for a name, lets say Bob.
Once I find the Bob entity I want to check all of his Statement entities for an attribute called amountOwed and see if it's greater or less then 0.
Check for a name in the Person Entity. When that name matches, use that entity.
Check if the amountOwed in a Statement entity is greater or less then 0.
This is what I have been trying to get to work.
let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
fetchRequest.predicate = NSPredicate(format:"name == %# AND #statement.amountOwed >= 0", personName))
let sort = NSSortDescriptor(key: #keyPath(Person.name), ascending: true)
fetchRequest.sortDescriptors = [sort]
positiveFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: coreDataStack.managedContext, sectionNameKeyPath: nil, cacheName: nil)
do {
try positiveFetchedResultsController.performFetch()
} catch let error as NSError{
print("Fetching error: \(error), \(error.userInfo)")
}
I am getting this error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'to-many key not allowed here'
I found the AND method here: using AND link
Edit
You should build your FRC to fetch Statement objects, not Person objects:
let fetchRequest: NSFetchRequest<Statement> = Statement.fetchRequest()
Assuming the statement relationship has an inverse (to-one) relationship named person, then you can use the following predicate to ensure you fetch only the statements with a positive amountOwed relating to a person with a given name:
fetchRequest.predicate = NSPredicate(format:"person.name == %# AND amountOwed >= 0", personName)
(and similarly for the negative amountOwed). Specify sort descriptors to get whatever sort order you wish. Your FRCs' fetchedObjects arrays will then contain Statement objects which you can use to populate the table view: the positive FRC for section 0 and negative FRC for section 1 (or vice versa).
NB. because you are fetching the Statement objects, it is possible to achieve what you want with just one FRC - but only if you are happy with the statements being sorted by amountOwed (ascending or descending). If you wish to do this, I can provide further detail.
Try this:
NSPredicate(format:"name == %# AND statement.#amountOwed >= 0", personName)
The AND has to be part of the string as the example you attached...
I have a simple iOS8 Core Data application that lists information about a patient in a Master Detail structure. Xcode 6.3.1
That piece of the app works fine - a tableView of Patients with details on each Patient in a second view controller. Core Data has just two entities - Patient and Note and I set a one-to-many relationship. Each Patient can have many Notes, but each Note belongs to only one Patient.
On the Patient detail page I have a button that launches a second tableview-viewcontroller pair for the list of Notes and Note detail that belongs to that Patient.
Using a fetchedresultscontroller, the data is correctly populated in the Patient table view and the Patient Detail view. However, I can't seem to tie the Notes to an individual Patient. My attempts to retrieve notes belonging to an individual Patient have not worked either, but I suspect that is due to the fact that I haven't linked the Notes to a Patient object when saving the Note.
The Note save code:
func addNewNote() {
makeEntryFieldsEnabledYES()
//the Patient that owns this record is available as self.patientPassedIn
//println("patientPassedIn is: \(patientPassedIn)")
let context = kAppDelegate.managedObjectContext
let entity = NSEntityDescription.entityForName("Note", inManagedObjectContext: kAppDelegate.managedObjectContext!)
var noteToAdd = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: kAppDelegate.managedObjectContext!) as! Note
noteToAdd.setValue(self.titleField.text, forKey: "title")
noteToAdd.setValue(self.noteCategoryField.text, forKey: "noteCategory")
noteToAdd.info = informationView.text
noteToAdd.creationDate = NSDate()
kAppDelegate.saveContext()
performSegueWithIdentifier("unwindToNotesTableViewController", sender: self)
}//addNewNote
I do pass the relevant Patient object to the Note view controller, but I have not been able to link the note to
that object.
How does one construct a save operation for a to-many entity so that the many Note objects relate only to the single
Patient object? Then how do you construct the fetch request to fetch only the Note objects that belong to
a given Patient? I want to pass a Patient object to the predicate and retrieve the appropriate notes.
The Note fetching code:
var fetchedResultsController: NSFetchedResultsController {
managedObjectContext = kAppDelegate.managedObjectContext
if myFetchedResultsController != nil {
return myFetchedResultsController!
}
let fetchRequest = NSFetchRequest()
// Check this: auto generate didn't work - fix that
let entity = NSEntityDescription.entityForName("Note", inManagedObjectContext: managedObjectContext)
fetchRequest.entity = entity
fetchRequest.fetchBatchSize = 20
let sortDescriptor = NSSortDescriptor(key: "creationDate", ascending: false)
let sortDescriptors = [sortDescriptor]
fetchRequest.sortDescriptors = [sortDescriptor]
//prove that you have passed the Patient object
var testString = passedInPatient?.lastName
println(testString!)
//try this for only retrieving the notes for the given patient
//this does not work
var notePredicate = NSPredicate(format: "passedInPatient")
fetchRequest.predicate = notePredicate
//
//try above
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
var countError : NSError? = nil
var count = managedObjectContext.countForFetchRequest(fetchRequest, error: &countError)
println("The count is \(count)")
aFetchedResultsController.delegate = self
myFetchedResultsController = aFetchedResultsController
var error: NSError? = nil
if !myFetchedResultsController!.performFetch(&error) {
//create an alert if the fetching fails
println("Unresolved error \(error), \(error!.userInfo)")
abort()
}//if !
return myFetchedResultsController!
}//var fetchedResultsController
I do pass the relevant Patient object to the Note view controller, but I have not been able to link the note to that object.
How does one construct a save operation for a to-many entity so that the many Note objects relate only to the single Patient object? Then how do you construct the fetch request to fetch only the Note objects that belong to a given Patient? I want to pass a Patient object to the predicate and retrieve the appropriate notes.
In your addNewNote function, add another line to set the relationship (it is easiest to set the to-one relationship; CoreData will automatically set the inverse relationship for you):
noteToAdd.patient = patientPassedIn
In your NSFetchedResultsController, the format you need for your predicate is like this:
var notePredicate = NSPredicate(format: "patient == %#", passedInPatient)
fetchRequest.predicate = notePredicate