NSFetchResultsController time filtering predicate advice - ios

I need some advice with predicates. In my app I want to have ability to filter results by date. Before I was doing it with having 2 separate predicates and checking if a filter was set.
if (self.isDateFilterSet==[NSNumber numberWithBool:NO])
{
fetchRequest.predicate = [NSPredicate predicateWithFormat:#" whoRecorded.username = %# AND deleted=0", [self.settings.currentUser username]];
}
else
{
fetchRequest.predicate = [NSPredicate predicateWithFormat:#" (whoRecorded.username = %#) AND (deleted = 0) AND (date =>%#) AND (date<=%#)", [self.settings.currentUser username],self.dateMinSetFilter ,self.dateMaxSetFilter];
}
I was cleaning up my code and decided to always keep the dates in the predicate and check for dates on every update. However when I insert new data the delegate methods are not called because date<=%# is not satisfied.
Can you suggest me the correct way of dealing with situations like this?

When the user hasn't specified dates to filter with, don't use the min and max from the database. You know you want everything so set the min to [NSDate distantPast] and the max to [NSDate distantFuture] and then you guarantee to cover everything.
Add a method:
- (void)resetDateFilters {
self.dateMinSetFilter = [NSDate distantPast];
self.dateMaxSetFilter = [NSDate distantFuture];
// destroy the FRC here, delete cache if necessary
}
Call it in view did load (this is initial setup). Call it whenever the user wants to reset filters.

Related

Use CONTAINS or ANY in CloudKit predicate with an array of comparison

I tried to use NSCompoundPredicate(orPredicateWithSubpredicates:) with CloudKit NSPredicate but then I read on Apple's documentation that OR comparison predicates aren't supported in CloudKit so I hit a road-block with this.
I have an array of CKReferences that I need to see if a Record-type of Reference list contains those references.
I'm struggling on how to assemble the predicate itself , because I'm trying to avoid nested-queries in the completionBlock.
let's say I have the following array of references:
let refs = [CKReference]() // in my case the array isn't empty
I tried the following but it didn't worked, because somehow it only looks for the first object of the array, and not the whole array.
let predicate = NSPredicate(format: "tableName CONTAINS %#", argumentArray: refs)
Here if I print the predicate it prints:
tableName CONTAINS CKReference: 0x600000229c40; 20B54862-46CC-405F-BAE8-0DC8D3A52F78:(_defaultZone:defaultOwner)>
As you can see, it only looks for the first object in the array.
It may work if I use the ANY operator in the predicate, something like:
let predicate = NSPredicate(format: "ANY { '%#', '%#' } = tableName", args: refs[0], refs[1])
But my problem here is how can I build that predicate, since the refs array is dynamic and I don't know how many objects it may contain , and by that I don't know how to build the predicate args accessing the [0], [1], ... of the refs array.
Do you have any workaround with this? Or a best way to approach this issue?
Thank you.
EDIT
I figure out a way to solve this issue, but I don't know if that's the most efficient one, so I'm still opened to answers and opinions.
Here's my temporary solution:
for (i, reference) in refs.enumerated() {
let predicate = NSPredicate(format: "tableName CONTAINS %#", reference)
// CKQuery
// CKQueryOperation
// Database.add(CKQueryOperation)
if i == refs.count - 1 {
// UPDATE UI
}
}
NSMutableArray * tagsReferencesArray = [[NSMutableArray alloc] init];
[tagsReferencesArray addObject:tag100_Reference];
[tagsReferencesArray addObject:tag300_Reference];
[tagsReferencesArray addObject:tag200_Reference];
predicate= [NSPredicate predicateWithFormat:#"ANY %# in field_TagsReferenceList ", tagsReferencesArray];

Extract objects with specific date from custom object Array

I have an ordered array of Availability object which is a class containing two attributes, startAt and endAt.
I'd like to extract from my availability array the objects from a specific day without taking into account the time.
I'd also like to use NSPredicate instead of "for" loop to do it.
Could you help me?
Thanks
for-in loop actually faster than NSPredicate for me, and easier since u can convert it to string or extract it for easy compare, but if u want then the format should be:
#"(%# >= %#) AND (%# <= %#)", dateForCompare, dayStart, dateForCompare, dayEnd where dayStart is the date to compare at 00:00:00, dayEnd is the date to compare at 23:59:59, tweak it base on your needs, u have to do this because a NSDate object contain exact date and time
Hope it help
if you are using timestamp(NSNumber) to store startAt and endAt then you can use predicate as bellow (to fetch all object whose start date is greter then current date)
NSPredicate *pred = [NSPredicate predicateWithFormat:#"startAt > %#", [NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]]];
NSArray *filteredArr = [yourArray filteredArrayUsingPredicate:pred];
and if you are using only date string then you can use
NSPredicate *pred = [NSPredicate predicateWithFormat:#"startAt = %#", #"your date string must be same as formate you saved in array"];
and if you are using only date string then you can use

how to get "Newest Record" when fetching record With Magical Record

How do I get the latest record when fetching records?
for example:
first iteration i save "Singapore" with object name Country,
second iteration i save "USA" with object name Country,second
Now when i fetch it
SomeDatabase *Country = [SomeDatabase MR_findFirst];
NSLog(#"Country Name---->%#",Country.Name);
I get "Singapore",
I want get the last record inserted to coredata, which is supposed to be "USA".
The most straight forward way is to add a "createdDate" or similar date field to your entity. Then you can simply use the query:
[MyEntity MR_findFirstOrderedByAttribute:#"createdDate" ascending:NO];
SWIFT 3
If you want to add extra query arguments:
let predicate = NSPredicate(format: "(attribute1 = %# && attribute2 = %# && date <= %#)", argument1, argument2, yourdate as NSDate)
let fetch = YourCCEntity.mr_requestAllSorted(by: "date", ascending: false, with: predicate)
let latestObject = YourCCEntity.mr_executeFetchRequestAndReturnFirstObject(fetch)
The as NSDate is necessary because of an Xcode bug (Some Swift classes don't work with NSPredicate arguments)

NSPredicate from Core Data selecting by Object ID (and potentially summing)

I'm finding my way to the advanced part of core data. I know it is not SQL, and there are limitations, however, how would I translate the code below to a NSPredicate to be applied in a fetch request?
Product* p = self.product; //this is a managed object
double vol=0;
for (PlanningItem* pi in self.planitems) {
if (pi.product==p)
vol+=pi.volume;
I want to fetch all plan items that have p as their product.
Next I want to sum all the volumes of that set.
How do I do 1 in a fetch request with a NSPredicate?
Can i do 2 at the same time?
I'm asking for the string for the NSPredicate. I am capable of building the Fetchrequest myself.
Tx!
// Create the predicate
NSPredicate *productPredicate =
[NSPredicate predicateWithFormat:#"(product == %#)", product];
// Apply the predicate to your FetchRequest
[fetchRequest setPredicate:productPredicate];

NSPredicate: how to treat strings as numbers?

I am building a compound NSPredicate to make a fetch request, using core data on sqlite, within iOS app. Everything already works fine, but I am not being able to include the last condition. The reason is quite simple: I need to check if the value, stored as a string, is within certain float bounds. The problem is that the conditions are checked on alphabetical order basis, and not according its float value. Here it is the code:
NSString * conditionToBeAdded = [NSString stringWithFormat:#" && (%# >= \"""%#\""") && (%# <= \"""%#\""")", propertyName, myMinFloat, propertyName, myMaxFloat];
stringForPredicate = [stringForPredicate stringByAppendingString:conditionToBeAdded];
NSPredicate * myPredicate = [NSPredicate predicateWithFormat:stringForPredicate];
Does anybody know how to build an NSPredicate object able to check string values as numbers, to be used within a coredata request?
Thank you.
Since NSPredicates allow you to use everything which matches Key-Value Coding approach you can call on NSStrings methods like: intValue or floatValue
NSString * conditionToBeAdded = [NSString stringWithFormat:#" && (propertyName.floatValue >= %f ) && (propertyName.floatValue <= %f)", myMinFloat, myMaxFloat];
stringForPredicate = [stringForPredicate stringByAppendingString:conditionToBeAdded];
NSPredicate * myPredicate = [NSPredicate predicateWithFormat:stringForPredicate];
I hope that helps.
You will want to change that string to a number and fix it properly. String searching is SLOW and should be avoided. Putting numbers into strings is a big performance hit with no gain.
As for your actual predicate, you can do a < or > comparison on strings. I suspect all of those quotation marks you have in the predicate are throwing things off. Quotation marks are not needed in predicates unless you are setting a constant which in this case you are not. Remove them and try it again.

Resources