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.
Related
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 working with core data and i have two entity:
SwimmingPool
Parameters
Each swimming pool can have multiple parameters
I have already saved and fetched data for swimming pool.
I've saved them trough a form.
In my AppDelegate i have:
let ad = UIApplication.shared.delegate as! AppDelegate
let context = ad.persistentContainer.viewContext
and here's the func:
var swimmingpool: SwimminPool!
if swimmingPoolToEdit == nil {
swimming = SwimminPool(context: context)
} else {
swimming = swimmingPoolToEdit
}
if let name = swimmingName.text {
swimming.name = name
}
if let volume = swimmingVolume.text {
swimming.volume = volume
.........................................................
ad.saveContext()
and i perform the fetch like so:
var controller: NSFetchedResultsController<SwimmingPool>!
func attemptFetch() {
let fetchRequest: NSFetchRequest<SwimminPool> = SwimminPool.fetchRequest()
let dataSort = NSSortDescriptor(key: "createdAt", ascending: false)
fetchRequest.sortDescriptors = [dataSort]
let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
controller.delegate = self
self.controller = controller
do {
try controller.performFetch()
} catch {
let error = error as NSError
print("\(error.debugDescription)")
}
}
Through a segue i pass all the data to a new controller and through a new form (in an another controller) i'm trying to save the data for the parameters and connect them to the the specific swimming pool.
Do i need to save the data like i did for the swimming pool and the relationship will automatically connect the two entities or do i need to connect them upon saving?
I'm new to swift and i'm not sure how to save related datas
The connection will be made upon saving or when i will perform the parameters fetch?
Thank you!
Create Parameter managed object and then link this parameter to a SwimingPool managed object:
let parameter = Parameters(context: context)
parameter.chlorum = Double(12245454.12211) // insert your value
parameter.oxigen = "your value here"
parameter.createdAt = "your value here"
let swimming = SwimminPool(context: context)
swimming.parameter = parameter
ad.saveContext()
If you want to add Parameters to a specific SwimmingPool
In the segue pass the specific SwimmingPool instance to the form controller.
Create Parameters instances by inserting them into the context.
Add the relationship to the SwimmingPool instance by calling insert on the parameter attribute / relationship or the generated accessor addToParameter of the NSManagedObject subclass.
Save the context. (important as last step).
Note : To avoid confusion name an Entity always in singular form (e.g. SwimmingPool / Parameter), a one-to-one relationship also in singular form (e.g. parameter) and a one-to-many relationship in plural form (e.g. parameters)
PS: You question is ambiguous: You are writing Each swimming pool can have multiple parameters but the model design is Each parameter can have multiple swimming pools
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 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.
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