How to create a universal convenience initializer in Swift?

I do generics in Swift. I have an extension to the NSManagedObject class and you want to create an initializer that is only available for classes that implement a specific protocol I. Now I have something like the following, but this does not work and does not even compile. Could you help me make it work?

 public extension NSManagedObject { public convenience init<Self: Nameable>(context: NSManagedObjectContext) { let entity = NSEntityDescription.entityForName(Self.entityName(), inManagedObjectContext: context)! self.init(entity: entity, insertIntoManagedObjectContext: context) } } public protocol Nameable { static func entityName() -> String } 

Xcode says: "The common sign of" I "is not used in the signature of the function."

+7
generics swift
source share
3 answers

As matte already explained, you cannot define an initializer that is limited to types that implement the protocol. Alternatively, you can instead define a global function:

 public protocol Nameable { static func entityName() -> String } func createInstance<T : NSManagedObject where T: Nameable>(type : T.Type, context : NSManagedObjectContext) -> T { let entity = NSEntityDescription.entityForName(T.entityName(), inManagedObjectContext: context)! return T(entity: entity, insertIntoManagedObjectContext: context) } 

which is then used as

 let obj = createInstance(Entity.self, context) 

You can avoid an extra type parameter if you define a method

 func createInstance<T : NSManagedObject where T: Nameable>(context : NSManagedObjectContext) -> T { ... } 

and use it as

 let obj : Entity = createInstance(context) 

or

 let obj = createInstance(context) as Entity 

where the type is now inferred from the context.

+10
source share

It seems to me that you are describing something like this:

 class Thing {} func makeANewThing<T:ThingMaker>(caller:T) -> Thing { let t = Thing() return t } protocol ThingMaker { } class Dog : ThingMaker { } class Cat { // not a ThingMaker } let t = makeANewThing(Dog()) // ok let t2 = makeANewThing(Cat()) // illegal 

In real life, I assume that makeANewThing really do something with its caller , but the fact is that it can only be called by passing in the caller that ThingMaker accepted.

This is probably the best thing you can do in Swift 1. If you want to implement the method only in classes that accept a specific protocol, then what you want is an extension of the protocol, but only available in Swift 2.

+2
source share

Ok, thanks for your comments on this topic. I realized with @matt comments that I cannot do what I thought because it is not even possible. I did not want to subclass it to work, and that was not possible with my understanding of the problem.

Finally, I found another way to get the name of the entity. This has pros and cons, but I decided to use it now. Then I created an extension for NSManagedObject to make it work.

 extension NSManagedObject { class func entityName() -> String { let fullClassName = NSStringFromClass(object_getClass(self)) let nameComponents = split(fullClassName) { $0 == "." } return last(nameComponents)! } public convenience init(context: NSManagedObjectContext) { let name = self.dynamicType.entityName() let entity = NSEntityDescription.entityForName(name, inManagedObjectContext: context)! self.init(entity: entity, insertIntoManagedObjectContext: context) } } 

And then

 obj = Obj(context: ctx) 
0
source share

All Articles