Skip to content

Instantly share code, notes, and snippets.

@capttaco
Last active October 14, 2024 15:20
Show Gist options
  • Save capttaco/adb38e0d37fbaf9c004e to your computer and use it in GitHub Desktop.
Save capttaco/adb38e0d37fbaf9c004e to your computer and use it in GitHub Desktop.
A utility protocol for custom NSManagedObjects that makes querying contexts simpler and more convenient. Requires Swift 2.
import CoreData
protocol Fetchable
{
typealias FetchableType: NSManagedObject
static func entityName() -> String
static func objectsInContext(context: NSManagedObjectContext, predicate: NSPredicate?, sortedBy: String?, ascending: Bool) throws -> [FetchableType]
static func singleObjectInContext(context: NSManagedObjectContext, predicate: NSPredicate?, sortedBy: String?, ascending: Bool) throws -> FetchableType?
static func objectCountInContext(context: NSManagedObjectContext, predicate: NSPredicate?) -> Int
static func fetchRequest(context: NSManagedObjectContext, predicate: NSPredicate?, sortedBy: String?, ascending: Bool) -> NSFetchRequest
}
extension Fetchable where Self : NSManagedObject, FetchableType == Self
{
static func singleObjectInContext(context: NSManagedObjectContext, predicate: NSPredicate? = nil, sortedBy: String? = nil, ascending: Bool = false) throws -> FetchableType?
{
let managedObjects: [FetchableType] = try objectsInContext(context, predicate: predicate, sortedBy: sortedBy, ascending: ascending)
guard managedObjects.count > 0 else { return nil }
return managedObjects.first
}
static func objectCountInContext(context: NSManagedObjectContext, predicate: NSPredicate? = nil) -> Int
{
let request = fetchRequest(context, predicate: predicate)
let error: NSErrorPointer = nil;
let count = context.countForFetchRequest(request, error: error)
guard error != nil else {
NSLog("Error retrieving data %@, %@", error, error.debugDescription)
return 0;
}
return count;
}
static func objectsInContext(context: NSManagedObjectContext, predicate: NSPredicate? = nil, sortedBy: String? = nil, ascending: Bool = false) throws -> [FetchableType]
{
let request = fetchRequest(context, predicate: predicate, sortedBy: sortedBy, ascending: ascending)
let fetchResults = try context.executeFetchRequest(request)
return fetchResults as! [FetchableType]
}
static func fetchRequest(context: NSManagedObjectContext, predicate: NSPredicate? = nil, sortedBy: String? = nil, ascending: Bool = false) -> NSFetchRequest
{
let request = NSFetchRequest()
let entity = NSEntityDescription.entityForName(entityName(), inManagedObjectContext: context)
request.entity = entity
if predicate != nil {
request.predicate = predicate
}
if (sortedBy != nil) {
let sort = NSSortDescriptor(key: sortedBy, ascending: ascending)
let sortDescriptors = [sort]
request.sortDescriptors = sortDescriptors
}
return request
}
}
@rothomp3
Copy link

rothomp3 commented Aug 7, 2015

You can replace FetchableType with Self by the way and simplify things!

class MyClass { 
       static func build() -> MyClass { 
           return MyClass() 
       } 
   }    
protocol Thingable 
{ 
    func getThing() -> Self 
}    
extension Thingable where Self: MyClass 
{ 
    func getThing() -> Self { 
        return Self.build() as! Self
     } 
}    

@Logistes
Copy link

Instead of protocol Fetchable
{
typealias FetchableType: NSManagedObject

wouldn't this be better?

protocol Fetchable
{
typealias FetchableType: NSManagedObject = Self

I got that advice on the Apple forums: https://forums.developer.apple.com/thread/14153

@daehn
Copy link

daehn commented Aug 31, 2015

It should also be possible to provide a default implementation for the entityName in the protocol extension:

static func entityName() -> String {
        return NSStringFromClass(self).componentsSeparatedByString(".").last!
    }

And then use it in the fetchRequest() function:

NSEntityDescription.entityForName(FetchableType.entityName(), inManagedObjectContext: context)

@arseniy-panfilov
Copy link

the guard on line 29 definitely should look like this:

        guard error == nil else {

@niels-k-86
Copy link

@Logistes

Instead of protocol Fetchable

{
typealias FetchableType: NSManagedObject

wouldn't this be better?

protocol Fetchable
{
typealias FetchableType: NSManagedObject = Self

Yes, that makes things easier. However, you still need to define the typealias when subclassing an NSManagedObject subclass.

class Image: NSManagedObject, Fetchable {
    // This works, without typealias
}

class ThumbnailImage: Image {
    // Need redefining of FetchableType if you want the Fetchable methods to return instances of ThumbnailImage
    typealias FetchableType = ThumbnailImage
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment