I'm trying to access a default method implementation defined in a protocol extension that's constrained in implementation to a class. A 'regular' declaration works fine, however when I try to cast to the protocol I can not access the default defined method on the protocol, though the type satisfies the where clause.
Please consider this example:
class Person {
var name: String
init(name: String) {
self.name = name
}
}
class Hero: Person {
var ability: String
init(name: String, ability: String) {
self.ability = ability
super.init(name: name)
}
}
class Pilot: Person {
var callSign: String
init(name: String, callSign: String) {
self.callSign = callSign
super.init(name: name)
}
}
class Programmer: Person {
var favoriteLanguage: String
init(name: String, favoriteLanguage: String) {
self.favoriteLanguage = favoriteLanguage
super.init(name: name)
}
}
// define a protocol
protocol PersonPresenter: class { }
// extend it where the conformer is a type of Person
extension PersonPresenter where Self: Person {
func displayName() {
print(name.uppercased())
}
}
// conform subclasses of Person to PersonPresenter
extension Hero: PersonPresenter { }
extension Pilot: PersonPresenter { }
extension Programmer: PersonPresenter { }
let myHero = Hero(name: "Hiro", ability: "Bend time & space")
myHero.displayName() // prints 'HIRO'
let myPilot = Pilot(name: "Pete", callSign: "Maverick")
myPilot.displayName() // prints 'PETE'
let myProgrammer = Programmer(name: "Chris", favoriteLanguage: "Swift")
myProgrammer.displayName() // prints 'CHRS'
let people: [Person] = [myHero,myPilot,myProgrammer]
if let presenter = people[0] as? PersonPresenter {
presenter.displayName() // Errror, PerseonPresenter is not a subtype of 'Person'
}
I would like to find a way to cast to PersonPresenter while satisfying the where constraint so as not to be forced to try and cast to each specific sub-class to access the default implementation of the protocol extension. Or to not have to conform the super class (which may be used in many other places) to the protocol.
The real problem is that you are extending an empty protocol.
By casting to Hero, the compiler knows it is a subclass of Person and then it satisfies the extension constraint.
But when casting to the protocol PersonPresenter itself, the compiler doesn't know if the constraint (being a Person) can be satisfied.
If you declare the requirement in the protocol, it will work:
protocol PersonPresenter: class {
func displayName()
}
Related
I have a protocol. This is implemented by many structs that fall into one of two types of category: TypeOne and TypeTwo. I want to be able to distinguish between their types, so I've added an enum ProtocolType that defines the types typeOne and typeTwo. By default I set the protocolType to be typeOne, but I manually specify typeTwo when it's a TypeTwo struct:
enum ProtocolType {
case typeOne
case typeTwo
}
protocol MyProtocol {
let name: String { get }
var protocolType: ProtocolType { get }
}
extension MyProtocol {
var protocolType: ProtocolType {
return .typeOne
}
}
enum TypeOne {
struct Foo: MyProtocol {
let name = "foo"
}
}
enum TypeTwo {
struct Bar: MyProtocol {
let name = "bar"
let protocolType = .typeTwo
}
}
Is there any way I can remove the necessity for defining protocolType in all structs and somehow use generics to identify what type a struct is? They're already defined under the TypeOne and TypeTwo convenience enums, I was wondering if I could utilise that some how?
Given some protocol:
protocol MyProtocol {
var name: String { get }
}
It sounds like you want to "tag" certain types as special, even though they have the same requirements. That's not an enum, that's just another type (protocol):
// No additional requirements
protocol SpecialVersionOfMyProtocol: MyProtocol {}
You can then tag these at the type level, not the value level:
struct Foo: MyProtocol {
let name = "foo"
}
struct Bar: SpecialVersionOfMyProtocol {
let name = "bar"
}
And you can tell the difference using is if you need to:
func f<T: MyProtocol>(x: T) {
if x is SpecialVersionOfMyProtocol {
print("special one")
}
}
In most cases, though, I wouldn't use this kind of runtime check. I'd just have two protocols (one for TypeOne and one for TypeTwo), and implement whatever you need as extensions on those. For example, say you want to print the name differently depending on the type. Start with a protocol that just expresses that:
protocol NamePrintable {
var name: String { get }
func printName()
}
func printIt<T: NamePrintable>(x: T) {
x.printName()
}
Then extend that for TypeOnes and TypeTwos:
protocol TypeOne: NamePrintable {}
extension TypeOne {
func printName() {
print("I'm a type one with the name \(name)")
}
}
protocol TypeTwo: NamePrintable {}
extension TypeTwo {
func printName() {
print("I'm a type two with the name \(name)")
}
}
And conform your structs:
struct Foo: TypeOne {
let name = "foo"
}
struct Bar: TypeTwo {
let name = "bar"
}
printIt(x: Foo()) // I'm a type one with the name foo
printIt(x: Bar()) // I'm a type two with the name bar
If you want a default implementation, you can hang it on NamePrintable, but I kind of recommend not doing that for what you've described. I'd probably just have "type one" and "type two" explicitly.
extension NamePrintable {
func printName() {
print("BASE FUNCTIONALITY")
}
}
How to deal with this problem?
Definitions:
protocol NameProtocol {
var rawValue: String { get }
}
struct CatName: NameProtocol {
enum CatNamesEnum: String {
case Tiger, Max, Sam
}
var literal: CatNamesEnum
var rawValue: String {
literal.rawValue
}
}
struct DogName: NameProtocol {
enum DogNamesEnum: String {
case Jack, Toby, Sadie
}
var literal: DogNamesEnum
var rawValue: String {
literal.rawValue
}
}
Definitions:
protocol AnimalProtocol {
associatedtype Name: NameProtocol
var name: Name { get }
func cry() -> String
}
class Cat: AnimalProtocol {
var name: CatName
func cry() -> String {
return "meow, I am \(name.rawValue)"
}
init(name: CatName) {
self.name = name
}
// some other cat actions...
}
class Dog: AnimalProtocol {
var name: DogName
func cry() -> String {
return "bark, I am \(name.rawValue)"
}
init(name: DogName) {
self.name = name
}
// some other dog actions...
}
The code above are some definition code structure, should not be modified.
And the functions below takes some problem:
Protocol with asccociatedtype cannot be the dictionary value type.
Function with Opaque Return Type cannot return some different types extends the same protocol.
// <1>
// Error: Protocol 'AnimalProtocol' can only be used as a generic constraint because it has Self or associated type requirements
let animals: [String: AnimalProtocol] = [
"cat": Cat(name: CatName(literal: .Sam)),
"dog": Dog(name: DogName(literal: .Jack))
// ...
// maybe a lot of animals
]
for (animal, entity) in animals {
print("\(animal): \(entity.cry())")
}
// <2>
// Error: Function declares an opaque return type, but the return statements in its body do not have matching underlying types
func animalCry(animal: String) -> some AnimalProtocol {
switch animal {
case "cat":
return Cat(name: CatName(literal: .Sam))
default:
return Dog(name: DogName(literal: .Toby))
}
}
Any solutions?
Or best practice of different types(with embed generic type) in a list.
You say that the definitions should not be modified but this is exactly what I have done here:
I removed the associated type from the AnimalProtocol protocol since it wasn't used elsewhere in any conforming types
protocol AnimalProtocol {
var name: NameProtocol { get }
func cry() -> String
}
Then I changed both Cat and Dog to conform to the protocol by changing the name declaration in both to
var name: NameProtocol
this resolves the issue with the dictionary and the function was fixed by changing the return type from some AnimalProtocol to AnimalProtocol
I have a protocol and a class which I want to extend. The protocol requires field of some type and the class has a field with the same name and the type as Implicitly Unwrapped Optional of this type.
Can this class be extended by this protocol? If yes, then how?
If I try to write an extension, Xcode give an error of not conforming. But if I add the field into the extension, it gives an error of redeclaration.
protocol Named {
var name: String { get }
}
class Person {
var name: String!
}
extension Person: Named {
// Type 'Finances.Account' does not conform to protocol 'Named'
}
Property names and types declared in a protocol must exactly be matched by the conforming classes.
So you cannot resolve the error without changing the property type in either the protocol or the conforming type. You could also rename one of the properties and add the matching property to the conforming type as a new field.
So either do:
protocol Named {
var name: String { get }
}
class Person {
var name: String
init(_ name:String) {
self.name = name
}
}
extension Person: Named {
}
Or
protocol Named {
var name: String { get }
}
class Person {
var _name: String!
}
extension Person: Named {
var name: String {
return _name
}
}
As #user28434 pointed out, there's a(n ugly) workaround. You can create a wrapper protocol that matches the optionality of the Person class, make that protocol inherit from the original protocol, declare the non-optional variable in an extension on the new protocol and make Person conform to the new protocol instead of the original Named.
protocol Named {
var name: String { get }
}
class Person {
var name: String!
}
protocol Namedd: Named {
var name: String! { get }
}
extension Namedd {
var name: String {
return name!
}
}
extension Person: Namedd {
}
https://www.raywenderlich.com/148448/introducing-protocol-oriented-programming
protocol Bird {
var name: String { get }
var canFly: Bool { get }
func doSomething()
}
protocol Flyable {
var airspeedVelocity: Double { get }
}
extension Bird {
// Flyable birds can fly!
var canFly: Bool { return self is Flyable }
func doSomething() {
print("default Bird: \(name)")
}
}
class FlappyBird: Bird, Flyable {
let name: String
let canFly = true
var airspeedVelocity: Double = 5.0
init(name: String) {
self.name = name
}
}
class Penguin: Bird {
let name: String
let canFly = false
init(name: String) {
self.name = name
}
}
class Owl<T> : Bird {
let name: String
let power: T
init(name: String, power: T) {
self.name = name
self.power = power
}
}
extension Bird where Self: FlappyBird {
func doSomething() {
print("FlappyBird: \(name)")
}
}
extension Bird where Self: Owl<String> {
func doSomething() {
print("Owl<String>: \(name)")
}
}
let fb = FlappyBird(name:"PAK")
let penguin = Penguin(name:"Mr. Pickle")
let nightOwl = Owl<String>(name:"Night Owl", power:"Who")
let dayOwl = Owl<Int>(name:"Day Owl", power: 50)
let birds: [Bird] = [fb, penguin, nightOwl, dayOwl]
birdloop: for bird in birds {
bird.doSomething()
}
The output I get is:
FlappyBird: PAK
default Bird: Mr. Pickle
default Bird: Night Owl
default Bird: Day Owl
The first result works as expected since
extension Bird where Self: FlappyBird {
func doSomething() {
print("FlappyBird: \(name)")
}
}
The second result works as expected since it calls the default protocol extension:
extension Bird {
// Flyable birds can fly!
var canFly: Bool { return self is Flyable }
func doSomething() {
print("default Bird: \(name)")
}
}
The third result I would expect to print
Owl<String>: Night Owl
since nightOwl is of type Owl<String>. But instead it calls the default protocol extension:
default Bird: Night Owl
Is there some reason why
extension Bird where Self: FlappyBird {
func doSomething() {
print("default Bird: \(name)")
}
}
is called for the FlappyBird type but
extension Bird where Self: Owl<String> {
func doSomething() {
print("Owl<String>: \(name)")
}
}
is not called for the Owl<String> type?
For the generic type Owl<T> you are allowed to have the constraint where Self: Owl<String>, but it will only work in contexts where the concrete type information is available.
To make it clear what is happening, consider this:
let nightOwl = Owl<String>(name: "Night Owl", power: "Who")
nightOwl.doSomething() // prints "Owl<String>: Night Owl"
As opposed to this:
let nightOwl: Bird = Owl<String>(name: "Night Owl", power: "Who")
nightOwl.doSomething() // prints "default Bird: Night Owl"
When Swift creates the protocol witness tables for the types Owl<T> and FlappyBird, it has to act differently for each one because Owl is generic. If it doesn't have the concrete type information, i.e. Owl<String> at the call site, it must use the default implementation for Owl<T>. This "loss" of type information is happening when you are inserting the owls into the array of type [Bird].
In the case of FlappyBird, since there is only one possible implementation (since it's not generic), the compiler produces a witness table with the "expected" method reference, which is print("FlappyBird: \(name)"). Since FlappyBird is not generic, its witness table doesn't need any reference to the unconstrained default implementation of doSomething() and can therefore correctly call the the constrained implementation even when the concrete type information is missing.
To make it clear that the compiler "needs" to have the fall back behavior for a generic type, you can remove the Bird conformance from Owl<T> and try to rely solely on the constrained default implementation. This will result in a compilation error with an error that is, as usual with Swift, highly misleading.
Value of type 'Owl' has no member 'doSomething'
Basically, it seems the witness table can't be built because it requires the existence of an implementation that will work for all types T on Owl.
References
https://forums.swift.org/t/swifts-method-dispatch/7228
https://developer.apple.com/videos/play/wwdc2016/416/
#AllenHumphreys answer here is a decent explanation on the why part.
As for the fix part, implement doSomething() for the generic Owl as:
class Owl<T> : Bird {
//...
func doSomething() {
print("Owl<\(type(of: power))>: \(name)")
}
}
and now you no longer need doSomething() in extension Bird where Self: Owl<String>
Assume a protocol defined below:
protocol Identifiable {
static var identifier: String { get }
}
extension Identifiable {
static var identifier: String { return "Default Id" }
}
What is the best way to reference the static variable? The example below illustrates two ways to access the variable. What is the difference, and is the type(of:) better?
func work<I: Identifiable>(on identifiable: I) {
let identifier: String = I.identifier
print("from Protocol: \(identifier)")
let identiferFromType: String = type(of: identifiable).identifier
print("using type(of:): \(identiferFromType)")
}
struct Thing: Identifiable {
static var identifier: String { return "Thing" }
}
work(on: Thing())
In the example you show, there is no difference. Because identifier is a protocol requirement, it will be dynamically dispatched to in both cases, therefore you don't need to worry about the wrong implementation being called.
However, one difference arises when you consider the value of self inside the static computed property when classes conform to your protocol.
self in a static method/computed property is the metatype value that it's is called on. Therefore when called on I, self will be I.self – which is the static type that the compiler infers the generic placeholder I to be. When called on type(of: identifiable), self will be the dynamic metatype value for the identifiable instance.
In order to illustrate this difference, consider the following example:
protocol Identifiable {
static var identifier: String { get }
}
extension Identifiable {
static var identifier: String { return "\(self)" }
}
func work<I : Identifiable>(on identifiable: I) {
let identifier = I.identifier
print("from Protocol: \(identifier)")
let identiferFromType = type(of: identifiable).identifier
print("using type(of:): \(identiferFromType)")
}
class C : Identifiable {}
class D : C {}
let d: C = D()
// 'I' inferred to be 'C', 'type(of: d)' is 'D.self'.
work(on: d)
// from Protocol: C
// using type(of:): D
In this case, "which is better" completely depends on the behaviour you want – static or dynamic.