Swift Protocol Defining Class Method Returning Self

Swift protocol defining class method returning self

Self in a protocol is a requirement that implementations of the protocol use their own type. Since Invoice is the type you're adopting the protocol in, your implementation of FromJson should have a return type of Invoice.

Protocol func returning Self

The problem is that you're making a promise that the compiler can't prove you'll keep.

So you created this promise: Calling copy() will return its own type, fully initialized.

But then you implemented copy() this way:

func copy() -> Self {
return C()
}

Now I'm a subclass that doesn't override copy(). And I return a C, not a fully-initialized Self (which I promised). So that's no good. How about:

func copy() -> Self {
return Self()
}

Well, that won't compile, but even if it did, it'd be no good. The subclass may have no trivial constructor, so D() might not even be legal. (Though see below.)

OK, well how about:

func copy() -> C {
return C()
}

Yes, but that doesn't return Self. It returns C. You're still not keeping your promise.

"But ObjC can do it!" Well, sort of. Mostly because it doesn't care if you keep your promise the way Swift does. If you fail to implement copyWithZone: in the subclass, you may fail to fully initialize your object. The compiler won't even warn you that you've done that.

"But most everything in ObjC can be translated to Swift, and ObjC has NSCopying." Yes it does, and here's how it's defined:

func copy() -> AnyObject!

So you can do the same (there's no reason for the ! here):

protocol Copyable {
func copy() -> AnyObject
}

That says "I'm not promising anything about what you get back." You could also say:

protocol Copyable {
func copy() -> Copyable
}

That's a promise you can make.

But we can think about C++ for a little while and remember that there's a promise we can make. We can promise that we and all our subclasses will implement specific kinds of initializers, and Swift will enforce that (and so can prove we're telling the truth):

protocol Copyable {
init(copy: Self)
}

class C : Copyable {
required init(copy: C) {
// Perform your copying here.
}
}

And that is how you should perform copies.

We can take this one step further, but it uses dynamicType, and I haven't tested it extensively to make sure that is always what we want, but it should be correct:

protocol Copyable {
func copy() -> Self
init(copy: Self)
}

class C : Copyable {
func copy() -> Self {
return self.dynamicType(copy: self)
}

required init(copy: C) {
// Perform your copying here.
}
}

Here we promise that there is an initializer that performs copies for us, and then we can at runtime determine which one to call, giving us the method syntax you were looking for.

Protocol for class method

In a class definition, static is an alias for class final,
so it marks a type method (or property) which cannot be overridden
in subclasses.

Since you want to override the method in subclasses,
all you have to do is to define the method as class instead of static:

extension A: MyManagedObjectCoolStuff {

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

extension B: MyManagedObjectCoolStuff {

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

extension C: MyManagedObjectCoolStuff {

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

Alternatively one could use the fact that for a Core Data entity,
the class name is usually defined as <ModuleName>.<EntityName>
so that the entity name is the last component of the class name.

So you could define entityName() as an
extension method of NSManagedObject (the superclass of all Core Data
object classes) as in How can I create instances of managed object subclasses in a NSManagedObject Swift extension?:

extension NSManagedObject {

class func entityName() -> String {
let classString = NSStringFromClass(self)
// The entity is the last component of dot-separated class name:
let components = split(classString) { $0 == "." }
return components.last ?? classString
}
}

and override it only where necessary:

class A: NSManagedObject { }

class B: A { }

class C: A { }

extension C {

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

println(A.entityName()) // A
println(B.entityName()) // B
println(C.entityName()) // Cat

Why using Self as return type is not considered as the protocol's constraint?

Because it's not useful to have a Self as a parameter type of a method. Suppose that you can do:

protocol P {
func f(_ x: Self)
}

class A: P {
func f(_ x: Self) {
// not relevant
}
}

class B: A { }

Now suppose I have:

func g(x: A) {
// what can I do with x?
}

The fact is, there is no way to call x.f. Because I can pass an instance of B to x, in which case x.f would accept a B instead. x could be an instance of any subclass of A that I have no way of knowing at compile time, so I don't know what I can pass to x.f.


Compare that to Self used as the return type:

protocol P {
func f() -> Self
}

// Implementation:
class A: P {
func f() -> Self {
self
}
}

class B: A { }

func g(x: A) {
let foo: A = x.f()
}

Here, we know that I can at least assign the return value of x.f to a variable of type A. Even if x is an instance of B, which means that f returns a B, we can still assign that to a variable of type A.

In swift, how do I return an object of the same type that conforms to a protocol

Autocomplete will help you, but basically, if Foo is a class, you just need a method which matches the exact signature of the method in your protocol:

class Foo {}

protocol JSONInitializable {
static func initialize(fromJSON: [String : AnyObject]) throws -> Self?
}

extension Foo: JSONInitializable {
static func initialize(fromJSON: [String : AnyObject]) throws -> Self? {
return nil
}
}

As a note here, you will have to, within this method, replace all instances of Foo with Self. If you want to use a constructor, it will have to be one marked as required.

With this in mind, it probably makes more sense to change your protocol to requiring an initializer rather than a static method called initialize, in which case, take a look at Blake Lockley's answer. However, this answer stands to answer the question of how to deal with Self in protocols, as there are certainly cases where we might want a method that returns Self that isn't an initializer.


If Foo is not a class, it is a value type which cannot be subclasses, and as such, we return the name of the type:

struct Foo: JSONInitializable {
static func initialize(fromJSON: [String : AnyObject]) throws -> Foo? {
return nil
}
}
enum Foo: JSONInitializable {
case One, Two, Three

static func initialize(fromJSON: [String : AnyObject]) throws -> Foo? {
return nil
}
}

The reason you need to return Self? from the method in the case of a class is because of inheritance. The protocol declares that if you conform to it, you will have a method called initialize whose return type will be an optional version of whatever you are.

If we write Foo's initialize method as you wrote it, then if we subclass Foo with Bar, then now we have broken our conformance to the protocol. Bar has inherited the conformance to the protocol from Foo, but it doesn't have a method which is called initialize and returns Bar?. It has one that returns Foo.

Using Self here means that when our subclasses inherit this method, it will return whatever type they are. Self is Swift's equivalent of Objective-C's instancetype.

Method in non-final class must return `Self` to conform to protocol

In Swift 3 or 4:

import Foundation

protocol P {
static func f() -> Self
static func g() -> Self
}

extension P {
static func f() -> Self {
return g()
}
}

extension Data: P {
static func g() -> Data {
return self.init()
}
}

Or you can replace your class with a final subclass:

import Foundation

protocol P {
static func f() -> Self
static func g() -> Self
}

extension P {
static func f() -> Self {
return g()
}
}

import Foundation

final class MyData: NSData {}
extension MyData: P {
static func g() -> Self {
return self.init()
}
}

If NSData is one of those class clusters that can't easily be subclassed (you'll see a stacktrace with __CFRequireConcreteImplementation), you may have to create a final class wrapper for a real NSData instead of using a subclass.



Related Topics



Leave a reply



Submit