I want to make my code more reusable for core data, I have a fetchRequest with predicates that I always must use. However some methods require more conditions. I want to add those conditions to the predicate list however I am unsure how to do this. I would like a method to return a predicate with basic queries and then add on to those queries.
let fetchRequest = NSFetchRequest(entityName: "Stop")
var currentTime = NSDate.getTime()
var sort = NSSortDescriptor(key: "time", ascending: true) // sort by bus stop
fetchRequest.sortDescriptors = [sort]
let predicate = NSPredicate(format: "time >= %ld", currentTime)
let predicate2 = NSPredicate(format: "stop_name == %#", stop)
let predicate3 = NSPredicate(format: "busParent.direction == %#", direction)
let predicate4 = NSPredicate(format: "busParent.name == %#", name)
let predicate5 = NSPredicate(format: "busParent.schedule == %ld", schedule)
fetchRequest.predicate = NSCompoundPredicate.andPredicateWithSubpredicates(
[predicate, predicate2, predicate3, predicate4, predicate5])
// EXAMPLE: HOW WOULD I ADD TO THE COMPOUND PREDICATE ALREADY MADE?
fetchRequest.predicate.????
You can compound NSCompoundPredicates with other NSPredicates:
let currentPredicate = fetchRequest.predicate!
let additionalPredicate = NSPredicate(format: "whatever",...)
let nextPredicate = NSCompoundPredicate(type: NSCompoundPredicateType.AndPredicateType, subpredicates: [currentPredicate, additionalPredicate])
fetchRequest.predicate = nextPredicate
By specifying .AndPredicateType you will "narrow down" the existing results to those that also meet the additional predicate. But you could also specify OrPredicateType or NotPredicateType to add to or exclude (respectively) the existing results.
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've been looking on how to search using a NSFetchResultController but most post I came across are from 2 years ago. Okay I'm trying to filter the objects, which in my case are Pokemon. Here is my function I'm using.
func attemptPokemonFetch(generation: String = "1", name: String = "") {
let context = coreData.persistentContainer.viewContext
let request: NSFetchRequest<Pokemon> = Pokemon.fetchRequest()
let sortByName = NSSortDescriptor(key: "id", ascending: true)
request.sortDescriptors = [sortByName]
request.predicate = NSPredicate(format: "generation = %#", generation)
if !name.isEmpty {
request.predicate = NSPredicate(format: "name CONTAINS[cd] %#", name)
}
let controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
controller.delegate = self
self.controller = controller
do {
try controller.performFetch()
}
catch let err {
print(err)
}
}
I call this function in view did load, search bar did change text and when the segment changes index value. I know how to filter the Pokemon and use the searching. However the problem I come across is when I begin searching. When searching for a Pokemon in a specific generation other Pokemon from another generation will also appear. So I'll be at index 2 which is for generation 2 Pokemon and when searching, other Pokemon from different generation will be appearing. I'm not sure if its how I initialize the context. This is how my search bar function look like.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar == pokemonSearchBar {
let text = pokemonSearchBar.text!
self.attemptPokemonFetch(name: text)
collectionView.reloadData()
}
}
Another Example: Lets say I have 3 types of people some are skinny, normal and fat. When I'm at the index for skinny people I only want to search for people that are skinny not all the people. So what would I need to do to achieve this type of behavior.
Would really appreciate any help. :)
In your code
request.predicate = NSPredicate(format: "generation = %#", generation)
if !name.isEmpty {
request.predicate = NSPredicate(format: "name CONTAINS[cd] %#", name)
}
the second assignment replaces the previously assigned predicate.
What you probably want is
if name.isEmpty {
request.predicate = NSPredicate(format: "generation = %#", generation)
} else {
request.predicate = NSPredicate(format: "generation = %# AND name CONTAINS[cd] %#", generation, name)
}
A more flexible approach is to use NSCompoundPredicate:
var predicates = [NSPredicate]()
predicates.append(NSPredicate(format: "generation = %#", generation))
if !name.isEmpty {
predicates.append(NSPredicate(format: "name CONTAINS[cd] %#", name))
}
request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicates)
I want to search a list by typing multi words from the detailed list.
e.g,
in a claim request list there are different type of lables such as amount, requestor name, request name and nos also. So i want to search anything from this label so that i can able to find the exact request.
var predicateList = [NSPredicate]()
let words = filterText.componentsSeparatedByString(" ")
for word in words{
if count(word)==0{
continue
}
let RequestTypeArray = NSPredicate(format: "RequestType contains[c] %#", word)
let RequestEmployeeArray = NSPredicate(format: "RequestorEmployee contains[c] %#", word)
let RegesterNumberArray = NSPredicate(format: "ReqNo contains[c] %#", word)
let AmountOrDaysArray = NSPredicate(format: "AmountOrDays contains[c] %#", word)
let orCompoundPredicate = NSCompoundPredicate(type: NSCompoundPredicateType.OrPredicateType, subpredicates: [firstNamePredicate, lastNamePredicate,departmentPredicate,jobTitlePredicate])
predicateList.append(orCompoundPredicate)
}
request.predicate = NSCompoundPredicate(type: NSCompoundPredicateType.AndPredicateType, subpredicates: predicateList)
May be this answer was helpful to you..
let addresspredicate = NSPredicate(format: "address_name contains[c] %#",searchText)
let accnopredicate = NSPredicate(format: "acc_no contains[c] %#",searchText)
let propertytype = NSPredicate(format: "property_type contains[c] %#",searchText)
let subpropertytypoe = NSPredicate(format: "subproperty_type contains[c] %#",searchText)
let predicateCompound = NSCompoundPredicate.init(type: .or, subpredicates: [addresspredicate,accnopredicate,propertytype,subpropertytypoe])
filteredProperty = (propertyArray as Array).filter { predicateCompound.evaluate(with: $0) };
print("filteredProperty = ,\(filteredProperty)")
My predicate wants to exclude some records that are already downloaded and available in a [CKRecordID]. Now I can query 1 CKRecordID[0], but not the [CKRecordID] array. How can I query the array?
let excludeIDs: [CKRecordID]
This works:
let pred1 = NSPredicate(format: "NOT(recordID = %#)", excludeIDs[0])
But this doesn't:
let pred1 = NSPredicate(format: "NOT(recordID IN %#)", excludeIDs)
ERROR:
loadImageCompareRecordIDsAndEndDateThatHaveNotEnded Error: Invalid predicate: Invalid predicate: Array members must conform to CKRecordValue: (
"",
"",
"",
"",
""
) (CKRecordID)
The other general parts of the code:
let sort = NSSortDescriptor(key: "creationDate", ascending: false)
let query = CKQuery(recordType: MyRecordTypes.ImageCompare, predicate: pred1)
query.sortDescriptors = [sort]
let operation = CKQueryOperation(query: query)
operation.desiredKeys = ["endDate"]
operation.resultsLimit = 50
Using [CKReference] and not [CKRecordID] solved it.
To be explicit (because it took me hours to get this right)...
let refs = excludeIDs.map { CKRecord.Reference(recordID: $0.recordID, action: .none) }
let pred1 = NSPredicate(format: "NOT(recordID IN %#)", refs)
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