patternswiftMinor
Swift 3 Generic fetch request extension
Viewed 0 times
swiftgenericrequestfetchextension
Problem
I implemented an extension for the
I am new to CoreData and would very much appreciate any sort of feedback for this function
NSManagedObjectContext that takes any CoreData class as a parameter and performs a fetch request on it. I am new to CoreData and would very much appreciate any sort of feedback for this function
extension NSManagedObjectContext{
func fetchObjects (_ entityClass:T.Type, sortBy: [NSSortDescriptor]? = nil, predicate: NSPredicate? = nil) throws -> [AnyObject]{
var request: NSFetchRequest
if #available(iOS 10.0, *) {
request = entityClass.fetchRequest()
} else {
let entityClassName = NSStringFromClass(entityClass)
let entityName = entityClassName.components(separatedBy: ".").last ?? entityClassName
request = NSFetchRequest(entityName: entityName)
}
var fetchRequestError: Error?
request.returnsObjectsAsFaults = false
if let predicate = predicate {
request.predicate = predicate
}
if let sortBy = sortBy {
request.sortDescriptors = sortBy
}
var fetchedResult: [T]?
do {
fetchedResult = try self.fetch(request) as? [T]
}
catch let error {
fetchRequestError = error
//print(fetchRequestError)
}
guard let entityArray = fetchedResult else {
throw fetchRequestError!
}
return entityArray
}
}Solution
Your code looks good, but there are some things which can be simplified.
Additionally, the method can be improved to return a array of the
actual managed object subclass type instead of
Simplifications and small improvements
can be simplified to
which returns the unqualified class name of the given class
(in contrast to
class name including the module name).
The
the given parameters. You can assign them directly:
We know that the fetch request returns an array of
the cast cannot fail. Therefore the second half of the method
can be reduced to
making the
need to print the error locally then no do/catch is needed at all:
A possible error will be re-thrown to the caller of the method.
Putting it all together, the method now looks like this:
Returning a "correctly" typed array
The return type is
This can be improved by changing the return type to
a typed fetched request
This is now called as
and
Additionally, the method can be improved to return a array of the
actual managed object subclass type instead of
AnyObject.Simplifications and small improvements
var request: NSFetchRequestrequest is assigned exactly once, therefore it should be a constant:let request: NSFetchRequestlet entityClassName = NSStringFromClass(entityClass)
let entityName = entityClassName.components(separatedBy: ".").last ?? entityClassNamecan be simplified to
let entityName = String(describing: entityClass)which returns the unqualified class name of the given class
(in contrast to
String(reflecting:) which returns the fully qualifiedclass name including the module name).
if let predicate = predicate {
request.predicate = predicate
}
if let sortBy = sortBy {
request.sortDescriptors = sortBy
}The
predicate and sortDescriptors of NSFetchRequest are optionals, so there is no need to conditionally unwrap the given parameters. You can assign them directly:
request.predicate = predicate
request.sortDescriptors = sortByfetchedResult = try self.fetch(request) as? [T]We know that the fetch request returns an array of
T objects,the cast cannot fail. Therefore the second half of the method
can be reduced to
do {
let fetchedResult = try self.fetch(request) as! [T]
return fetchedResult
} catch let error {
print(error.localizedDescription)
throw error
}making the
fetchRequestError variable obsolete. And if you don'tneed to print the error locally then no do/catch is needed at all:
let fetchedResult = try self.fetch(request) as! [T]
return fetchedResultA possible error will be re-thrown to the caller of the method.
Putting it all together, the method now looks like this:
extension NSManagedObjectContext{
func fetchObjects (_ entityClass:T.Type,
sortBy: [NSSortDescriptor]? = nil,
predicate: NSPredicate? = nil) throws -> [AnyObject] {
let request: NSFetchRequest
if #available(iOS 10.0, *) {
request = entityClass.fetchRequest()
} else {
let entityName = String(describing: entityClass)
request = NSFetchRequest(entityName: entityName)
}
request.returnsObjectsAsFaults = false
request.predicate = predicate
request.sortDescriptors = sortBy
let fetchedResult = try self.fetch(request) as! [T]
return fetchedResult
}
}Returning a "correctly" typed array
The return type is
[AnyObject], which means that the return value has to be cast to the actual managed object subclass typelet objects = try context.fetchObjects(YourEntity.self) as! [YourEntity]This can be improved by changing the return type to
[T] and usinga typed fetched request
NSFetchRequest:extension NSManagedObjectContext{
func fetchObjects (_ entityClass:T.Type,
sortBy: [NSSortDescriptor]? = nil,
predicate: NSPredicate? = nil) throws -> [T] {
let request: NSFetchRequest
if #available(iOS 10.0, *) {
request = entityClass.fetchRequest() as! NSFetchRequest
} else {
let entityName = String(describing: entityClass)
request = NSFetchRequest(entityName: entityName)
}
request.returnsObjectsAsFaults = false
request.predicate = predicate
request.sortDescriptors = sortBy
let fetchedResult = try self.fetch(request)
return fetchedResult
}
}This is now called as
let objects = try context.fetchObjects(YourEntity.self)and
objects is an array of YourEntity without additional cast.Code Snippets
var request: NSFetchRequest<NSFetchRequestResult>let request: NSFetchRequest<NSFetchRequestResult>let entityClassName = NSStringFromClass(entityClass)
let entityName = entityClassName.components(separatedBy: ".").last ?? entityClassNamelet entityName = String(describing: entityClass)if let predicate = predicate {
request.predicate = predicate
}
if let sortBy = sortBy {
request.sortDescriptors = sortBy
}Context
StackExchange Code Review Q#147005, answer score: 6
Revisions (0)
No revisions yet.