Binary operator '!==' cannot be applied to two 'MyProtocol' - swift

I have created a protocol:
public protocol MyProtocol {
func doTask()
}
Then, I have an array for elements with MyProtocol type:
var taskList: [MyProtocol] = []
Callers can add elements to taskList, eventually, I got an non-empty taskList.
Now, I need to have a function that could remove an element from taskList, this is what I tried:
func removeTask(task: MyProtocol) {
// Compiler error: Binary operator '!==' cannot be applied to two 'MyProtocol'
taskList = taskList.filter{$0 !== task}
}
But I get compiler error: Binary operator '!==' cannot be applied to two 'MyProtocol'
How to get rid of this error?
=== UPDATE ===
Thanks #holex, after changed MyProtocol to be class-only, it works fine. But now I wonder if I need MyProtocol to be not class-only, what would be the solution then?

You are using !== which is comparing references. You can't compare protocol directly using this operator, since MyProtocol can become a class or struct. Since the !== can only compare instances, you must explicitly declare that your protocol is a class.
Please change the MyProtocol to the following, which should fix your problem:
protocol MyProtocol: class { // AnyObject can be used here as well
func doTask()
}
Trying to accomplish this without class or AnyObject will not work with your current design. You might want to implement another comparison method.
Also note that you can try to use != which might be able to do the exact same thing you want. Using this way you don't have to declare class or AnyObject. So check if that works for you.

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 extend protocol Optional, where Wrapped item is Array of Equatable generic elements?

I would say this problem is about proper declaration of extension.
I would like to extend Array filled with generic Elements, where Element conforms to Equatable. I've managed to do that by :
extension Array where Element: Equatable{
// my code
}
However I'd like to know how to properly declare extension when the Array filled with Equatable elements is inside an Optional? I know that in this case I am actually extending protocol Optional, but I can't figure out the rest
I was thinking something like:
extension Optional where Wrapped: Array & Equatable {
// my code
}
Can't figure it out.
Any ideas ?
Cristik provided a good solution. An alternative is to write an extension to an optional collection of equatable elements:
extension Optional where Wrapped: Collection, Wrapped.Element: Equatable {
func foo() { }
}
This would be applicable to arrays, array slices, and other collections.
Depending on what the extension does, you may want to use a combination of Collection, MutableCollection, BidirectionalCollection, RandomAccessCollection.
I don't think you can't specify this constraint at the extension level, however you should be able to specify it at the function level:
extension Optional {
func myFunc<T: Equatable>() where Wrapped == [T] {
// do your thing
}
}

Swift: Is it possible to add a protocol extension to a protocol?

Lets say I have two protocols:
protocol TheirPcol {}
protocol MyPcol {
func extraFunc()
}
What I want to do is to create a protocol extension for 'TheirPcol' which lets extraFunc() work on anything which conforms to 'TheirPcol'. So something like this:
extension TheirPcol : MyPcol { // Error 'Extension of protocol 'TheirPcol' cannot have an inheritance clause.
func extraFunc() { /* do magic */}
}
struct TheirStruct:TheirPcol {}
let inst = TheirStruct()
inst.extraFunc()
The kicker in this is that 'TheirPcol', 'TheirStruct' are all handled by an external API which I do not control. So I'm passed the instance 'inst'.
Can this be done? Or am I going to have to do something like this:
struct TheirStruct:TheirPcol {}
let inst = TheirStruct() as! MyPcol
inst.extraFunc()
It seems there are two use-cases of why you may want to do what you are doing. In the first use-case, Swift will allow you to do what you want, but not very cleanly in the second use-case. I'm guessing you fall into the second category, but I'll go through both.
Extending the functionality of TheirPcol
One reason why you might want to do this is simply to give extra functionality to TheirPcol. Just like the compiler error says, you cannot extend Swift protocols to conform to other protocols. However, you can simply extend TheirPcol.
extension TheirPcol {
func extraFunc() { /* do magic */ }
}
Here, you are giving all objects that conform to TheirPcol the method extraFunc() and giving it a default implementation. This accomplishes the task of extending functionality for the objects conforming to TheirPcol, and if you want it to apply to your own objects as well then you could conform your objects to TheirPcol. In many situations, however, you want to keep MyPcol as your primary protocol and just treat TheirPcol as conforming to MyPcol. Unfortunately, Swift does not currently support protocol extensions declaring conformance to other protocols.
Using TheirPcol objects as if they were MyPcol
In the use case (most likely your use case) where you really do need the separate existence of MyPcol, then as far as I am aware there is no clean way to do what you want yet. Here's a few working but non-ideal solutions:
Wrapper around TheirPcol
One potentially messy approach would be to have a struct or class like the following:
struct TheirPcolWrapper<T: TheirPcol>: MyPcol {
var object: T
func extraFunc() { /* Do magic using object */ }
}
You could theoretically use this struct as an alternative to casting, as in your example, when you need to make an existing object instance conform to MyPcol. Or, if you have functions that accept MyPcol as a generic parameter, you could create equivalent functions that take in TheirPcol, then convert it to TheirPcolWrapper and send it off to the other function taking in MyPcol.
Another thing to note is if you are being passed an object of TheirPcol, then you won't be able to create a TheirPcolWrapper instance without first casting it down to an explicit type. This is due to some generics limitations of Swift. So, an object like this could be an alternative:
struct TheirPcolWrapper: MyPcol {
var object: MyPcol
func extraFunc() { /* Do magic using object */ }
}
This would mean you could create a TheirPcolWrapper instance without knowing the explicit type of the TheirPcol you are given.
For a large project, though, both of these could get messy really fast.
Extending individual objects using a child protocol
Yet another non-ideal solution is to extend each object that you know conforms to TheirPcol and that you know you wish to support. For example, suppose you know that ObjectA and ObjectB conform to TheirPcol. You could create a child protocol of MyPcol and then explicitly declare conformance for both objects, as below:
protocol BridgedToMyPcol: TheirPcol, MyPcol {}
extension BridgedToMyPcol {
func extraFunc() {
// Do magic here, given that the object is guaranteed to conform to TheirPcol
}
}
extension ObjectA: BridgedToMyPcol {}
extension ObjectB: BridgedToMyPcol {}
Unfortunately, this approach breaks down if there are a large number of objects that you wish to support, or if you cannot know ahead of time what the objects will be. It also becomes a problem when you don't know the explicit type of a TheirPcol you are given, although you can use type(of:) to get a metatype.
A note about Swift 4
You should check out Conditional conformances, a proposal accepted for inclusion in Swift 4. Specifically, this proposal outlines the ability to have the following extension:
extension Array: Equatable where Element: Equatable {
static func ==(lhs: Array<Element>, rhs: Array<Element>) -> Bool { ... }
}
While this is not quite what you are asking, at the bottom you'll find "Alternatives considered", which has a sub-section called "Extending protocols to conform to protocols", which is much more what you're trying to do. It provides the following example:
extension Collection: Equatable where Iterator.Element: Equatable {
static func ==(lhs: Self, rhs: Self) -> Bool {
// ...
}
}
Then states the following:
This protocol extension would make any Collection of Equatable elements Equatable, which is a powerful feature that could be put to good use. Introducing conditional conformances for protocol extensions would exacerbate the problem of overlapping conformances, because it would be unreasonable to say that the existence of the above protocol extension means that no type that conforms to Collection could declare its own conformance to Equatable, conditional or otherwise.
While I realize you're not asking for the ability to have conditional conformances, this is the closest thing I could find regarding discussion of protocols being extended to conform to other protocols.

Errors when working with protocols as if they were classes

I have a protocol like this:
protocol Moveable {
var moveSpeed: Float { get set }
}
And in one of my classes that don't conform to "Moveable" I have a function like this:
var moveables: [Moveables]()
func checkMoveable(mov: Moveable) -> Bool {
for m in moveables {
if m === mov { // <- Error
return true
}
}
return false
}
I get this error:
Binary operator '===' cannot be applied to two 'Moveable' operands
also tried with "==" instead of "==="
And in another function that relies on the previous one:
func removeMoveable(mov: Moveable) {
if checkMoveable(mov) {
self.moveables = moveables.filter({$0 !== mov})// <- Error
}
}
I get this error:
Cannot convert value of type 'Moveable' to expected argument type
'AnyObject?'
If "Moveable" was a class instead of being a protocol none of these errors would appear.
The identity operator === applies only to reference types (classes). Your protocol can be adopted by either reference or value types, so something whose only type information is that protocol can't be used with the identity operator.
If you want your protocol to be adopted only by classes, declare it thusly:
protocol Movable: class { //...
Or make it extend a protocol that's already restricted to classes:
protocol Movable: NSObjectProtocol { //...
The equivalence operator == applies only to types that are Equatable. If you want to use that operator on two Movables, you'll need to declare that for a type to be Movable it must also conform to Equatable:
protocol Movable: Equatable { //...
Since it seems you're looking for a particular SKSpriteNode instance, the first (making === work) is probably what you want.

Swift Extensions for Collections

I'm working on a framework to make it easier to work with Key Value Observing and I've defined a protocol for converting native Swift types to NSObject as follows:
public protocol NSObjectConvertible {
func toNSObject () -> NSObject
}
Extending the builtin types was easy, simply defining the function to convert the given type to the appropriate NSObject:
extension Int8: NSObjectConvertible {
public func toNSObject () -> NSObject {
return NSNumber(char: self)
}
}
When I got to the Array type, I hit a number of snags, which I tried to work out. I didn't want to extend any array type, but only arrays whose element type was itself NSObjectConvertible. And naturally, needed Array to itself conform to the protocol.
After hunting around on SO, it looks like extending the Array type itself is a little harder because it's generic, but extending SequenceType can be done. Except that I can't both constrain the element type and declare its conformance to the protocol in the same declaration.
The following:
extension SequenceType where Generator.Element == NSObjectConvertible : NSObjectConvertible = {
public func toNSObject () -> NSObject {
return self.map() { return $0.toNSObject() }
}
}
Produces a compiler error:
Expected '{' in extension
And the carat points to the ":" where I'm trying to declare the protocol conformance. Removing the protocol conformance compiles without errors, but obviously doesn't help the case.
I'm not sure if this is a bug, or if Swift simply can't (or doesn't want to) support what I'm trying to do. Even if I simply define the extension, then try to take care of the conformance in the body, it produces the risk of passing sequences that don't really conform to what they should.
At best it's a hacky solution to just fail in cases where a sequence with non-conforming members are passed. I'd much rather let the compiler prevent it from happening.
(This is in Swift 2.1, Xcode 7.1.1)
You can't add the protocol conformance, unfortunately.