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
How to Migrate Core Data's Data to App Group's Data
Properly Using Firebase Cloud Functions and Stripe
How to Call a Generic Swift Function When None of the Arguments Provides the Generic Type
Xcode Error - Undefined Symbols for Architecture X86_64
Correct Way to Call "Realloc" in Swift with a Float Array
How to Detect If a Observable Has Not Emitted Any Events for Specific Time in Rxswift
How to Understand What Is Causing a Crash Involving '_Nstouchbarfinderobservation'
Prevent Redirect Response with Alamofire in Swift
Curl with Alamofire - Swift - Multipart/Form-Data
Swift Firebase Custom Object with Document Id
Swiftui Sheet Not Updating Variable
Calculate Time Difference in Swift 4
How to Draw Line Node Keep Same Size in Camera as Measure App in Iphone
Swift Alternative to Respondstoselector:
Realitykit - How to Edit or Add a Lighting
How to Use a Value Type Object as a Reference Type