I defined a simple class:
class MyClass {
var name:String?
required init() {
println("init")
}
}
I can add a new initializer in an extension like this:
extension MyClass {
convenience init(name: String) {
self.init()
self.name = name
}
}
Everything works fine.
But as soon as I define the new initializer in a protocol:
protocol MyProtocol {
init(name:String)
}
And make my extension conform to that protocol:
extension MyClass : MyProtocol {
convenience init(name: String) {
self.init()
self.name = name
}
}
I get the following error:
Initializer requirement 'init(name:)' can only be satisfied by a
required initializer in the definition of non-final class 'MyClass'
What is going on here?
(BTW: I can't make my class final, because this is only the extract of a more complicated use case.)
Ok, my bad.
To guarantee that all subclasses conform to MyProtocol new initializer has to be marked as required as well.
Furthermore Swift requires to declare all required initializers directly within the class and does not allow to declare them in extensions.
extension MyClass : MyProtocol {
required convenience init(name: String) {
self.init()
self.name = name
}
}
Related
Both a struct and a class conforms to a protocol. I use 2 protocol extensions with where conditions to add an implementation of the var property for both class and struct.
I'm quite surprised to see a compile error for classes only.
Why does this happen for classes and not for structs?
protocol MyProtocol {
var property:String { get }
}
extension MyProtocol where Self == MyStruct {
var property: String { return "" }
}
extension MyProtocol where Self == MyClass {
var property: String { return "" }
}
struct MyStruct : MyProtocol {}
class MyClass : MyProtocol {} //Type 'MyClass' does not conform to protocol 'MyProtocol'
It does not compile because your
extension MyProtocol where Self == MyClass
provides a default method only for MyClass itself, but not for possible subclasses. Changing the constraint to
extension MyProtocol where Self: MyClass
makes the code compile. Alternatively prevent the creation of subclasses with
final class MyClass : MyProtocol {}
This is not a problem for MyStruct because struct types cannot be inherited from in Swift.
Classes can inherit from other classes.
So some class X may inherit from MyClass.
But your extension provides implementation of MyProtocol properties of if the class is MyClass and not it's descendant. So any class that inherits from MyClass won't have any properties of MyProtocol implemented. And that is the problem.
If you declare MyClass final your code would be valid:
final class MyClass : MyProtocol {}
If you extend conditional extension to any MyClass descendants your code would be valid:
extension MyProtocol where Self: MyClass {
var property: String { return "" }
}
But right now all those potential subclasses lack implementation for MyProtocol and it is not allowed.
Suppose I have a simple factory which returns various subclasses of a custom ModelObject class like:
class func testModelObject(className: String) -> ModelObject
{
let obj = // instance of the subclass of ModelObject specified by className
return obj
}
Is there a way to do this? Will Swift freak out when I try to call any methods of that object? Should I have something else for my return type?
For best type safety, you should let testModalObject to accept a meta-type like:
class ModelObject {
required init() {}
}
class Subclass: ModelObject {
required init() { super.init() }
}
func testModalObject(_ type: ModelObject.Type) -> ModelObject {
return type.init()
}
testModalObject(Subclass.self)
If you really need a string input, you will need to rely on the Objective-C runtime and do some casting (see how to create instance of a class from a string in swift 3):
#objc
class ModelObject: NSObject { // <---
required override init() {}
}
#objc(MOSubclass) // <-- tell ObjC the name is "MOSubclass" instead of "????.Subclass".
class Subclass: ModelObject {
required init() { super.init() }
}
func testModalObject(_ typeName: String) -> ModelObject? {
let cls = NSClassFromString("MO\(typeName)") as? ModelObject.Type
return cls?.init()
}
testModalObject("Subclass")!
I am trying to understand why Swift enforces a class that conforms to a protocol with an initializer to be marked as required. This essentially enforces any subclasses to also implement that initializer. Surely the designated superclass initializer would be inherited?
The quotes below are taken from the Swift Language Guide:
https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID272
You can implement a protocol initializer requirement on a conforming
class as either a designated initializer or a convenience initializer.
In both cases, you must mark the initializer implementation with the
required modifier:
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// initializer implementation goes here
}
}
class SomeSubclass: SomeClass {
required init(someParameter: Int) { // enforced to implement init again
// initializer implementation goes here
}
}
The use of the required modifier ensures that you provide an explicit
or inherited implementation of the initializer requirement on all
subclasses of the conforming class, such that they also conform to the
protocol.
EDIT:
I had not initially mentioned that I am currently limited to Swift 2.1. It appears to be a compiler issue in this release and does not occur in later versions.
Surely the designated superclass initializer would be inherited?
No, not always. If the subclass defines its own designated initialisers, then it won't automatically inherit the superclass' designated initialisers. Consider the following example:
class Foo {
init() {}
}
class Bar : Foo {
var str: String
init(str: String) {
self.str = str
}
}
let b = Bar() // illegal – what value would the 'str' property have?
As Bar defines its own init(str:) designated initialiser, it doesn't automatically inherit Foo's designated initialiser init(). This prevents unsafe initialisation in cases where the subclass declares its own stored properties.
Marking init() as required enforces Bar has an init(), be it through providing its own implementation:
class Foo {
required init() {}
}
class Bar : Foo {
var str: String
init(str: String) {
self.str = str
}
// implement required init(), as Bar defines its own designated initialiser.
required init() {
self.str = "foo" // now str is correctly initialised when calling init()
}
}
let b = Bar() // now legal
Or by inheriting Foo's implementation (when Bar doesn't define its own designated initialisers):
class Foo {
required init() {}
}
class Bar : Foo {
// inherits init() from Foo, as Bar doesn't define its own designed initialisers.
}
let b = Bar() // legal
You're not force to implement the initializer in the subclass. Consider this example, which compiles just fine:
protocol SomeProtocol {
init(someParameter: Int)
}
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// initializer implementation goes here
print(someParameter)
}
}
class SomeSubclass: SomeClass {
// Notice that no inits are implemented here
}
_ = SomeClass(someParameter: 123)
_ = SomeSubclass(someParameter: 456)
If I have the following code:
class MyClass {
...
}
protocol MyProtocol {
...
}
Is it possible to declare a type that accepts a class or subclass of MyClass conforming to MyProtocol?
e.g. in pseudo code:
var thing: MyClass & MyProtocol = ...
The naive approach works (i.e. specify the type first and then use it in the variable declaration):
class MCImplementingMP: MyClass, MyProtocol {
}
var thing: MCImplementingMP = ...
Nope, it was possible in Objective-C but not possible in Swift.
All solutions that I know looks like hacks and require lot of runtime type checking. So I came out with my own - declare a wrapper type which can act like needed class or protocol depending on circumstances:
class MyClass {}
protocol MyProtocol: class {}
class Wrapper {
var instance: AnyObject
init?(instance: MyClass) {
guard instance is MyProtocol else { return nil }
self.instance = instance
}
init?(instance: MyProtocol) {
guard instance is MyClass else { return nil }
self.instance = instance
}
var instanceAsMyClass: MyClass {
return instance as! MyClass
}
var instanceAsMyProtocol: MyProtocol {
return instance as! MyProtocol
}
}
You may want to change property names but idea is clear.
If I have a protocol that has an optional property, and a class that needs to conform to the protocol which has the same property already, but as a non-optional property, how do I make the class conform to the protocol.
protocol MyProtocol {
var a: String? { get set }
}
class MyClass {
var a: String
}
extension MyClass: MyProtocol {
// What do I put here to make the class conform
}
Unfortunately, you can't redeclare the same variable in MyClass with a different type.
What Dennis suggests will work, but if you want to keep your variable in MyClass non-Optional, then you could use a computed property to wrap around your stored variable:
protocol MyProtocol {
var a: String? { get set }
}
class MyClass {
// Must use a different identifier than 'a'
// Otherwise, "Invalid redeclaration of 'a'" error
var b: String
}
extension MyClass: MyProtocol {
var a: String? {
get {
return b
}
set {
if let newValue = newValue {
b = newValue
}
}
}
}
You just have to make the property of the class optional as well in order to conform to the protocol, as follows:
protocol MyProtocol {
var a: String? { get set }
}
class MyClass {
var a: String? // Added '?'
}
extension MyClass: MyProtocol {
// Nothing needed here to conform
}