Swift how to have a TableView of hashs - ios

New to swift and mobile at all.
Created the default Master project in Xcode and trying to have a table (tableView) of categories where each category has an int next to it
House 1
Child 4
Food 5
That's it.
The default cells are just time stamp like so
let sortDescriptor = NSSortDescriptor(key: "timeStamp", ascending: false)
let sortDescriptors = [sortDescriptor]
I presume I will want a hash But i can't even set something other than a timestamp in that field
tried
newManagedObject.setValue(1, forKey: "timeStamp")
instead of
newManagedObject.setValue(NSDate.date(), forKey: "timeStamp")
get an error
Unacceptable type of value for attribute: property = "timeStamp"; desired type = NSDate; given type = __NSCFNumber; value = 1.'
just FYI, my master controller is like so
MasterViewController: UITableViewController, NSFetchedResultsControllerDelegate

Related

How to group Core Data objects as the data source for a UITableView

In Core Data, I have a large number of "City" objects, each of which has a "country" property.
Rather than have a very long scrollable list of cities, I need to have a separate screen which lists just Countries in a UITableView. The user then selects a country and is taken to a new screen, which has the Cities for that Country.
Note: This is for a game, so the UITableView data source is bound to a SKScene, but I don't think that should make any difference as it works fine for other screens.
I have figured out the detail screen which displays the Cities for a specific country and it works fine in test with hard-coded data.
I am using Swift 5 so my data calls are like this:
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "City")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
fetchRequest.predicate = NSPredicate(format: "country = %#", country)
try fetchedResultsController.performFetch()
However for the other screen, I don't know how to "group" the Countries, which are the property on each City, and make the groupings the data source for the UITableView on the other screen.
a) I could create an array of Countries, but how would I have the fetchedResultsController return the Country data to UITableView?
b) Or, is it possible to group or filter objects within a fetchedResultsController?
c) Or, is it possible to do SQL-like grouping within a fetch request?
Thanks
Thanks #Larme for the link to https://developer.apple.com/documentation/coredata/nsfetchrequest/1506191-propertiestogroupby
So to get this working, Results Container needs to be defined with NSDictionary.
var fetchedResultsController: NSFetchedResultsController<NSDictionary>!
The fetchRequest needs to be set to group and fetch the specific property.
fetchRequest.propertiesToFetch = ["country"]
fetchRequest.propertiesToGroupBy = ["country"]
fetchRequest.resultType = .dictionaryResultType
Then in the methods where we need to access the data:
// cell data
let countryName = (fetchedResultsController.object(at: indexPath) as NSDictionary)["country"]!
cell.textLabel?.text = "\(countryName)"
Thanks for pointing me in the right direction!

CoreData sort using two fields

I am trying to sort CoreData entity by two fields schedule_order and activity_order. I want to sort by schedule_order then keep it sorted by that and sort by activity_order while keeping the schedule_order in tact.
I have tried this:
let sortDescriptor = NSSortDescriptor(key: "schedule_order", ascending: true)
let sortDescriptor1 = NSSortDescriptor(key: "activity_order", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor,sortDescriptor1]
Do I need to do this a different way? I have one case where it isn't sorting it correctly. The one that should be appearing first keeps up ending up at the end.

Sorting data by multiple key values

I have a basic list app with two entities Folder and Item with a to many relationship, the folder subclass has a function called itemsArray() which returns the Items in a sorted array by creationDate which are displayed UITableView.
Item has two properties called date of type NSDate and another of type bool called completed. I have created a function where I can mark the cell item object as completed which changes the cell text color to grey.
Is there a way I can also sort it by its completed value ? For e.g the completed value of true or false should separate all the completed items and then also sort it by the creationDate.
The end result should be where the completed items are at the bottom of the UITableView and in order of date as well as the non completed items are in order of date. As if completion is higher priority than the date but still orders by creationDate
Heres my current function with sort descriptor to return an array;
func itemsArray() -> [Item] {
let sortDescriptor = NSSortDescriptor(key: "creationDate", ascending: true)
return checklist.sortedArrayUsingDescriptors([sortDescriptor]) as! [Item]
}
PS: I tried some work arounds such as when marking as completed updating the date so it is at the bottom. This separates the completed items with the non completed. But when unmarking the item it remains at the bottom also this isn't a good long term solution as I may want to change the sorting to alphabetical order and completion.
You can add another NSSortDescriptor to the function:
func itemsArray() -> [Item] {
let sortByCompleted = NSSortDescriptor(key: "completed", ascending: true)
let sortByCreationDate = NSSortDescriptor(key: "creationDate", ascending: true)
return checklist.sortedArrayUsingDescriptors([sortByCompleted, sortByCreationDate]) as! [Item]
}
Play with the ascending value to get true first or false first. Or the Swifty way of doing things:
func itemsArray() -> [Item] {
return checklist.sort {
if $0.completed == $1.completed {
return $0.creationDate < $1.creationDate
}
return $0.completed < $1.completed
}
}

Sorting NSFetchedResultsController by Swift Computed Property on NSManagedObjectSubclass

I'm building an app using Swift with Core Data. At one point in my app, I want to have a UITableView show all the objects of a type currently in the Persistent Store. Currently, I'm retrieving them and showing them in the table view using an NSFetchedResultsController. I want the table view to be sorted by a computed property of my NSManagedObject subclass, which looks like this:
class MHClub: NSManagedObject{
#NSManaged var name: String
#NSManaged var shots: NSSet
var averageDistance: Int{
get{
if shots.count > 0{
var total = 0
for shot in shots{
total += (shot as! MHShot).distance.integerValue
}
return total / shots.count
}
else{
return 0
}
}
}
In my table view controller, I am setting up my NSFetchedResultsController's fetchRequest as follows:
let request = NSFetchRequest(entityName: "MHClub")
request.sortDescriptors = [NSSortDescriptor(key: "averageDistance", ascending: true), NSSortDescriptor(key: "name", ascending: true)]
Setting it up like this causes my app to crash with the following message in the log:
'NSInvalidArgumentException', reason: 'keypath averageDistance not found in entity <NSSQLEntity MHClub id=1>'
When I take out the first sort descriptor, my app runs just fine, but my table view isn't sorted exactly how I want it to be. How can I sort my table view based on a computed property of an NSManagedObject subclass in Swift?
As was pointed out by Martin, you cannot sort on a computed property. Just update a new stored property of the club every time a shot is taken.

How to get specific row data from the CoreData sqlite table using Swift

I am new to iOS development using Swift language.
I am using CoreData in my app as a database option.
I am using NSFetchRequest for fetching records from the table. I can retrieve all records from the table, but I can't retrieve a specific row from the same table.
I have checked for the solutions online and other forums, but I am unable to get a proper solution for this.
I want to implement this in Swift only. I don't want to add the sqlite library or Bridging-wrapper (Objective - C) which will be my last option to implement this.
Any links or tutorials or suggestions will be helpful.
NSFetchRequest also support NSPredicate. With NSPredicate you can choose which exactly rows or row you need from Core Data.
More about NSPredicate - https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSPredicate_Class/
You can fetch a desired row by executing a fetch request, casting it as an array, and simply using subscript to retrieve an index. Or you can narrow down your results using a unique id and a predicate and getting the object at index 0.
if let results = managedObjectContext?.executeFetchRequest(fetchRequest, error: nil) as? [SomeClass] {
let someObject = results[someIndex]
}
As Pavel said, use NSPredicate. Here is a working example from my code, it might help you to get started ;) I added some comments below relevant lines
var req = NSFetchRequest(entityName: "YourDBTable")
// your db-table goes here
req.sortDescriptors = [NSSortDescriptor(key: "timeMillis", ascending: true)]
// if you want to sort the results
let start = NSNumber(longLong:int64StartValue)
// you need to convert your numbers to NSNumber
let end = NSNumber(longLong:int64EndValue)
let pred = NSPredicate(format:"timeMillis>=%# AND timeMillis <=%# AND foreignTableObject=%#",start,end, foreignTableObject)
// if you have relationships, you can even add another NSManagedObject
// from another table as filter (I am doing this with foreignTableObject here)
req.predicate = pred
var fetchedResults = managedContext?.executeFetchRequest(req,error: &error) as! [YourDBTable]?
// YourDBTable-class was created using the Xcode data model -> Editor -> Create NSManagedObject SubClass... - option
if let results = fetchedResults {
// iterate through your results
}

Resources