I'm running into a few oddities while trying to conform to StringLiteralConvertible:
class Person: StringLiteralConvertible {
var name = ""
init(name n:String){
name = n
}
init(stringLiteral value: StringLiteralType){
name = n
}
init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType){
name = n
}
init(unicodeScalarLiteral value: UnicodeScalarLiteralType){
name = n
}
}
var ironMan = Person(name: "Tony Stark")
var spiderMan: Person = "Peter Parker"
I implemented both the ExtendedGraphemeClusterLiteralConvertible
and the UnicodeScalarLiteralConvertible
protocols (whatever on Earth that means).
However I still got errors and had to provide definitions for both ExtendedGraphemeClusterLiteralType
and UnicodeScalarLiteralType
:
typealias ExtendedGraphemeClusterLiteralType = String
typealias UnicodeScalarLiteralType = String
Why did I have to provide this, if it's already in the standard header???
The compiler considered it still had the right to complain and forced me to add the required
keyword to the inits, even though the definition of the protocols does not include the required
keyword! Why????
The following code compiles, but I don't understand why the first version didn't compile!
class Person: StringLiteralConvertible {
var name = ""
init(name n:String){
name = n
}
typealias ExtendedGraphemeClusterLiteralType = String
typealias UnicodeScalarLiteralType = String
required convenience init(stringLiteral value: StringLiteralType){
self.init(name: value)
}
required convenience init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType){
self.init(name: value)
}
required convenience init(unicodeScalarLiteral value: UnicodeScalarLiteralType){
self.init(name: value)
}
}
Swift, if you like the compiler to yell at you, you're gonna love it! :-P
The answer to your first question lies in the fact that Swift protocols that have associated types can be considered abstract, and need to be bound to a specific type. Associated types are an alternative to using a more specialized approach like class MyClass : IsCompatibleWith<Int> {}. I think the reason for this is to have common hierarchies, but this limitation also causes many other headaches. No matter what though, it is reasonable and expected that you need to specify the type to which the protocol is bound. In Swift, you do this by type-aliasing associated type to the one you want (String in your case).
Also note, that the associated type is not specified in the protocol, the protocol only specifies that you must define it. That said, unless you have conflicting methods on your class, the compiler can generally infer the associated type (read this in the docs somewhere).
It appears that initializers specified in protocols are required by default / nature. The documentation says that you can implement them as designnated or convenience, but that either way they are required:
Class Implementations of Protocol Initializer Requirements
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 } }
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.
From my reading though, it answers what
but not why
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With