CKQuery where predicate has reference and not field? - cloudkit

I need to create a CKQuery where the predicate contains a reference of a record, and not a field of the record.
Like this
let query = CKQuery(recordType: "OUP", predicate: NSPredicate(format: "o = %#", "FF4FB4A9-271A-4AF4-B02C-722ABF25BF44")
How do I set o is a CKReference, not field!
I get this error:
Field value type mismatch in query predicate for field 'o'

You can use a CKRecord or CKRecordID, with or without a CKReference, to match relationships.
CKRecord:
let predicate = NSPredicate(format: "artist == %#", artist)
CKRecordID:
let predicate = NSPredicate(format: "artist == %#", artistID)
CKReference with CKRecord:
let recordToMatch = CKReference(record: artist, action: CKReferenceAction.None)
let predicate = NSPredicate(format: "artist == %#", recordToMatch)
CKReference with CKRecordID:
let recordToMatch = CKReference(recordID: artistID, action: CKReferenceAction.None)
let predicate = NSPredicate(format: "artist == %#", recordToMatch)

I found in CKQuery class reference the answer, or at least an example how to use CKReference in CKQuery:
CKReference* recordToMatch = [[CKReference alloc] initWithRecordID:employeeID action:CKReferenceActionNone];
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"employee == %#", recordToMatch];
To match records that link to a different record whose ID you know, create a predicate that matches a field containing a reference object as shown in Listing 1. In the example, the employee field of the record contains a CKReference object that points to another record. When the query executes, a match occurs when the ID in the locally created CKReference object is the same ID found in the specified field of the record.

Related

CoreData Fetch: Getting the objects with the maximum of one atttribute and having another attribute in common

I have a CoreData ManagedObject type Event with the properties name:Sting and date:Date.
I want to fetch all EventObject, with the name containing a filter. If more than one object with the same name matches the filter, only the object with the latest date should be returned.
Just to clarify what I want. In a table based approach I would query in SQL like:
SELECT name, max(date) FROM Event
WHERE name contains 'filter'
GROUP BY name
In CoreData I tried like this, but got:
Could not cast value of type 'NSKnownKeysDictionary1' to 'xx12.Event'.
let fetchRequest : NSFetchRequest<Event> = Event.fetchRequest( fetchRequest.predicate = NSPredicate (format: "name contains %#", filter)
fetchRequest.sortDescriptors = [NSSortDescriptor (key: "name", ascending: true)]
// We want Event not Dictionary back
// fetchRequest.resultType = NSFetchRequestResultType.dictionaryResultType
let keypathExpression = NSExpression(forKeyPath: "date")
let maxExpression = NSExpression(forFunction: "max:", arguments: [keypathExpression])
let key = "maxDate"
let expressionDescription = NSExpressionDescription()
expressionDescription.name = key
expressionDescription.expression = maxExpression
expressionDescription.expressionResultType = .dateAttributeType
fetchRequest.propertiesToFetch = [expressionDescription]
let resultEvents = try moc.fetch(fetchRequest)
Example:
if I have these objects
meeting1, 1.1.2020
meeting1, 1.4.2020
meeting2, 1.1.2020
meeting2, 1.4.2020
party, 2.3.2020
and the filter: meeting
I want to get the objects:
meeting1, 1.4.2020
meeting2, 1.4.2020
I would use a basic fetch request and then do the grouping part in swift code instead of working with NSExpression
let fetchRequest : NSFetchRequest<Event> = Event.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "name contains %#", filter)
let result = try moc.fetch(fetchRequest)
return Dictionary(grouping: result, by: \.name).values
.compactMap { $0.max {$0.date < $1.date } }

CoreData predicate using relationship

I have two entities - Quotes and Customers. One customer can have many quotes. The relationships are quotes and customers.
I want to get a quote object based on the customer name and email address, sorted by date but I'm stuck trying to format the predicate...
func getMostRecentQuote(name: String, email: String) -> Quotes? {
var predicateList = [NSPredicate]()
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Quotes")
let predicate1 = NSPredicate(format: "name CONTAINS[c] %#", name)
let predicate2 = NSPredicate(format: "email CONTAINS[c] %#", email)
let orCompoundPredicate = NSCompoundPredicate(type: .or, subpredicates: [predicate1, predicate2])
predicateList.append(orCompoundPredicate)
fetchRequest.predicate = NSCompoundPredicate(type: .and, subpredicates: predicateList)
fetchRequest.fetchLimit = 1
Probably you have a to-one relationship from Quotes to Customers, if not, establish one and name the property customer
Then use this single predicate
let predicate = NSPredicate(format: "customer.name CONTAINS[c] %# OR customer.email CONTAINS[c] %#", name, email)
If you want to filter the full string caseinsensitive CONTAINS is actually the wrong operator, better use LIKE
let predicate = NSPredicate(format: "customer.name LIKE[c] %# OR customer.email LIKE[c] %#", name, email)
 
Note: Please name entities in singular form, semantically your method is going to return one Quote, not one Quotes

NSPredicate - convert arguments?

Any idea how to do this in Swift 3?
let number: NSArray = [123, 456, 678]
let check = "1"
let predicate = NSPredicate(format: "number.stringValue CONTAINS %#", check)
number.filtered(using: predicate)
The problem is .stringValue. Any idea how this can be done without using a block predicate and not using the standard filter function, but with a predicate as above?
The name of the array variable (here: "number") is not part of the key path of the array elements, so it is just:
let number: NSArray = [123, 456, 678]
let check = "6"
let predicate = NSPredicate(format: "stringValue CONTAINS %#", check)
print(number.filtered(using: predicate)) // [456, 678]
Or with "SELF", if you want to be more explicit:
let predicate = NSPredicate(format: "SELF.stringValue CONTAINS %#", check)
The "SELF" keyword is explained in Predicate Format String Syntax in the "Predicate Programming Guide":
SELF
Represents the object being evaluated.

How to sort ckrecords by userID?

How should I go about creating an nspredicate that checks the userID of a record in swift?
let userID = "__defaultOwner__"
let predicate = NSPredicate(format: "keyToUseHere == %#", userID)
What should I put in place of the 'keyToUseHere' in the nspredicate to sort by the creator's id?
You need to use the key which can be property of your model class something like this:-
let predicate = NSPredicate(format: "userID" == %#", userIDValue)

Combining Two Conditions in NSPredicate

How do you combine two conditions in NSPredicate? I am using the following statement and I would like to add another condition that compares the the password with the contents of a textfield using AND:
request.predicate = NSPredicate(format: "username = %#", txtUserName.text!)
As already said, you can use logical operators like "AND", "OR"
in predicates. Details can be found in
Predicate Format String Syntax in the "Predicate Programming Guide".
As an alternative, use "compound predicates":
let p1 = NSPredicate(format: "username = %#", "user")
let p2 = NSPredicate(format: "password = %#", "password")
let predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [p1, p2])
This is useful for more complex expressions, or if you want to build
a predicate dynamically at runtime.
Try this
request.predicate = NSPredicate(format: "username = %# AND password = %#", txtUserName.text!, txtPassword.text!)
AND is exactly what you need
request.predicate = NSPredicate(format: "username = %# AND password = %#", txtUserName.text!, txtPassWord.text!)