Fetch request not working in Swift 4.1 [duplicate] - swift

This question already has answers here:
Xcode 9.3 - NSPredicate Bool crash
(3 answers)
Closed 4 years ago.
I have a predicate that is a Bool set to false (mostly). It stopped working correctly in Swift 4.1 when I upgraded yesterday.
The predicate is:
requestData.predicate = NSPredicate(format: "delete == %#", false as CVarArg);
How do I check if this value is false in the Core Data store? Thanks.

The placeholder %# is for objects, the placeholder for a Bool is %d
However there is a simpler syntax if the argument is a constant
requestData.predicate = NSPredicate(format: "delete == FALSE")

You should get your predicate working by using:
requestData.predicate = NSPredicate(format: "delete == NO")
Unfortunately I can't tell you why your example with CVarArg did stop working - but since it is not needed in your example I would try using the other initializer with the optional argumentArray parameter: init(format:argumentArray:)
The documentation in section Boolean Values says:
You specify and test for equality of Boolean values as illustrated in the following examples:
NSPredicate *newPredicate =
[NSPredicate predicateWithFormat:#"anAttribute == %#", [NSNumber numberWithBool:aBool]];
NSPredicate *testForTrue =
[NSPredicate predicateWithFormat:#"anAttribute == YES"];

Related

NSPredicate in swift for empty string

Swift 4.2 iOS 11.x
Trying to read the records in a iCloud database that have a lineOwner field set, but struggling to create an NSPredicate that works!
This looks ok, but doesn't parse.
let predicate = NSPredicate(format: remoteAttributes.lineOwner + " != %#",0)
I get an invalid predicate error message [and a nasty crash into the bargain]. Spend a couple of hours on this and losing the will to live.
If I print out the database I see this.
You can simply compare to the empty String, "". The %# placeholder represents a String, so the crash happens because you supply an Int to the NSPredicate instead of a String.
You should also use the %K placeholder for variable names instead of appending strings.
let predicate = NSPredicate(format: " %K != %#", remoteAttributes.lineOwner, "")
If you also want to filter out nil values, you can use a compound predicate:
let predicate = NSPredicate(format: " %K != %# AND %K != nil", remoteAttributes.lineOwner, "")

NSPredicate: Unable to parse the format string for relationship

I have two entity named "CIUser" and "CICast". "CIUser" entity has an one-to-one relationship with "CICast" named "cast".
CIUser :
-> userId(Int)
-> isLive(bool)
-> name(String)
CICast:
-> castId(Int)
-> lastUpdate(Date)
Now my requirement is to fetch all the users who are currently live and there lastUpdate is less than a calculated date. So I had prepared my fetch request like
let time = //an calculated NSDate object
let fetchRequest = NSFetchRequest(entityName: "CIUser")
fetchRequest.predicate = NSPredicate(format: "isLive == %# AND cast.lastUpdate <= %#", NSNumber(bool: true), time)
But it crash the application throwing *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to parse the format string "isLive == %# AND cast.lastUpdate <= %#"'
Can anyone help me where I am doing wrong, or what approach should I take. Suggestion will be appreciated.
The problem is that "CAST" is a reserved word in the predicate format
syntax, and the reserved words are case-insensitive. So this conflicts
with your relationship named "cast".
As a workaround, use the %K key path substitution:
fetchRequest.predicate = NSPredicate(format: "isLive == %# AND %K <= %#",
NSNumber(bool: true), "cast.lastUpdate", time)
or rename the relationship. You may want to use the %K expansion
generally to avoid such problems:
fetchRequest.predicate = NSPredicate(format: "%K == %# AND %K <= %#",
"isLive", NSNumber(bool: true),
"cast.lastUpdate", time)
For more information, see Predicate Format String Syntax
in the "Predicate Programming Guide".

Searchbar with core data issues

I have a search bar.And data displayed in labels with scrollview.
For ex:
core data Fields :
1.id
2.company
3.Employe Name
4.Address
If i type id,company or Employee Name in searchbar i want to dispaly associated results.
my code :
For search data :
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
var request = NSFetchRequest(entityName: "Agency")
request.returnsObjectsAsFaults = false
var countResult : NSArray = context.executeFetchRequest(request, error: nil)!
let result = NSPredicate(format: "SELF CONTAINS[c] %#",searchText)
self.filtered = self.countResult.filteredArrayUsingPredicate(result!)
if (filtered.count == 0 ) {
searchActive = false;
}else {
searchActive = true;
}
println(filtered)
}
It shows an error " 'Can't use in/contains operator with collection".
These codes cannot satisfy what i want.And also i dont have a idea how to fetch the related rows according to enter value in search bar.
Thanks in Advance.
The first problem is your predicate - you're trying to use CONTAINS on an NSManagedObject subclass, but CONTAINS only works with String. To check whether your search text is contained within any of your managed objects you need to evaluate whether it is contained in each attribute (in your case id, company and empolyeeName, I'm assuming they're all Strings).
To do this you should change your predicate to:
let searchPredicate = NSPredicate(format: "id BEGINSWITH %# OR
company BEGINSWITH %# OR
employeeName BEGINSWITH %#",
searchText, searchText, searchText)
I would recommend using BEGINSWITH instead of CONTAINS[c] since when searching your user is likely to be entering the first part of the phrase. Also, as Apple said in their 2013 WWDC talk Core Data Performance Optimization and Debugging -
...we've got begins with and ends with and that's by far the cheapest query that you can execute.
...
Contains is more expensive because we have to work along and see
whether it contains...
And in the case of a search, you want it to be fast!
Secondly, you don't need to filter your results after getting them back from CoreData. You can set the predicate property on your NSFetchRequest and your returned results will be filtered. For example:
let request = NSFetchRequest(entityName: "Agency")
request.predicate = // Your predicate...
let results = context.executeFetchRequest(request, error)
// Now do what you need with the results.
A final note, it's best not to force unwrap your results from executeRequest in case there is some problem and nil is returned - in that case your app would crash. You could instead use:
if let unwrappedResults = results {
// Now do what you want with the unwrapped results.
}
I suspect it has something to do with your use of SELF in the predicate format, and the "collection" referred to in the error message is the controller sub/class within which your code resides.
Try something like this (forgive me I'm Obj-C not Swift so may have the syntax incorrect).
let searchAttribute = <<entity.attribute key path>>
let result = NSPredicate(format:"%K CONTAINS[cd] %#",searchAttribute, searchText)
Where %K refers to the key path, that in the case of Core Data is your entity attribute. For example: Agency.name if that attribute exists for your Agency object.
Read about Predicate Format String Syntax.
UPDATE after third comment...
In my apps my solution includes the creation of a custom method in an extension of the Core Data generated NSManagedObject subclass. If that sounds like you know what I mean, let me know and I will post details.
In the meantime, create a custom method in whatever class your UISearchBar is controlled... (apologies Obj-C not Swift)
- (NSString *)searchKey {
NSString *tempSearchKey = nil;
NSString *searchAtrribute1 = Agency.attribute1;
NSString *searchAtrribute2 = Agency.attribute2;
NSString *searchAtrribute3 = Agency.attribute3;
NSString *searchAtrribute4 = Agency.attribute4;
tempSearchKey = [NSString stringWithFormat:#"%# %# %# %#", searchAtrribute1, searchAtrribute2, searchAtrribute3, searchAtrribute4];
return tempSearchKey;
}
You'll obviously need a strong reference for your Agency entity object to persist within the class, otherwise you will need to embed this bit of code into your searchBar function.
Work OK?

NSPredicate with Swift and Core Data

i have a Core Data Object and i have 2 Fieds (one String(GUID) and one Int which i want to use as Filter)
So in SQL it would be "SELECT * FROM Answers WHERE qIndex = 1 AND GUID = '88bfd206-82fb-4dd0-b65d-096f8902855c'
Ive tried it with Core Data but i am not able to Filter with the String Value.
Here is my Code
var request = NSFetchRequest(entityName: "Answers")
request.returnsObjectsAsFaults = false;
let resultPredicate1 = NSPredicate(format: "qIndex = %i", qIndex)
let resultPredicate2 = NSPredicate(format: "formUUID = %s", formUUID)
var compound = NSCompoundPredicate.andPredicateWithSubpredicates([resultPredicate1, resultPredicate2])
request.predicate = compound
var results:NSArray = context.executeFetchRequest(request, error: nil)
Any ideas what i am doing Wrong? With the Same Code and Filter for 2 Integer Values it works fine.
Thanks in Advance
If formUUID is an NSString or a Swift String then you have to use the
%# placeholder:
let resultPredicate2 = NSPredicate(format: "formUUID = %#", formUUID)
This is not the exact response to your question, but a problem people might now encouter with your code now:
In the latest version of XCode, you must now unwrap the predicate, like this:
var compound = NSCompoundPredicate.andPredicateWithSubpredicates([predicate1!, predicate2!])
because NSPredicate initializer now return NSPredicate? type.
Instead of worrying about %# conversions and then composing AND predicates, you can use the PredicatePal framework:
let compound = *(Key("qIndex") == qIndex && Key("formUUID") == formUUID)
Assuming that qIndex and formUUID are the correct type, Swift will automatically deduce the correct types for the Key objects.

create a Compound Predicate in coreData xcode iphone

HI i am working on the core data with 3 entities (Class,Students,ExamRecord) and their relations area as :
Class<------>> Students <------> ExamRecord
I created a predicate for fetching list of students for class 5th.
NSString * fmt2 = #"studentsToClass.className=%#";
NSPredicate * p2 = [NSPredicate predicateWithFormat:fmt2,#"5th",nil];
with this i am getting all students of class 5th
Now i also want to apply another filter on the Students fetched.
Fetch students whose Exam Record "result" is "Pass".result is an attribute for student in ExamResult entity
How can i make use of Compound predicate in this ?
Please correct me if i am wrong
Any help will be appreciated
Thanks
You can use a compound predicate:
NSPredicate *p1 = [NSPredicate predicateWithFormat:#"studentsToClass.className = %#", #"5th"];
NSPredicate *p2 = [NSPredicate predicateWithFormat:#"studentsToExamRecord.result = %#", #"Pass"];
NSPredicate *p = [NSCompoundPredicate andPredicateWithSubpredicates: #[p1, p2]];
Or you simply combine the tests with "AND":
NSPredicate *p = [NSPredicate predicateWithFormat:#"studentsToClass.className = %# AND studentsToExamRecord.result = %#",
#"5th", #"Pass"];
Note that the argument list of predicateWithFormat is not nil-terminated.
The number of arguments is determined by the number of format specifiers in the format
string.
First, you shouldn't really call the student - class relation studentsToClass. The name of the relation should reflect what type of object is at the other end.
E.g.
In this case the Student relation to Class should be called class because the object there is a single Class entity.
The inverse relation should not be called classToStudent it should be called students because the object there is a NSSet of multiple Students.
EDIT
Just to add to this. The name of the relation should explain WHY it is there. We can see that the relation is from class to student but if you call it "classToStudent" it doesn't explain anything. Also, what if you have a second relation from class to student? What do you call that. If you call it attendees or pupils or attendingStudents etc.. it gives the relation meaning.
SOLUTION
In this example I'm going to call them how I would call them and you will see it makes it a bit easier to understand...
Anyway...
NSPredicate *classPredicate = [NSPredicate predicateWithFormat:#"class.className = %#", #"5th"];
NSPredicate *passPredicate = [NSPredicate predicateWithFormat:#"result.name = %#", #"Pass"];
NSCompoundPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:#[classPredicate, passPredicate]];
First, your quoted predicate is really already wrong. You should reference the managed object, not its property (i.e. not the name of the Class). It should be:
[NSPredicate predicateWithFormat:#"class = %#", classObject];
Also, you should really choose more readable names for your variables and property. So, not fmt2 but formattingString. Not studentsToClass but form ("class" is a special word in objective-C). You get the idea.
So your desired compound predicate is done like this (short version):
[NSPredicate predicateWithFormat:#"class = %# && record.result = %#",
classObject, #"Pass"];
The complicated version, if you really need a higher level of abstraction (which I doubt):
classPredicate = [NSPredicate predicateWithFormat:#"class = %#", classObject];
resultPredicate = [NSPredicate predicateWithFormat:#"record.result = %#", #"Pass"];
finalPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:
#[classPredicate, resultPredicate]];
Predicates can also be nested using compounded predicates (For Swift)
let orPredicate = NSCompoundPredicate(type: .or, subpredicates: [date_1KeyPredicate, date_2KeyPredicate])
let functionKeyPredicate = NSPredicate(format: "function_name = %#", self.title!)
let andPredicate = NSCompoundPredicate(type: .and, subpredicates: [orPredicate, functionKeyPredicate])