Sort entities based on fetched property - ios

In my application (written in Swift) I use a model for Core Data with two configurations, each of these uses different save path (one is read-only in the application bundle and another is in the Documents folder). I set in one of two configurations an entity "Domanda" with the following attributes:
In the other configuration I set an entity "StatDomanda" with the following attributes:
"StatDomanda" is used for user data, while "Domanda" is used as preloaded source. I know that I can't set up a relationship between the two entities (two configurations), but I would order the "Domanda" entity based on the "corrette" attribute of the "StatDomanda" entity where StatDomanda.numero == Domanda.numero and StatDomanda .argomento == Domanda.argomento.nomeArgomento.
Not all "Domanda" objects have corresponding "StatDomanda" objects.
I set a fetched property (called "statistiche") in the "Domanda" entity with this predicate
$FETCH_SOURCE.numero = numero AND
$FETCH_SOURCE.argomento.nomeArgomento = argomento
but I do not understand how to use it to order the objects.
Thanks for the help!

I found a solution, I don't know if is the best way but it works!
In my core data manager class (ModelManager) I have this function used to fetch all the "Domanda" entities:
func getAllQuestions() -> [Domanda] {
let fetchRequest = NSFetchRequest(entityName: "Domanda")
let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [Domanda]
return fetchResults!
}
In my ViewController I use this code to obtain the sorted array of "Domanda" entities based on attribute "corrette" of "statistiche" fetched property:
override func viewDidLoad() {
super.viewDidLoad()
var arrayDomande = ModelManager.instance.getAllQuestions()
var sortedArrayDomande = self.sortArray(arrayToSort: arrayDomande)
}
func sortArray(arrayToSort sarray: Array<Domanda>) -> Array<Domanda> {
// 1 - Filter the array to find only questions that have stats
var onlyAnswered = sarray.filter({($0.valueForKey("statistiche") as [Domanda]).count == 1})
// 2 - Sort the array based on fetched property
var sortedArrayDomande = sorted(onlyAnswered, {
let first = ($0.valueForKey("statistiche") as [StatDomanda])[0]
let firstC = first.corrette.integerValue
let second = ($1.valueForKey("statistiche") as [StatDomanda])[0]
let secondC = second.corrette.integerValue
return firstC > secondC
})
return sortedArrayDomande
}
or even in condensed form:
// 2 - Sort the array based on fetched property
var sortedArrayDomande = sorted(onlyAnswered, {($0.valueForKey("statistiche") as [StatDomanda])[0].corrette.integerValue > ($1.valueForKey("statistiche") as [StatDomanda])[0].corrette.integerValue})
Goodbye guys, see you next time!

Related

Swift Realm - get types of all database objects

I have a list of various objects in my Realm Database all of which are created as default ClassName: Object classes. Is there any way to get types (classes names) of those objects, that can be saved as variables, or create an enum of these types?
The issue is that Realm Results objects are homogenous - they only contain one object type. That translates to meaning you will never have a situation where Results would contain Person and Dog classes at the same time.
That being said we have the option of using the Realm AnyRealmValue
Let me set this up: Suppose you have a PersonClass and a DogClass
let dog = DogClass()
let person = PersonClass()
and a class with a List that can contain AnyRealmValue
class MyClass: Object {
#Persisted var myList = List<AnyRealmValue>()
}
when then need to cast those objects to AnyRealmValue to make this work
let obj0: AnyRealmValue = .object(dog)
let obj1: AnyRealmValue = .object(person)
and we add those to the List
let m = MyClass()
m.myList.append(obj0)
m.myList.append(obj1)
You mentioned switch but here's a simple if...then clause to handle them differently - depending on the class
if let person = item.object(PersonClass.self) {
print("is a person")
} else if let dog = item.object(DogClass.self) {
print("is a dog")
}

How to list a Property in realm Swift 3

How to list a property in realm DB like SELECT columnName FROM mytablein SQL?
Here is my try:let person = self.realm.objects(Person.self).filter("age")
You can access a single property (since Realm models are native objects, they have properties, not columns) of all instances of your particular model class stored in Realm using map.
filter, as its name suggests can be used to only work on a subset of all instances of a certain type that all fulfilled the same condition (for example you can use filter to find all people whose age is above 18 by saying: let adults = self.realm.objects(Person.self).filter("age > 18")).
Get the age property of all instances of Person persisted in Realm using map:
let people = self.realm.objects(Person.self)
let ages = people.map{$0.age}
or in one line giving an Array as an output:
let ages = Array(self.realm.objects(Person.self)).map{$0.age}
you can get list of records like this
let realmCities = try! Realm()
lazy var arrDefaultCities: Results<Cities> = { self.realmCities.objects(Cities.self).sorted(byKeyPath: "cityName", ascending: true) }()
func filterCities()
{
let statePredicate = NSPredicate(format: "stateId = %d", objState.stateId)
arrDefaultCities = try! Realm().objects(Cities.self).filter(statePredicate).sorted(byKeyPath: "cityName", ascending: true)
self.filterArrCities.removeAll()
for objCities : Cities in arrDefaultCities{
if objCities.cityName == APP_DELEGATE.currentCity
{
self.objCity = objCities
}
self.filterArrCities.append(objCities.cityName)
}
}

Adding up an attribute inside entity for CoreData ios 10

I have a tableView and that tableview is being populated with data from Coredata. The data breaks down like this.
Entity - Person
Entity - Statement
The statement entity has an attribute called amountOwed and it is of decimal type.
The relationships is that a person can have many statements, but each statement belongs to a single person.
Here is the path of the data that I would like to add up. Person > Statement > AmountOwed.
In the tableView function I have a let that represents the Person entity.
let person = fetchedResultsController.object(at: indexPath)
I know its working because I can print out the persons name like so
print(person.name) // Bob
What I want to be able to do is add up all the amountOwed attributes for each Person inside the Statement entity and display them on a cell.
I have been trying to follow an example of calculated fetches but I seem to not quiet understand how to target my Statements Entities which are linked to each Person entity.
let fetchRequest = NSFetchRequest<NSDictionary>(entityName:"statement")
fetchRequest.resultType = .dictionaryResultType
let sumExpressionDesc = NSExpressionDescription()
sumExpressionDesc.name = "sumDeals"
let specialCountExp = NSExpression(forKeyPath: #keyPath(Person.statement[indexPath].amountOwed))
sumExpressionDesc.expression = NSExpression(forFunction: "sum:", arguments: [specialCountExp])
sumExpressionDesc.expressionResultsType = .interger32AttributeType
fetchRequest.propertiesToFetch = [sumExpressionDesc]
do{
let results = try coreDataStack.managedContext.fetch(fetchRequest)
let resultDict = results.first!
let numDeals = resultDict["sumDeals"]
print(numDeals!)
}
catch let error as NSError{
print("Count not fetched \(error), \(error.userInfo)")
}
Do I need to fetch a Statement entity or should I just use the FetchedREsultsController? If I do use my fetchedResultsController does the keypath to the Statement Entity look like this
person[indexPath].statement.amountOwed
You can do that in one line. If the relationship from Person to Statement is called statements, you get the total of the amounts with
let amountTotal = newPerson.value(forKeyPath: "statements.#sum.amount") as? Int64
Change the downcast at the end from Int64 to whatever is appropriate for your amount attribute-- Double or whatever.
OK on your People+CoreDataClass add:
var totalOwed: Float {
get {
var value: Float = 0
if let statements = self.statements.allObjects() as? [Statement] {
for s in statements {
value = value + s.sum
}
}
return value
}
}
And remove all the code from your fetch that is unnecessary

Can't load Core Data in to-many attribute

I have been unable to add and read core data attributes in a one-to-many
relationship. After reading many SO, Apple Docs and other articles I still have
not accomplished this. To make a simple test, I created a standard Master Detail
App with Core Data. This works with no issues for the main entity.
The core data relationship is as shown below.
Here is the code to place some test data into the store. Note that I am
attempting to add the to-many data as a Set for each InvItem and have made the
keywordList attribute Transformable. I thought this was the best approach, but
obviously that is not working here.
func seedInvItems(num : Int) {
for index in 1...num {
let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName("InvItem", inManagedObjectContext: kAppDelegate.managedObjectContext) as! InvItem
newManagedObject.name = "myName\(index)"
newManagedObject.category1 = "myCategory1x\(index)"
newManagedObject.compartment = "myCompartment\(index)"
newManagedObject.entryDate = NSDate()
//and for the one-to-many relationship
var myStoreKeywordSet = Set<String>()
myStoreKeywordSet = ["one","two","three"]
// do an insert just for grins
myStoreKeywordSet.insert("four")
let newManagedObject2 = NSEntityDescription.insertNewObjectForEntityForName("InvItemKeyword", inManagedObjectContext: kAppDelegate.managedObjectContext) as! InvItemKeyword
newManagedObject2.keywordList = myStoreKeywordSet
}//for in
kAppDelegate.saveContext()
let fetchRequestTest = NSFetchRequest(entityName: "InvItem")
let sorter : NSSortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequestTest.sortDescriptors = [sorter]
var resultsTest : [InvItem]?
do {
resultsTest = try kAppDelegate.managedObjectContext.executeFetchRequest(fetchRequestTest) as? [InvItem]
for object in resultsTest! {
let myRetrieveKeywordSet = object.invitemkeywords
print(myRetrieveKeywordSet)
}//for in
} catch let error as NSError {
//what happens on failure
print("And the executeFetchRequest error is \(error.localizedDescription)")
}//do catch
}//seedInvItems
For completeness, I did create the NSManagedObject subclasses for the Core
Data Entities.
When running the app, the master detail for InvItem behaves as expected but I
get no storage of the to-many items.
Here is the console log (for one InvItem):
Optional(Relationship 'invitemkeywords' fault on managed object (0x7f985a62bb70) (entity: InvItem; id: 0xd000000000700000 ; data: {
category1 = myCategory1x3;
compartment = myCompartment3;
entryDate = "2016-02-09 02:10:21 +0000";
invitemkeywords = "";
name = myName3;
}))
Looking at the database, there is no data for the keywordList.
Any help would be appreciated. Xcode 7.2.1 IOS 9.2
You need to set the relationship from newManagedObject to newManagedObject2. In fact, because the relationship is one-many, it is easier to set the to-one relationship:
newManagedObject2.invitem = newManagedObject
and let CoreData handle the inverse, to-many relationship.
As to the keyWordList not being populated, I wonder whether your SQL browser is unable to decode CoreData's NSSet? I also wonder whether you need to have keywordList as an NSSet? The relationship from InvItem is already to-many, so your model implies that each InvItem can have many InvItemKeywords, each of which holds many keywords in its keywordList attribute: is that what you intended?

I'm having trouble fetching entities from core data based on relationships with predicates

Quick background: I have en ExamEntity, which is linked via a relationship to sections covered in the exam (SectionEntity), which is linked via a relationship to the subsections under each section (SubsectionEntity).
What I'm try to do is fetch the sections and related subsections which will be covered be a selected Exam, and store them in a dictionary which can hold multiple string values per key.
My code starts like this:
var sectionsDictionary = [String:[String]]()
//---Fetch Section Entity
let sectionPredicate = NSPredicate(format: "(exam = %#)", selectedExam)
let sectionsFetchRequest:NSFetchRequest = SectionEntity.MR_requestAllWithPredicate(sectionPredicate)
let fetchedSections:NSArray = SectionEntity.MR_executeFetchRequest(sectionsFetchRequest)
All good here. But then I try and fetch the subsections related to the selected sections and things go wrong.
following Code:
for section in fetchedSections {
let sectionName = section.valueForKey("name") as! String
// //error occurs on the line below
let subsectionPredicate = NSPredicate(format: "(section = %#)", section as! SectionEntity)
let subsectionsFetchRequest:NSFetchRequest = SubsectionEntity.MR_requestAllWithPredicate(subsectionPredicate)
let fetchedSubsections:NSArray = SubsectionEntity.MR_executeFetchRequest(subsectionsFetchRequest)
The error I get is:
Could not cast value of type 'NSManagedObject_SectionEntity_' (0x7fb363e18580) to 'MyApp.SectionEntity' (0x10c9213e0).
The next few lines are supposed to iterate through the fetched results, adding their names as values to the dictionary with section names as keys.
for subsection in fetchedSubsections{
let subsectionName = subsection.valueForKey("name") as! String
sectionsDictionary[sectionName] = [subsectionName]
}
}
thanks in advance.
You're not defining the type of the class for your record
Go to the Project.xcdatamodeld file, and select your SectionEntity
Check if the field Class is like this
If it is then write in the Class field "SectionEntity" and press Enter, it should be like this
Then try again
You need to make sure that the elements of the collection you enumerate are of the correct type.
Presumably, Magical Record will return an NSArray of NSManagedObject items. You need to make sure Swift knows what type section is.
for object in fetchedSections {
let section = object as! SectionEntity
// ...
}
Also, see my answer about the unsuccessful casts to NSManagedObject subclasses.

Resources