-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create a generic CoreDataQuery
type
#19394
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
import Foundation | ||
|
||
struct CoreDataQuery<Result: NSManagedObject> { | ||
private var predicates = [NSPredicate]() | ||
private var sortDescriptors = [NSSortDescriptor]() | ||
|
||
private var includesSubentities: Bool = true | ||
|
||
init() { | ||
} | ||
|
||
func ascending(by key: String) -> Self { | ||
var query = self | ||
query.sortDescriptors.append(NSSortDescriptor(key: key, ascending: true)) | ||
return query | ||
} | ||
|
||
func descending(by key: String) -> Self { | ||
var query = self | ||
query.sortDescriptors.append(NSSortDescriptor(key: key, ascending: false)) | ||
return query | ||
} | ||
|
||
func includesSubentities(_ value: Bool) -> Self { | ||
var query = self | ||
query.includesSubentities = value | ||
return query | ||
} | ||
|
||
func count(in context: NSManagedObjectContext) -> Int { | ||
(try? context.count(for: buildFetchRequest())) ?? 0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It'll be a bit more work, but let's not eat the error here – we should pass it up to the caller to deal with (for now we can have the same implementation at a higher level, but there's a difference between "there are no cells" and "there was an error trying to fetch the data", and the higher we can propagate that error, the more likely we can help the user understand what's going on) |
||
} | ||
|
||
func first(in context: NSManagedObjectContext) throws -> Result? { | ||
let request = buildFetchRequest() | ||
request.fetchLimit = 1 | ||
return (try context.fetch(request).first) | ||
} | ||
|
||
func result(in context: NSManagedObjectContext) throws -> [Result] { | ||
try context.fetch(buildFetchRequest()) | ||
} | ||
|
||
private func buildFetchRequest() -> NSFetchRequest<Result> { | ||
let request = NSFetchRequest<Result>(entityName: Result.entityName()) | ||
request.includesSubentities = includesSubentities | ||
request.sortDescriptors = sortDescriptors | ||
request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicates) | ||
return request | ||
} | ||
|
||
func and(_ predicate: NSPredicate) -> Self { | ||
var query = self | ||
query.predicates.append(predicate) | ||
return query | ||
} | ||
} | ||
|
||
extension CoreDataQuery { | ||
private func compare<Value>(_ keyPath: KeyPath<Result, Value>, type: NSComparisonPredicate.Operator, _ value: Value?, options: NSComparisonPredicate.Options = []) -> Self { | ||
and( | ||
NSComparisonPredicate( | ||
leftExpression: NSExpression(forKeyPath: keyPath), | ||
rightExpression: NSExpression(forConstantValue: value), | ||
modifier: .direct, | ||
type: type, | ||
options: options | ||
) | ||
) | ||
} | ||
|
||
func contains<Value>(_ keyPath: KeyPath<Result, Value>, _ value: Value) -> Self { | ||
compare(keyPath, type: .contains, value) | ||
} | ||
|
||
func equal<Value>(_ keyPath: KeyPath<Result, Value>, _ value: Value) -> Self { | ||
compare(keyPath, type: .equalTo, value) | ||
} | ||
|
||
func null<Value>(_ keyPath: KeyPath<Result, Value>) -> Self { | ||
compare(keyPath, type: .equalTo, nil) | ||
} | ||
|
||
func notNull<Value>(_ keyPath: KeyPath<Result, Value>) -> Self { | ||
compare(keyPath, type: .notEqualTo, nil) | ||
} | ||
|
||
func order<Value>(by keyPath: KeyPath<Result, Value>, ascending: Bool = true) -> Self { | ||
let property = NSExpression(forKeyPath: keyPath).keyPath | ||
return ascending ? self.ascending(by: property) : self.descending(by: property) | ||
} | ||
} | ||
|
||
protocol CoreDataQueryable { | ||
associatedtype CoreDataQueryResult: NSManagedObject | ||
|
||
static func query() -> CoreDataQuery<CoreDataQueryResult> | ||
} | ||
|
||
extension NSManagedObject: CoreDataQueryable {} | ||
|
||
extension CoreDataQueryable where Self: NSManagedObject { | ||
static func query() -> CoreDataQuery<Self> { | ||
return CoreDataQuery<Self>() | ||
} | ||
} | ||
|
||
extension CoreDataQuery where Result == Blog { | ||
|
||
static func `default`() -> CoreDataQuery<Blog> { | ||
Blog.query().order(by: \.settings?.name).includesSubentities(false) | ||
} | ||
|
||
func blogID(_ id: Int) -> Self { | ||
blogID(id as NSNumber) | ||
} | ||
|
||
func blogID(_ id: NSNumber) -> Self { | ||
equal(\.blogID, id) | ||
} | ||
|
||
func blogID(_ id: Int64) -> Self { | ||
blogID(id as NSNumber) | ||
} | ||
|
||
func username(_ username: String) -> Self { | ||
equal(\.account?.username, username) | ||
} | ||
|
||
func hostname(containing hostname: String) -> Self { | ||
contains(\.url, hostname) | ||
} | ||
|
||
func hostname(matching hostname: String) -> Self { | ||
equal(\.url, hostname) | ||
} | ||
|
||
func visible(_ flag: Bool) -> Self { | ||
equal(\.visible, flag) | ||
} | ||
|
||
func hostedByWPCom(_ flag: Bool) -> Self { | ||
flag ? notNull(\.account) : null(\.account) | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit:
Result
here is confusing to me because of theResult<T, Error>
type. WDYT?