Getting objects from NSSet and populating TableView - ios

I have this piece of code as shown below
func getItemsOnList(){
let app = UIApplication.shared.delegate as! AppDelegate
let context = app.persistentContainer.viewContext
//fetchRequest to get the List
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "List")
let predicate = NSPredicate(format: "title == %#", listName)
fetchRequest.returnsObjectsAsFaults = false
fetchRequest.predicate = predicate
if let fetchResults = try? context.fetch(fetchRequest){
if fetchResults.count > 0 {
for listEntity in fetchResults {
let list = listEntity as! List
print(list.title as Any)
itemsOnListSet = list.contains!
What this does is it gets the Items from the specified List using the .contains relationship between the two entities, and saves all the items in to an NSSet.
What i want to do is to populate a TableView with the objects that are in the NSSet.
Is there a function related to NSSet which allows me to get the items from the set? Or should i save the items in an Array instead of an NSSet.
P.S. the .contains relationship is of type NSSet
#NSManaged public var contains: NSSet?

why don't you convert the Set to Array using,
if let _ = list.contains {
let itemsOnListArray = list.contains!.allObjects
}
else
if let unwrappedList = list.contains {
let itemsOnListArray = unwrappedList.allObjects
}
Now use your itemsOnListArray as your tableView's data source :)
EDIT:
Your code
let item = itemsOnListArray[indexPath.row]
cell.textLabel?.text = item as? String
Assumes itemsOnListArray is a array of strings!!! Which is absolutely impossible because list.contains! is a set of NSManagedObjects or if you created mapped subclasses of ManagedObjects than it will contain a set of your managed objects like items.
What you should be doing is (because you have not provided the description of item am assuming item has a name property in it)
let item = itemsOnListArray[indexPath.row] as! Item
cell.textLabel?.text = item.name

Related

Output of fetched data to a variable as an Int

In this example, print gewicht gives an output of optional({10)} i need the output (10) as
an Int assigned to a variable . So the output has to be let mijnGewicht = 10
How can i do that. Iam new to swift, so excuse me for the question.
let appDelegate = UIApplication.shared.delegate as? AppDelegate
let managedObjectContext = appDelegate!.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Dogs")
fetchRequest.predicate = NSPredicate(format: "name = %#", "Toni")
fetchRequest.returnsObjectsAsFaults = false
fetchRequest.relationshipKeyPathsForPrefetching = ["gewicht"]
do {
let fetchedResults = try managedObjectContext.fetch(fetchRequest)
for i in fetchedResults {
dogs.append(i as! NSManagedObject)
for i in dogs {
let gewicht = i.value(forKeyPath: "gewicht.kg")
print(gewicht)
}
Dealing with unspecified NSManagedObject and value(forKeypath: is outdated.
Take advantage of the generic abilities of Core Data. The benefit is no type cast and no Any.
First declare dogs as
var dogs = [Dogs]()
By the way it's highly recommended to name entities in singular form. Semantically you have an array of Dog instances.
Create the fetch request for the specific NSManagedObject subclass Dogs
As gewicht is a to-many relationship you have to use a loop to get all values
let appDelegate = UIApplication.shared.delegate as? AppDelegate
let managedObjectContext = appDelegate!.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<Dogs>(entityName: "Dogs")
fetchRequest.predicate = NSPredicate(format: "name = %#", "Toni")
fetchRequest.returnsObjectsAsFaults = false
do {
let result = try managedObjectContext.fetch(fetchRequest)
dogs.append(contentsOf: result)
for dog in dogs {
for gewicht in dog.gewicht {
let kg = gewicht.kg
print(kg)
}
}
} catch { print(error) }
If the relationship and/or the attribute is declared optional (which is still unclear) you have to unwrap the optional.
And consider that the integer value is Int16, Int32 or Int64 (unfortunately this information is missing, too). There is no Int type in Core Data.
And it's up to you how to distinguish the many values.
Try changing your code like this, it checks if the value you are assigning is not nil and then prints it
for i in dogs {
if let mijnGewicht = i.value(forKeyPath: "gewicht.kg"){
print(mijnGewicht)
}
}
You can try something like this
let gewicht = i.value(forKeyPath: "gewicht.kg")
if let gewichtInt = gewicht as? Int {
print(gewichtInt)
}
for i in dogs {
if let gewicht = i.value(forKeyPath: "gewicht.kg") {
print(gewicht) // this will give you the safe value as Int, if the value is nill it will not come in this if condition
}
let x = i.value(forKeyPath: "gewicht.kg") ?? 0
print (x) //this will give you wrapped safe value of gewicht.kg if exists else it will give you 0
}
here in above example i have shown you two ways to safe cast a value from optional, you can also use guard or guard let on the basis of your requirement

Dynamic filtering with Swift CoreData

I am developing an application that retrieves data from CoreData. I retrieve a list of items from the database and display these on screen.
The user has an option to filter these items for up to 5 categories in 5 separate drop downs. What is the best way of doing this dynamically? What I mean by that, is if the user selects one filter option, only the items that match that filter will be shown, as well as the other filter options then only showing filter options that exist for the already filtered items.
I hope that makes sense!
This is the code I currently have for retrieving the items:
func showDropDown(filterButton: UIButton) -> Void {
selectedButton = filterButton
let popController = UIStoryboard(name: STORYBOARD_NAME,
bundle: nil).instantiateViewController(withIdentifier: STORYBOARD_ID) as! FilterDropDownViewController
popController.modalPresentationStyle = .popover
popController.delegate = self
popController.popoverPresentationController?.permittedArrowDirections = .up
popController.popoverPresentationController?.delegate = self
popController.popoverPresentationController?.sourceView = filterButton
popController.popoverPresentationController?.sourceRect = filterButton.bounds
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Item")
var predicate = NSPredicate(format: "code matches[c] '\(code!)'")
fetchRequest.returnsObjectsAsFaults = false
fetchRequest.predicate = predicate
let entity = NSEntityDescription.entity(forEntityName: "Item",
in: context)
fetchRequest.resultType = .dictionaryResultType
let entityProperties = entity?.propertiesByName
let filterToFetch = "filter\(filterButton.tag)"
let propertiesToFetch: [Any] = [entityProperties![filterToFetch]!]
fetchRequest.propertiesToFetch = propertiesToFetch
fetchRequest.returnsDistinctResults = true
var result = [[String : String]]()
do {
result = try context.fetch(fetchRequest) as! [[String : String]]
} catch {
print("Unable to fetch managed objects for Item).")
}
var filterArray = [Filter]()
for dict in result {
if let search = dict[filterToFetch] {
predicate = NSPredicate(format: "code matches[c] '\(search)'")
let filterCode = DatabaseHelper.fetchRecordsForEntity(entity: "Filter",
managedObjectContext: context,
predicate: predicate) as! [Filter]
filterArray.append(filterCode.first!)
}
}
popController.filterArray = filterArray
present(popController, animated: true, completion: nil)
}
You can do it with these simple steps:
Create a NSFetchResultsController with the appropriate predicate based on the current filter settings (use a NSCompoundPredicate), and fetch with it. Set the view controller as the delegate and reload the data of the collectionView when data changes (it seems you don't expect data to be changing while the user is viewing it, so you can just keep it simple). Don't forget to reload the collectionView when updating the NSFetchResultsController.
Now run through all the fetchedObjects in the NSFetchResultsController and see what filterable properties it has. Add those properties to a set (one for each filter category). Then look at the set to determine what filters to display and update the UI.
When the filter changes set the delegate of the current NSFetchResultsController to nil, before creating a new one and the create a new one as described in step one.
In the code you shared you are needlessly doing an complicated fetch to figure out which filters are relevant. I don't know if your code is correct or not, but I do know that it is complicated. And it is faster to just look at the properties in the managedObject that you already have access to in the fetchResultsController. Those items are already fetched and in memory - so it doesn't need to hit the database again. And filtering those item in code is easier than figuring out how to write a complex predicate.

Querying Core data with predicates

So i am building this app using CoreData.
The two entities I have are Lists and Items. They have a to many relationship i.e. A List can have multiple items.
For example: List1 has Items: item1, item2
I have written the code for storing the Items in the specific list but i am having a difficult time on figuring out how to fetch and proccess the Items from a specific List.
What I have done so far is as follows
func getItemsOnList(){
let app = UIApplication.shared.delegate as! AppDelegate
let context = app.persistentContainer.viewContext
//fetchRequest to get the List
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "List")
let predicate = NSPredicate(format: "title == %#", listName)
fetchRequest.returnsObjectsAsFaults = false
fetchRequest.predicate = predicate
if let fetchResults = try? context.fetch(fetchRequest){
if fetchResults.count > 0 {
for listEntity in fetchResults {
let list = listEntity as! List
print(list.title as Any)
itemsOnList = list.contains!
print(itemsOnList)
print("The list with name:\(list.title)has \(itemsOnList.count) items")
}
}
}
}
This function returns an NSSet which is suppose to contain all the Items in that particular List.
My Data model is :
My questions are:
A. Is the way I coded the getItemsOnList() function correct? Or is there something I am doing wrong.
B. Given that the code is correct and the NSSet I get is correct with all the Items, how would I get each Item in that NSSet in order for me to put it on a TableView.
func getItemsWithFilter(filterQuery:NSPredicate,sortBy:String?,order:Bool) -> Array<Items> {
var fetchedResults:Array<Items> = Array<Items>()
let fetchRequest = NSFetchRequest(entityName: "Items")
fetchRequest.predicate = filterQuery
if sortBy != nil{
let sortDescriptor = NSSortDescriptor(key:sortBy! ,
ascending:order )
let sortDescriptors = [sortDescriptor]
fetchRequest.sortDescriptors = sortDescriptors
}
//Execute Fetch request you can go with your approach to
do {
fetchedResults = try self.mainContextInstance.executeFetchRequest(fetchRequest) as! [Items]
} catch let fetchError as NSError {
print("retrieveById error: \(fetchError.localizedDescription)")
fetchedResults = Array<Items>()
}catch {
fetchedResults = Array<Items>()
}
return fetchedResults
}
for calling this method you can pass the List item in predicate to as query saying fetch Items in which List.id == XXX
let predicate = NSPredicate(format: "ANY list.name in %#", name)
let myResult = self.getItemsWithFilter(predicate,sortBy:nil,order:false)
Answers:
A) Yes. You are using the graph of objects from a fetch. That is the main functionality of Core Data.
B) To fill a table view you cannot use a set. You need some kind of sorted list of elements. That is, an array. Use -orderedArrayUsingDescriptors: to get the sorted array.

How to make simple join in relationship of Core Data Swift

I have Core Data with relationship:
extension Rating {
#NSManaged var date: NSString?
#NSManaged var rating: NSNumber?
#NSManaged var ratenames: RateNames?
}
extension RateNames {
#NSManaged var name: String?
#NSManaged var rating: NSSet?
}
And I need to select all values from RateNames with all data from Rating linked to RateNames from a selected date.
For example RateNames data:
name=a; name=b;
For example RateNames data:
date=10-02-2016; rating=5; linked to a
date=10-02-2016; rating=3; linked to b
date=09-02-2016; rating=2; linked to a
date=08-02-2016; rating=3; linked to a
date=08-02-2016; rating=1; linked to b
For example I need result for 08-02-2016
Result should be:
{(name:a, date:08-02-2016, rating=3), (name:b, date:08-02-2016, rating=1)}
result for 09-02-2016 date should be the following:
{(name:a, date:09-02-2016, rating=2), (name:b, date:nil, rating=nil)}
I also do need b in results because I need to see whether it is null or not.
I am trying to do something like:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "RateNames")
do {
let results = try managedContext.executeFetchRequest(fetchRequest)
for result in results {
let child = result. ..some var here.. as! Rating
But cannot get how to implement this
It's easier to get what you want by fetching the Rating objects, and using the relationship to get the name of the related RateName objects:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Rating")
do {
let results = try managedContext.executeFetchRequest(fetchRequest) as! [Rating]
for result in results {
let name = result.ratenames.name
let date = result.date
let rating = result.rating
print("name:\(name), date:\(date), rating:\(rating)")
}
}
This will print results similar to your expected results, though it won't include the (name:b, date:nil, rating=nil).
Update
If you want to limit the above to a given date, you can use a predicate for the fetch request:
fetchRequest.predicate = NSPredicate(format:"date == %#", chosenDate)
But since you want to include, (name:b, date:nil, rating=nil), you will need to fetch ALL the RatingName objects. The rating property of each object will include any and all Ratings to which it is related. So I think you will then need to filter them to identify any for the correct date:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "RateNames")
do {
let results = try managedContext.executeFetchRequest(fetchRequest)
let datePredicate = NSPredicate(format:"date == %#", chosenDate)
for result in results {
let filteredRatings = result.rating.filteredSetUsingPredicate(datePredicate)
if filteredRatings.count > 0 {
let rating = filteredRatings.anyObject()
print("name:\(result.name), date:\(rating.date), rating:\(rating.rating)")
} else {
print("name:\(result.name), date:nil, rating:nil")
}
}
}
This assumes there can be only one Rating for each RatingName for a given date; if not you will have to iterate through the filteredRatings set.
you need to just setup relationship in Datamodel like i have created for following example
From your code i found that you didn't mentioned One-to-Many Relationship .
Then create again sub class of NSManageObject. xCode will create all needed variables for same.

Get fields from NSArray with CoreData content

I have a table in core data with some fields, I can get the count of records, but I canĀ“t get the values from each field, now I have this code:
var request:NSFetchRequest = NSFetchRequest(entityName: "Radar") //my table in core data
let appDelegate:AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
let context:NSManagedObjectContext = appDelegate.managedObjectContext!
request.returnsObjectsAsFaults = false
var results:NSArray = context.executeFetchRequest(request, error: nil)!
println(results.count) //this is the count that i can do
I need some more,
Radar have this fields: Descr,Latit,Long
I need get this fields to create an annotation
some like this: results.Descr
You will need to loop your array of results and cast each result as the NSManagedObject you retrieved, in you case Radar
if results.count > 0
{
for result in results
{
if let r = result as? Radar{
//now you can access the properties of Radar in r
println(r.descr)
} else{
println("Could not cast fetch result to Radar")
}
}
}

Resources