How do you do these Extensions with static methods?

Hi


I tried to follow the idea here: https://gist.github.com/capttaco/adb38e0d37fbaf9c004eto make my CoreData code more type safe. So I added this protocol to my project:


protocol Fetchable  // Fetchable from https://gist.github.com/capttaco/adb38e0d37fbaf9c004e
{
    typealias FetchableType: NSManagedObject

    static func entityName() -> String

    static func objectsInContext(context: NSManagedObjectContext, predicate: NSPredicate?, sortedBy: String?, ascending: Bool) throws -> [FetchableType]
    static func oneObjectInContext(context: NSManagedObjectContext, predicate: NSPredicate?, sortedBy: String?, ascending: Bool) throws -> FetchableType?
    static func allObjectsInContext(context: NSManagedObjectContext, 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
}


Then this protocol extension:

extension Fetchable where Self : NSManagedObject, FetchableType == Self
{

    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 oneObjectInContext(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 allObjectsInContext(context: NSManagedObjectContext, sortedBy: String? = nil, ascending: Bool = false) throws -> [FetchableType] {
        return try objectsInContext(context, sortedBy: sortedBy, ascending: ascending)
    }

    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 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
        request.predicate = predicate  // Can be nil
        if sortedBy != nil {
            let sort = NSSortDescriptor(key: sortedBy, ascending: ascending)
            request.sortDescriptors = [sort]
        }
        return request
    }

}


Then I added this protocol to one of my NSManagedObject classes:

extension LooseOffering: Fetchable {   ///@@@ This is giving Does not conform to protocol error


    class func entityName() -> String {
        return "LooseOffering"
    }

}


But as the comment says, this makes xcode give me a little red triangle saying "Type LooseOffering does not conform to protocol Fetchable.


LooseOffering is in Objective-C:

@class Account, Count, Event;
@interface LooseOffering : NSManagedObject
@property (nonatomic, retain) Account *account;
@property (nonatomic, retain) Count *count;
@property (nonatomic, retain) Event *event;
@end



Can anyone see what is happening there? This is in XCode 7.0 beta 5.


TIA

Mark

Accepted Reply

You aren't providing a typealias for FetchableType in the extension of LooseOffering.


Xcode can often infer what the typealias should be if you have a concrete implementation of the methods from the protocol which use that typealiased-type as a parameter, but since you are relying on the default implementations from the protocol extension, you'll need to include an explicit typealias statement in the conforming class.


extension LooseOffering: Fetchable
{
    typealias FetchableType = LooseOffering
   
    class func entityName() -> String {return "LooseOffering"}
}

Replies

You aren't providing a typealias for FetchableType in the extension of LooseOffering.


Xcode can often infer what the typealias should be if you have a concrete implementation of the methods from the protocol which use that typealiased-type as a parameter, but since you are relying on the default implementations from the protocol extension, you'll need to include an explicit typealias statement in the conforming class.


extension LooseOffering: Fetchable
{
    typealias FetchableType = LooseOffering
   
    class func entityName() -> String {return "LooseOffering"}
}

Thanks. That made the red dot in the gutter go away. I don't really understand why.


But I found it quite satisfying to be able to add this code to the protocol exension:


static func entityName() -> String
    {
        return Self.description()
    }


and get the name of the class, which is useful for Core Data.

The reason why your class didn't conform to the protocol without the typealias being defined is because the protocol extension where the default methods are implemented is constrained to a particular case (types where the FetchableType typealias is defined as Self).


Without your type having a subtype FetchableType typealiased to Self, your type doesn't inherit those methods and so doesn't meet the requirements for the protocol.


You could actually set a default typealias in the protocol definition too, like this:

protocol Fetchable 
{ 
    typealias FetchableType: NSManagedObject = Self

     /* ... */
}

if it's valid for all types which conform to Fetchable to use those default implementations, and then you wouldn't need to add the typealias to all the individual types which conform to it.

Thanks again! That was brilliant. I already had

protocol Fetchable
{
    typealias FetchableType: NSManagedObject


I just had to add " = Self", and I could remove "typealias FetchableType = LooseOffering" from the extension of the NSManagedObject entity subclass. That removes a requirement from all those subclasses.


I'm so steeped in object oriented programming that this is like a new world to me. I appreciate your time explaining this to me.


Regards

Mark

I've just tried out that code on XCode 7 beta 6, it has stopped working. In this snippet, the line return fetchResults as! [FetchableType] is giving me the error: 'FetchableType' is ambiguous for type lookup in this context


It is all the same as before. Have they introduces a bug in the compiler?


extension Fetchable where Self : NSManagedObject, FetchableType == Self
{
    static func entityName() -> String
    {
        return Self.description()
    }

    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]
    }

I'm not seeing that error in the playground I used to test the code (Xcode 7 beta 6, OS X playground).


Have you cleaned the project since updating, so that everything will be recompiled?


If that doesn't help, I'd search the project for "typealias FetchableType" and see if you have extra definitions somewhere?

"I'd search the project for "typealias FetchableType" and see if you have extra definitions somewhere?"


I didn't have that exactly, but I had added this line, to save the work of putting Fetchable on all my entity classes. When I commented it out the red dots went away.

extension NSManagedObject: Fetchable {}