I want to fetch Objects in Ascending order from core data. The problem is, I want the objects as
Abc ,abc Ball, ball bat, Cat, can
But, it sends like,
Abc,Ball,Cat ,abc, ball, cat
Following is my code.
class func CD_FetchAllContacts()->[RecentContact]{
var arrResult:[RecentContact] = []
let managedContext = PM.instance().managedObjectContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "RecentContact")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "firstName", ascending: true, selector: #selector(NSString.caseInsensitiveCompare))]
do {
let results =
try managedContext?.fetch(fetchRequest)
arrResult = results as! [RecentContact]
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
print(arrResult.count)
return arrResult
}
The sort descriptor should be like this, with (_ :) at the end:
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "firstName", ascending: true, selector: #selector(NSString.caseInsensitiveCompare(_:)))]
Edit: Actually even with your version of the sort descriptor it's working on a test project I've done. Nothing seems wrong with the code posted. Are you sorting the returned array in other part of your code?
Related
I'm new to CloudKit (and haven't used NSPredicate much) and am looking to do something which, I'd imagine, is quite basic. I'd like to retrieve the data record with the newest creationDate.
Right now I'm just pulling all the records and scooping up the last one, but there's got to be a more elegant way. Here's my current approach:
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "NewsItem", predicate: predicate)
CKContainer(identifier: "…").publicCloudDatabase.perform(query, inZoneWith: CKRecordZone.default().zoneID) { result, error in
let sortedRecords = result.sorted {
guard let date1 = $0.value(forKey: "creationDate") as? Date,
let date2 = $1.value(forKey: "creationDate") as? Date else {
return false
}
return date1 < date2
}
let item = NewsItem(from: sortedRecords[0])
…
}
I've used NSPredicate to get a subset of results before, but never max/min style results. Is this possible?
This seems like a step in the right direction:
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "NewsItem", predicate: predicate)
query.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
CKContainer(identifier: "…").publicCloudDatabase.perform(query, inZoneWith: CKRecordZone.default().zoneID) { result, error in
let item = NewsItem(from: result.last)
…
}
Though I'm still curious if there's a way to simply query by max creationDate…
I am working on a CloudKit based project where it would be very helpful to use sort descriptors to get the most recent results from the database.
func getConversationPosts(for targetConversation: MessageConversation, completionHandler: #escaping ([MessagePost]) -> Void) {
var post = MessagePost()
let getRecordsOperation = CKQueryOperation()
getRecordsOperation.qualityOfService = .userInteractive
getRecordsOperation.resultsLimit = 1
getRecordsOperation.query = CKQuery(recordType: "MessagePost", predicate: NSPredicate(format: "conversationName = %#", targetConversation.conversationID.recordName))
getRecordsOperation.query?.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] // If you comment this out results are returned
/*getPostsOperation.queryCompletionBlock = {(searchCursor, error) in
print("sQ: \(searchCursor)")
}*/
getRecordsOperation.recordFetchedBlock = {(postRecord) in
let newPost = MessagePost()
newPost.textContent = postRecord.value(forKey: "textContent") as! String
newPost.poster = User(userName: postRecord.value(forKey: "postUser") as! String)
// posts.append(newPost)
post = newPost
}
getRecordsOperation.completionBlock = {() in
completionHandler(post)
}
OperationQueue().addOperation(getRecordsOperation)
}
The above code always works if the line that adds the sortDescriptors is removed. Even without a result limit, if any sort descriptor is added, the recordFetchedBlock isn't even called, but the completion block is. What could be causing this?
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.
I have Core Data database. Structure is on the image (DBContact have many DBMessages with property messages, each DBMessage have one contact with property contact):
I want to display messages grouped by contact and sorted by date. So the number of rows have to be = number of contacts with messages. And in each row I have to display avatar, nameAndSurname of contact and text and date of last message of this contact.
The only solution I found is:
let entityName = kDBOrder
let entityDescription = NSEntityDescription.entityForName(entityName, inManagedObjectContext: managedObjectContext)
let fetchRequest = NSFetchRequest()
fetchRequest.entity = entityDescription
fetchRequest.fetchBatchSize = 20
// sort
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "contact.id", ascending: false), NSSortDescriptor(key: "date", ascending: false)]
let sectionNameKeyPath: String? = "contact.id"
self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: sectionNameKeyPath, cacheName: nil)
fetchedResultsController!.delegate = self
if tableViewMain != nil {
do {
try fetchedResultsController!.performFetch()
} catch {
print("An error occurred in fetchresultcontroller")
}
tableViewMain.reloadData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count = 0
if let sections = fetchedResultsController?.sections {
let currentSection = sections[section]
count = currentSection.numberOfObjects
// in each section we must have only 1 row
if count > 0 {
count = 1
}
}
return count
}
But in this case contacts will be sorted by id, but I need to sort them by date of last message. But if I change sortDescriptors to fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false), NSSortDescriptor(key: "contact.id", ascending: false)] to sort by date first, it will not display contacts properly.
Do anybody have ideas how to implement this?
I'd probably change the model to make life easier and do a little more processing earlier. Basically if add another relationship, 1-to-1, between the 2 entities and each time a new message is added is update that relationship - so you always have a direct pointer to the most recent message.
Once you've done that your FRC and your table load speed are trivial because you just use contacts in the fetch. New messages are also handled correctly because the relationship gets updated so the contact changes and the FRC will see it.
I have found many examples of this, but none seem to work for my situation.
My data model is this.
I have a one to many relationship of Project <->> Entry.
Entry has an attribute of category which is a String.
I want to populate a TableView inside a Project that shows all the entries for that Project organized by the Entry.category as the titleForHeaderInSection.
func allCategoriesFetchRequest() -> NSFetchRequest {
var fetchRequest = NSFetchRequest(entityName: "Project")
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.predicate = NSPredicate(format:"(ANY entries == %#)", self.currentProject)
return fetchRequest
}
And loading the data....
func loadInitialData() {
fetchedResultsController = NSFetchedResultsController(fetchRequest: allCategoriesFetchRequest(), managedObjectContext: managedObjectContext!, sectionNameKeyPath: "entries.category", cacheName: nil)
fetchedResultsController?.delegate = self
fetchedResultsController?.performFetch(nil)
}
I get no error, but I get no data either. I am a beginner with iOS and have been struggling with this for a couple of days now.
Any help is greatly appreciated.
Well, there is a lot here.
Your current fetch request says "find any project where its entries are equal to the current project". When what you want to say is "find any entry where its project is equal to the current project".
Lets try to do it using an NSFetchRequest.
var fetchRequest = NSFetchRequest(entityName: "Entry")
let sortDescriptor = NSSortDescriptor.sortDescriptorWithKey("name", ascending: true)
let predicate = NSPredicate(format: "project == %#", self.currentProject)
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.predicate = predicate
The reason your code doesn't error is because its valid code. It will look for all the projects where an entry is equal to the current project, which will be never, because you won't have any projects as entry objects in your entries field of a project. So that is why you are getting no data.
If you don't want to use a NSFetchedResultsController, and if your model is set such that you have a set of Entry objects in the entries field of your Project object, then you can just do:
let entries = self.currentProject.entries
to get all your Entry objects. This will be a NSSet though, so you have to change it into an array to have it sorted
let sortDescriptor = NSSortDescriptor.sortDescriptorWithKey("name", ascending: true)
let sortedEntries = entries.sortedArrayUsingDescriptors([sortDesriptor])
Wrote all this code without testing, so let me know if there is a problem and I will update.
Excellent. Thank you. Just what I was looking for. This is what I tweaked based on ColdLogic's answer. In case it helps someone else.
var fetchRequest = NSFetchRequest(entityName: "Entry")
let sortDescriptor = NSSortDescriptor(key: "category", ascending: true)
let predicate = NSPredicate(format: "project == %#", self.currentProject)
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.predicate = predicate