NSPredicate with Swift and Core Data - swift

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.

Related

Swift - Core Data - using the result of fetch with GroupBy

I couldn't find answer to this question, I checked all questions here.
Summary:
I don't know how to use the result(that I successfully get back) of .fetch() method with GroupBy. The fetch method result must have .dictionaryResultType because I am using GroupBy, otherwise the GroupBy would not work. I can see that fetch returns [Any], so array of Any. However I try to cast/use/access this [Any] it fails.
My code:
//Create new entity
let timeLogEntity = NSEntityDescription.entity(forEntityName: "TimeLog", in: managedContext)!
//sum up the duration column
let keypathDuration = NSExpression(forKeyPath: "duration")
let expression = NSExpression(forFunction: "sum:", arguments: [keypathDuration])
let sumDesc = NSExpressionDescription()
sumDesc.expression = expression
sumDesc.name = "DurationSum"
sumDesc.expressionResultType = .integer64AttributeType
//Fetch request
var timeLogFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "TimeLog")
//group by region
timeLogFetchRequest.returnsObjectsAsFaults = false
timeLogFetchRequest.propertiesToGroupBy = ["region"]
//sum of duration column
timeLogFetchRequest.propertiesToFetch = [sumDesc, "region"]
timeLogFetchRequest.resultType = .dictionaryResultType
//it works only like this with [Any[\]
var dailyFetchResult: [Any]?
do{
dailyFetchResult = try managedContext.fetch(timeLogFetchRequest)
}catch...
So the fetch works and returns values, I just don't know how to use them, or convert them to anything usable dictionary, tuple, String and Int...
The result looks like this: {DurationSum = 235; region = RegionName1}{DurationSum = 256; region = RegionName2} etc
Thanks
(dailyFetchResult[0] as! [String:Any]).keys , and (dailyFetchResult[0] as! [String:Any]).values did it. Thank you!

Core Data - why is my NSPredicate not resulting in the correct SQL query?

I have tried both these NSPredicates:
let predicate = NSPredicate(format: “personID == %lld", id)
let predicate = NSPredicate(format: “personID == %d", id)
I enabled SQL logging and saw this at the end of the SQL query:
WHERE t0.ZPERSONID = ?
In variables view, id has a value of Int64(156), but regardless of what I put in place of id, the resulting SQL query has t0.ZPERSONID = ? for its WHERE clause. Because of this, the predicate is useless and I’m getting duplicate record insertions every time. How do I get t0.ZPERSONID = 156?
WHERE t0.ZPERSONID = ?
is (part of) the prepared statement. To see which values the parameters are bound to, set the Core Data debugging level to 3 (compare How to print Core Data debug values?):
-com.apple.CoreData.SQLDebug 3
-com.apple.CoreData.Logging.stderr 1
Also print(predicate) is useful to check the created predicate
for correctness.
In your case the problem is that you pass an NSNumber instance
as argument, but the %lld format expects an Int64.
You can convert the NSNumber to an Int64
let id = NSNumber(value: Int64(156))
let p1 = NSPredicate(format: "personID == %lld", id.int64Value)
print(p1) // personID == 156
or even simpler, use the %# format instead:
let p2 = NSPredicate(format: "personID == %#", id)
print(p2) // personID == 156

Filtering Realm objects with Swift

I always get the following error when trying to filter my Realm database using NSPredicate:
Property 'text' is not a link in object of type 'getType'
I want to filter my Realm database to show only the items that have some specific text in them. This is what I've tried:
let realm = try! Realm()
let predicate = NSPredicate(format: "typez.text.filter = 'special'")
let filterThis = realm.objects(Publication).filter(predicate)
print(filterThis)
The relevant portion of my model classes is:
class Publication: Object, Mappable {
dynamic var id: Int = 0
var typez = List<getType>()
dynamic var url: String?
}
class getType: Object, Mappable {
dynamic var text: String = ""
}
You mentioned that the relevant portions of you model classes look like so:
class Publication: Object, Mappable {
dynamic var id: Int = 0
var typez = List<getType>()
dynamic var url: String?
}
class getType: Object, Mappable {
dynamic var text: String = ""
}
If I understand you correctly, you want to find Publication instances that have an entry in their typez list with text equal to special. You can express that as:
let realm = try! Realm()
let result = realm.objects(Publication).filter("ANY typez.text = 'special'")
print(result)
I was not liking the accepted answer here because it doesn't actually answer the question... but then it helped me more than I realized. I will now be using closures instead of NSPredicates whenever possible. The actual answer to this question should be a slightly modified version of #NSGangster's answer:
let realm = try! Realm()
//Array of publications
let realmObjects = realm.objects(Publication)
//any publication where .text property == special will be filtered. and filter out empty array
let filterThis = realmObjects.filter({ $0.typez.filter({ $0.text == "special" } != [] ) })
print(filterThis)
.. or something close to that.
But what I was looking for was a bit different. I needed a way to filter on exact words of a multi-word string, and using an NSPredicate with "CONTAINS" would match any containing substring, e.g. a search for "red" would match "fred". Realm doesn't support "LIKE" or regex yet, so using a closure was the only thing I could get to work:
//I was going for a "related terms" result for a dictionary app
let theResults = terms.filter(
{
//Looking for other terms in my collection that contained the
//title of the current term in their definition or more_info strings
$0.definition.components(separatedBy: " ").contains(term.title) ||
$0.more_info.components(separatedBy: " ").contains(term.title)
}
)
With as much of the day as I spent searching, hopefully this helps someone else with a similar issue.
I don't usually use NSPredicate's directly, instead I do an inline predicate closure within the filter paramter.
let realm = try! Realm()
//Array of publications
let realmObjects = realm.objects(Publication)
//any publication where .text property == special will be filtered. and filter out empty array
let filterThis = realmObjects.filter({ $0.getType.filter({ $0.text == "special" } != [] ) })
print(filterThis)

Compare string with CoreData

I have a String (for example "Test") and I also have a few strings which are stored in CoreData. Now I'd like to check if the string "Test" also exists in CoreData. So I simply want to compare all strings which are saved in CoreData with the string "Test".
Does someone of you know how to do this in Swift?
I don't know of any one-line solution, however you can:
Fetch with predicate
See if the returned array is empty (it doesn't exist in core data) or not (element exists).
var request : NSFetchRequest = NSFetchRequest(entityName: "Core_Data_Entity");
request.predicate = NSPredicate(format: "Property_name = %#", #"Test")
var results : [NSManagedObject] = context.executeFetchRequest(request, error: nil) as [Core_Data_Entity]
if (results.count > 0) {
//Element exists
}
else {
//Doesn't exist
}

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?