I am attempting to take a Swift Predicate created with the #Predicate
macro and parse it into a database query statement. (SwiftData does this under the hood to query the underlying SQLite database that powers Core Data).
Example:
class Foo
{
var title: String = ""
}
let p = #Predicate<Foo> { $0.title == "test" }
Given that predicate, p
, I want to turn it into this string-ified query statement:
SELECT * FROM `Foo` WHERE `title` = "test"
The process for doing this is very sparsely documented. I'm following that example by declaring a custom protocol and then having each type of PredicateExpression
that I support adopt that protocol. (This code is very proof-of-concept at the moment; it ignores errors and handles only this very simple Predicate for now):
protocol DBPredicateExpression {
func dbQuery() -> String
}
// Handle the "==" operator
extension PredicateExpressions.Equal: DBPredicateExpression where LHS: DBPredicateExpression, RHS: DBPredicateExpression
{
func dbQuery() -> String {
return lhs.dbQuery() + " = " + rhs.dbQuery()
}
}
// Handle constant values
extension PredicateExpressions.Value: DBPredicateExpression
{
func dbQuery() -> String {
if let v = value as? String {
return "\"\(v)\""
}
return "" // TODO: handle other value types; throw for unsupported ones.
}
}
// Handle KeyPaths
extension PredicateExpressions.Keypath: DBPredicateExpression
{
func dbQuery() -> String
{
let rootType: Any.Type = type(of: root) // String(describing:) --> Variable<Foo>
let kpType: Any.Type = type(of: keyPath) // String(describing:) --> ReferenceWritableKeyPath<Foo, String>
// THIS IS WHERE I NEED HELP.
// How do I introspect `root` and `keyPath` to get string versions of them?
// This should return: "FROM `Foo` WHERE `title`"
// But getting "Foo" and "title" from `root` and `keyPath` is the tricky bit.
}
}
As stated in the comment: how do I transform the KeyPath
and Root
types into their String
equivalents (title
and Foo
)? Using string(describing:)
seems very fragile and non-performant. How does SwiftData take one of these KeyPath Predicates and transform it into the SQL statements that ultimately get run in Core Data?