Class does not conform to protocol, but struct does - swift

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.

Related

Protocol Extension where Self is ClassA OR ClassB

I have a protocol for which I want to provide default functionality in the case that the conforming class is either ClassA OR ClassB. Is it possible to use || in a protocol extension where clause? I've tried the following but it doesn't work:
extension Protocol where Self: ClassA || Self: ClassB {
func method() {
// implementation if conforming class is ClassA OR ClassB
}
}
No, you cannot use || (or something equivalent) in the where clause of a protocol extension.
If the extension method needs certain functionality which is present in both ClassA and ClassB then you can define that in a protocol (to which both classes conform), and constrain the extension to that protocol.
It's simply not possible at the moment. One solution could be creating two different extensions.
extension Protocol where Self: ClassA {
func method() {
commonMethod()
}
}
extension Protocol where Self: ClassB {
func method() {
commonMethod()
}
}
private func commonMethod() {
// do here your stuff
}
Or creating a common protocol, assuming that ClassA and ClassB conform to this one.
protocol CommonProtocol {}
class ClassA: Protocol, CommonProtocol {}
class ClassB: Protocol, CommonProtocol {}
extension Protocol where Self: CommonProtocol {
func method() {
// your stuff
}
}

Use protocol that inherits from another protocol as an associated type

I am trying to make a protocol that has has two associated types. One of these associated types is for a delegate. When I try to use another protocol as the associated type, I get the error "Type 'HostConnectionService' does not conform to protocol 'ConnectionService'". The code I have is written below:
protocol ConnectionService: class {
associatedtype Peer: Sharelist.Peer
associatedtype Delegate: ConnectionServiceDelegate
var connectedPeers: [Peer] { get }
var delegate: Delegate? { get }
}
protocol ConnectionServiceDelegate { }
// Error
class HostConnectionService: NSObject, ConnectionService {
typealias Peer = GuestPeer
typealias Delegate = HostConnectionServiceDelegate
var delegate: HostConnectionServiceDelegate?
var connectedPeers: [GuestPeer] = []
}
protocol HostConnectionServiceDelegate: ConnectionServiceDelegate { }
When I change the line
typealias Delegate = HostConnectionServiceDelegate
to be of a non-protocol type, I no longer get an error:
struct NonProtocolConnectionServiceDelegate: ConnectionServiceDelegate { }
// No Error
class HostConnectionSertice: NSObject, ConnectionService {
...
typealias Delegate = NonProtocolConnectionServiceDelegate
...
}
Is this a fundamental Swift limitation, or am I doing something wrong?
Your example is too complicated to understand. I tried to simplify it.
It compiles without errors:
protocol ProtocolA {}
protocol ProtocolB {
associatedtype SomeType
}
class SomeClass: ProtocolB {
typealias SomeType = ProtocolA
}
let object = SomeClass()
But the following example is no longer compiled:
protocol ProtocolA {}
protocol ProtocolB {
associatedtype SomeType: ProtocolA
}
class SomeClass: ProtocolB {
typealias SomeType = ProtocolA
}
The error is as follows:
error: type 'SomeClass' does not conform to protocol 'ProtocolB'
note: possibly intended match 'SomeType' (aka 'ProtocolA') does not conform to 'ProtocolA'
This is because protocols don't conform to themselves
Most likely in your case it is necessary to make the class template:
protocol ProtocolA {}
protocol ProtocolB {
associatedtype SomeType: ProtocolA
}
class SomeClass<T: ProtocolA>: ProtocolB {
typealias SomeType = T
}
extension Int: ProtocolA {}
extension Double: ProtocolA {}
let object1 = SomeClass<Int>()
let object2 = SomeClass<Double>()

Swift Array of Class Types that Each Conform to a Protocol and are Subclasses of Common Class

I have a method in Swift with a signature of the form:
func myMethod<T>(class: T.Type) where T: SomeClass & MyProtocol
I would like to have a variable that is an array of classes that are all subclasses of SomeClass and conform to MyProtocol.
It would look something like:
let classArray = [SubclassOfSomeClass.self, SubclassTwoOfSomeClass.self, SubclassThreeOfSomeClass.self]
Where each of SubclassOfSomeClass, SubclassTwoOfSomeClass and SubclassThreeOfSomeClass conform to MyProtocol
I would then like to call myMethod as follows:
classArray.forEach { classType in
myMethod(class: classType)
}
I receive the compiler error "Generic parameter T could not be inferred". This makes sense to me as the type of classArray variable is inferred as [MyClass.Type].
My question is, can and how do I define the variable classArray such that the compiler knows that every type defined therein conforms to the protocol MyProtocol.
You can achieve that by having SomeClass conform to MyProtocol. Therefore every subclass would also have to conform to MyProtocol.
Then your method signature would be just:
func myMethod<T>(class: T.Type) where T: SomeClass
If that is not desired then you can change the type of myMethod to not be generic. I think swift is unable to type the array to have elements of (SomeClass & MyProtocol).Type. In your case you are not really using a generic object. You do have a specific type in mind and it is (SomeClass & MyProtocol).Type. Your code would look something like this:
protocol MyProtocol: class {
}
class SomeClass: NSObject {
}
class SubClass: SomeClass, MyProtocol {
}
class SubClass2: SomeClass, MyProtocol {
}
let classArray: [(SomeClass & MyProtocol).Type] = [SubClass.self, SubClass2.self]
func myMethod(class: (SomeClass & MyProtocol).Type) {
}
classArray.forEach {
myMethod(class: $0)
}
protocol MyProtocol {
var x: Int { get }
}
class MyClass: NSObject {
var y: Int = 10
}
class SubClass1: MyClass, MyProtocol {
private(set) var x: Int = 9
}
class SubClass2: MyClass {
}
class SubClass3: MyClass, MyProtocol {
var x: Int { return 15 }
}
let items: [MyClass & MyProtocol] = [SubClass1(), SubClass3()]
func myMethod(_ item: MyClass & MyProtocol) {}
classArray.forEach { myMethod($0) }
So for now you can use just MyClass & MyProtocol

Can I declare a type that is a specific class conforming to a protocol in Swift?

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.

Swift Properties that conforms to a protocl

I have this protocol:
protocol TestProtocol {
func doSomething
}
I would like to use this protocol to ensure some properties are conforming to it like:
class MyClass {
var detailVC : UIViewController <TestProtocol>
}
like good old ObjC to ensure the detailVC conforms to TestProtocol
protocol MyViewControllerProtocol {
func protoFunc()
}
class MyClass {
var prop: MyViewControllerProtocol?
}
It's as simple as that. But if you want a pre-defined class to conform to a protocol, you then need to make an extension (but then this applies to the class as a whole) or you subclass it.
So...
As an extension to the class as a whole:
extension UIViewController: MyProtocol {
func protoFunc() {
print("do whatever")
}
}
In this case, when extended, you can just set the property as:
var myProperty: UIViewController?
As after being extended, it'll conform as required.
Or just subclass it with:
class MyConformingViewController: UIViewController, MyProtocol {
override func protoFunc() {
print("do whatever")
}
}
In this case, you just set the property as:
var myProp: MyConformingViewController?
And that'll automatically confirm to MyProtocol due to the class being set to conform to it.
You can't force a predesignated class to conform to a protocol which wasn't already designated to conform to it in the first place.
e.g. UIViewController wasn't originally set to confirm to MyOtherProtocol for example
That would defeat the object of protocols in the first place. This is why you either extend it to conform, or subclass it to conform.
So you can implement the method like following:
class detailVC : UIViewController, TestProtocol {
func doSomething() {}
}
In Swift you can't have a variable of one type and also declared as a protocol type.
What you can have is a variable that needs conform more than one protocol.
class MyClass {
var detailVC : TestProtocol
}
class MyClass {
var detailVC : protocol<TestProtocol,SecondProtocol>
}