Do not understand "Member '<func>' cannot be used on value of type 'any <type>'; consider using a generic constraint instead" error - swift

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)

You get this error because when you write:
var delegate : (any Protocol1)?
You don't give the compiler 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)
}
}

Related

Create a Swift extension with a where clause that filters on a struct that takes a generic

I'm trying to create an extension on Set that uses a where clause so that it only works on a struct I have that accepts a generic. But I keep running into errors about it the extension wanting the generic to be defined in the struct
In this example I'm getting the following error, with the compiler hint suggesting I use <Any>: Reference to generic type 'Test' requires arguments in <...>
struct Test<T> {
var value : T
func printIt() {
print("value")
}
}
extension Set where Element == Test {
}
However, when I use <Any> in the struct, I'm getting this error: Same-type constraint type 'Test' does not conform to required protocol 'Equatable'
extension Set where Element == Test<Any> {
}
Any suggestions on how to get the where clause to accept the Test struct for any type I'm using in the generic?
Thanks for your help
This is a limitation of Swift's type system. There's no way to talk about generic types without concrete type parameters, even when those type parameters are unrelated to the use of the type. For this particular situation (an extension for all possible type parameters), I don't believe there's any deep problem stopping this. It's a simpler version of parameterized extensions, which is a desired feature. It's just not supported (though there is an implementation in progress).
The standard way to address this today is with a protocol.
First, a little cleanup that's not related to your question (and you probably already know). Your example requires Test to be Hashable:
struct Test<T: Hashable>: Hashable {
var value : T
func printIt() {
print("value")
}
}
Make a protocol that requires whatever pieces you want for the extension, and make Test conform:
protocol PrintItable {
func printIt()
}
extension Test: PrintItable {}
And then use the protocol rather than the type:
extension Set where Element: PrintItable {
func printAll() {
for item in self { item.printIt() }
}
}
let s: Set<Test<Int>> = [Test(value: 1)]
s.printAll() // value
Just one more note on the error messages you're getting. The first error, asking you to add Any is really just complaining that Swift can't talk about unparameterized generics, and suggesting it's fallback type when it doesn't know what type to suggests: Any.
But Set<Any> isn't "any kind of Set." It's a Set where Element == Any. So Any has to be Hashable, which isn't possible. And a Set<Int> isn't a subtype of Set<Any>. There' completely different types. So the errors are a little confusing and take you down an unhelpful road.
This is not possible. The where clause requires a specific data type and simply passing a Test will not work unless I specify something more concrete like Test<String>.
Thank you to Joakim and flanker for answering the question in the comments
If you want to add extension for Set with where clause your Test must confirm to Hashable protocol.
Your Struct must look like this.
struct Test<T: Hashable> : Hashable {
var value : T
func printIt() {
print("value")
}
func hash(into hasher: inout Hasher) {
hasher.combine(value.hashValue)
}
}
So you can't use Any for your extension you must specify type that confirm to Hashable protocol.

How to solve the specification problem for Swift Protocol

protocol Specification {
associatedtype T
func isSatisfied(item : T) -> Bool
}
protocol AndSpecification {
associatedtype T
var arrayOfSpecs : [Specification] {set}
}
The aforementioned code gives the following error
Protocol 'Specification' can only be used as a generic constraint
because it has Self or associated type requirements
I understand that there is this problem because of associated types, which can be solved if I add a where clause. But I am at loss to understand where and how should I use this where clause
You think you need a where clause because you want the array to contain Specifications all with the same T, right? Well, protocols with associated types can't do that either!
What you can do is to have the array contain specifications all of the same type:
protocol AndSpecification {
associatedtype SpecificationType : Specification
var arrayOfSpecs : [SpecificationType] { get }
}
If you really like your T associated type, you can add one nonetheless:
protocol AndSpecification {
associatedtype T
associatedtype SpecificationType : Specification where SpecificationType.T == T
var arrayOfSpecs : [SpecificationType] { get }
}
But this is quite redundant, because you could just say AndSpecification.SpecificationType.T instead.

Difference between extension and direct call for protocol

I got this code:
protocol Protocol {
var id: Int { get }
}
extension Array where Element: Protocol {
func contains(_protocol: Protocol) -> Bool {
return contains(where: { $0.id == _protocol.id })
}
}
class Class {
func method<T: Protocol>(_protocol: T) {
var arr = [Protocol]()
// Does compile
let contains = arr.contains(where: { $0.id == _protocol.id })
// Doens't compile
arr.contains(_protocol: _protocol)
}
}
Why doesn't the line of code compile where I commented 'Doens't compile'? This is the error:
Incorrect argument label in call (have '_protocol:', expected 'where:')
When I change the method name in the extension to something else, like containz (and ofcourse change the name of the method that calls it to containz), I get this error when I try to call it:
Using 'Protocol' as a concrete type conforming to protocol 'Protocol' is not supported
But why doesn't it work when I try to call it through an extension, but it does work when I create the function in the extension directly? There isn't really any difference that I can see.
I agree with matt that the underlying answer is Protocol doesn't conform to itself?, but it's probably worth answering anyway, since in this case the answer is very simple. First, read the linked question about why [Protocol] doesn't work the way you think it does (especially Hamish's answer, which is much more extensive than the accepted answer that I wrote). [Protocol] does not match the where Element: Protocol clause because Protocol is not a concrete type that conforms to Protocol (because it's not a concrete type).
But you don't need [Protocol] here. You have T: Protocol, so you can (and should) just use that:
var arr = [T]()
With that change, the rest should work as you expect because T is a concrete type that conforms to Protocol.

In argument type '[ItemA]', 'ItemA' does not conform to expected type 'Sortable'

I have run into a weird error using Swift, but I can't seem to find the issue. The error should not be thrown I think, and I have verified this issue with the code below in a playground.
protocol Sortable {
}
protocol ItemA: Sortable {
}
func sortItems<T: Sortable>(items: [T]) -> [T] {
// do the sorting here
return items
}
let list: [ItemA] = []
sortItems(items: list)
You cannot pass another protocol that inherits from the constrained protocol in current Swift version (4.1).
If you make ItemA a struct, a class or an enum, it will work.
OR
If you would change your sortItems implementation to simply take Sortable as an argument like this, then you can use another protocol that inherits from Sortable, but you will lose the information about the type.
func sortItems(items: [Sortable]) -> [Sortable] {
// do the sorting here
return items
}
You can find more information on this issue here.

Statically typed properties in Swift protocols

I'm trying to use Protocol-Oriented Pgrogramming for model layer in my application.
I've started with defining two protocols:
protocol ParseConvertible {
func toParseObject() -> PFObject?
}
protocol HealthKitInitializable {
init?(sample: HKSample)
}
And after implementing first model which conforms to both I've noticed that another model will be basically similar so I wanted to create protocol inheritance with new one:
protocol BasicModel: HealthKitInitializable, ParseConvertible {
var value: AnyObject { get set }
}
A you can see this protocol has one additional thing which is value but I want this value to be type independent... Right now I have models which use Double but who knows what may show up in future. If I leave this with AnyObject I'm sentenced to casting everything I want to use it and if I declare it as Double there's no sense in calling this BasicModel but rather BasicDoubleModel or similar.
Do you have some hints how to achieve this? Or maybe I'm trying to solve this the wrong way?
You probably want to define a protocol with an "associated type",
this is roughly similar to generic types.
From "Associated Types" in the Swift book:
When defining a protocol, it is sometimes useful to declare one or
more associated types as part of the protocol’s definition. An
associated type gives a placeholder name (or alias) to a type that is
used as part of the protocol. The actual type to use for that
associated type is not specified until the protocol is adopted.
Associated types are specified with the typealias keyword.
In your case:
protocol BasicModel: HealthKitInitializable, ParseConvertible {
typealias ValueType
var value: ValueType { get set }
}
Then classes with different types for the value property can
conform to the protocol:
class A : BasicModel {
var value : Int
func toParseObject() -> PFObject? { ... }
required init?(sample: HKSample) { ... }
}
class B : BasicModel {
var value : Double
func toParseObject() -> PFObject? { ... }
required init?(sample: HKSample) { ... }
}
For Swift 2.2/Xcode 7.3 and later, replace typealias in the
protocol definition by associatedtype.