Reputation: 988
Having a problem with a protocol & generics that I am just not able to quite get a handle on.
In the code below, marked by ERROR HERE comment, I am getting the following error:
Member 'protocolMethod' cannot be used on value of type 'any Protocol1'; consider using a generic constraint instead
I assume it is complaining mainly because of the type of the item
parameter is at least in some way unresolved? And I am unfortunately not finding the suggestion helpful as I don't see how a generic constraint is going to help here (at least to my understanding of them).
Honestly I kind of feel like I might just be asking too much of Swift here.
Anybody perhaps see what the problem is or have suggestions for trying to resolve this?
Added 12/26/22 - As further background, what caused the error to appear was adding the item
param to the protocolMethod
method on the protocol, which pretty much shows it is the core of the issue.
protocol Protocol1
{
associatedtype DataItem
func protocolMethod(item : DataItem)
}
protocol Protocol2 {
associatedtype AType1: Hashable
//...
}
class Class1<Type1: Protocol2>: NSObject
{
typealias Item = Type1.AType1
var delegate : (any Protocol1)?
private func method1(item: Item)
{
delegate?.protocolMethod(item : item) //ERROR HERE
}
}
(using latest Xcode)
Upvotes: 16
Views: 9509
Reputation: 337
If the error didn't happen, you could set any var delegate: (any Protocol1)?
even though you specify the type of Item
on Class1
initialization.
This means you could initialize Class1
with some conformant to Protocol2
:
struct IntProtocol2Conformant: Protocol2 {
typealias AType1 = Int
}
let obj = Class1<MyProtocol2Type>()
Note, that obj.Item
is Int
.
After, you could swap your delegate
to any Protocol1
conformant, not just Int
:
struct StringProtocol1Conformant: Protocol1 {
typealias DataItem = String
protocolMethod(item : DataItem)
}
obj.delegate = StringProtocol2Conformant()
So now your Item
is still Int
and you could call
obj.method1(item: 42)
Of course it would lead to another error because your delegate
is supposed to get a String
now.
You're allowing delegate
to be any Protocol1
while Item
is set to Type1.AType1
on initialization, there's no connection between it and a delegate
you set after.
Strictly specifying that Delegate.DataItem == Type1.AType1
on initialization. Now you can only use a delegate
that has the same DataItem
as your Type1.AType1
:
class Class1<Type1: Protocol2, Delegate: Protocol1>: NSObject where Delegate.DataItem == Type1.AType1 {
typealias Item = Type1.AType1
var delegate: Delegate?
private func method1(item: Item) {
delegate?.protocolMethod(item: item)
}
}
Upvotes: 0
Reputation: 2661
You get this error because when you write:
var delegate : (any Protocol1)?
You don't give the compiler any information about what DataItem
is. It can be anything, and it should not matter in your Class1
implementation.
But when you write:
private func method1(item: Item)
{
delegate?.protocolMethod(item : item) //ERROR HERE
}
You are trying to pass an object of type Type1.AType1
as an argument of a method that expects a DataItem
, and since delegate can be any Protocol1
you have no guarantee that they are the same.
The error message actually proposes a solution:
Member 'protocolMethod' cannot be used on value of type 'any Protocol1'; consider using a generic constraint instead
You could add another type parameter to Class1
and add a generic constraint to tell the compiler that the AType1
and DataItem
need to be equal:
class Class1<Type1: Protocol1, Type2: Protocol2>: NSObject where Type1.DataItem == Type2.AType1
{
typealias Item = Type2.AType1
var delegate : Type1?
private func method1(item: Item)
{
delegate?.protocolMethod(item : item)
}
}
Upvotes: 11