Swift - Struct with equatable variable - swift

I'm trying to implement a struct which is Equatable and has a variable (in this example 'variable2') of type AnyObject that might or not be equatable.
struct MyStruct : Equatable{
var variable1 : String;
var variable2 : AnyObject;
}
func == (obj1 : MyStruct, obj2 : MyStruct) -> Bool {
if(obj1.variable2.conformsToProtocol(Equatable) && obj2.variable2.conformsToProtocol(Equatable)) {
//...
} else {
//...
}
return ...
}
At first I was trying to check if variable2 conforms to protocol Equatable, but doing so i get a compile error.
On a different approach I tried to change 'variable2' to Equatablebut even so I still have an error telling me it can only be used as a generic constraint.
struct MyStruct : Equatable{
var variable1 : String;
var variable2 : Equatable;
}
func == (obj1 : MyStruct, obj2 : MyStruct) -> Bool {
return obj1.variable2 == obj2.variable2;
}
I tried some different ways, but didn't manage to get it to work. Does anyone has a solution for this?Solving the first case would be the best situation, but the second might satisfy my needs as well.

Why not use the "is" operator:
if obj1.variable2 is Equatable && obj2.variable2 is Equatable {
...
}

(a) AnyObject doesn't conforms to Equatable
(b) If you downcast some class which conforms to Equatable to AnyObject,
the result is NOT equatable!!
class C: Equatable {
var i: Int
init(value: Int){
i = value
}
}
func ==(lhs: C, rhs: C)->Bool {
return 2 * lhs.i == rhs.i
}
let c1 = C(value: 1)
let c2 = C(value: 2)
c1 == c2 // true
let a1 = c1 as AnyObject
let a2 = c2 as AnyObject
// or if you want
// let a1: AnyObject = c1
// let a2: AnyObject = c2
// a1 == a2 // error!!!
a1 as? C == a2 as? C // true
In other words, you are not able to compare two AnyObject. You can check if two references to AnyObject are the same (representing the same instance). But that has nothing with Equatable protocol.

Related

Heterogeneous array that conforms to protocol with associated type in Swift

I have a protocol AProtocol with an associated type AType and a function aFunc. I want to extend Array such that it conforms to the protocol by using the result of its elements aFunc function. Clearly this is only possible if elements of the array conform to Aprotocol and have the same associated type so I have set this toy example:
protocol AProtocol {
associatedtype AType
func aFunc(parameter:AType) -> Bool
}
extension Array : AProtocol where Element : AProtocol, Element.AType == Int {
func aFunc(parameter: Int) -> Bool {
return self.reduce(true, { r,e in r || e.aFunc(parameter: parameter) })
}
}
extension String : AProtocol {
func aFunc(parameter: Int) -> Bool {
return true
}
}
extension Int : AProtocol {
func aFunc(parameter: Int) -> Bool {
return false
}
}
This works fine for arrays which contain only one type:
let array1 = [1,2,4]
array1.aFunc(parameter: 3)
However for heterogeneous arrays, I get the error Heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional and then Value of type '[Any]' has no member 'aFunc' if annotate it as follows:
let array2 = [1,2,"Hi"] as [Any]
array2.aFunc(parameter: 3)
Is it possible to extend Array as I wish such that heterogeneous arrays are allowed so long as they conform to AProtocol and have the same AType?
See if this fits your needs.
Approach:
Remove the associated type
Implementation:
protocol BProtocol {
func aFunc(parameter: BProtocol) -> Bool
}
extension String : BProtocol {
func aFunc(parameter: BProtocol) -> Bool {
return true
}
}
extension Int : BProtocol {
func aFunc(parameter: BProtocol) -> Bool {
return false
}
}
extension Array : BProtocol where Element == BProtocol {
func aFunc(parameter: BProtocol) -> Bool {
return self.reduce(true, { r,e in r || e.aFunc(parameter: parameter) })
}
}
Invoking:
let a1 : [BProtocol] = [1, 2, 3, "Hi"]
let boolean = a1.aFunc(parameter: 1)

Class referencing codable protocols is not codable

If a member of my class AnotherClass is a of a type with codable abstract protocol the compiler fails to synthesize the coding/decoding code for the class. If instead the same member is a concrete class conforming to the same protocol then the compiler will happily synthesize the coding/decoding code. I think the second case should work, the property mp will always be a Codable concrete instance of MyProtocol, which is Codable.
/* This works */
protocol MyProtocol : Codable {
func a(_ n : Int) -> Int
}
class MyClass : MyProtocol {
let x = 3
func a( _ n : Int ) -> Int {
return n * x
}
}
class AnotherClass : Codable {
let y = 5
let mp : MyClass // <---- ACTUAL CLASS
init() {
mp = MyClass()
}
}
/* But this won't work.
Compiler error:
Type 'AnotherClass' does not conform to protocol 'Decodable'
Type 'AnotherClass' does not conform to protocol 'Encodable'
*/
protocol MyProtocol : Codable {
func a(_ n : Int) -> Int
}
class MyClass : MyProtocol {
let x = 3
func a( _ n : Int ) -> Int {
return n * x
}
}
class AnotherClass : Codable {
let y = 5
let mp : MyProtocol // <-------- PROTOCOL
init() {
mp = MyClass()
}
}
This is how you fix it.
class AnotherClass<T: MyProtocol> : Codable {
let y = 5
let mp : T
init() {
mp = MyClass() as! T // handle this more gracefully
}
}
And use it this way
let a = AnotherClass<MyClass>()
But please consider reading this answer here. It explains a lot about why protocols behave the way they do and will help you understand more about them.

Swift protocol extension where Self: Equatable doesn't work

Can anyone shed a light as to why this doesn't work? I get an error Binary operator '==' cannot be applied to operands of type 'Self' and 'CustomEquatable'
protocol CustomEquatable {
func isEqualTo(_ other: CustomEquatable) -> Bool
}
extension CustomEquatable where Self: Equatable {
func isEqualTo(_ other: CustomEquatable) -> Bool {
return self == other
}
}
Let's start with your CustomEquatable protocol, without the extension:
protocol CustomEquatable {
func isEqualTo(_ other: CustomEquatable) -> Bool
}
Let's define some types to use for experiments:
struct A: Equatable {
let name: String
}
struct B: Equatable {
let id: Int
}
Suppose we then want A and B to conform to CustomEquatable. Then we have four cases to consider:
What does a1.isEqualTo(a2) mean (where a1 and a2 are both of type A)?
What does b1.isEqualTo(b2) mean (where b1 and b2 are both of type B)?
What does a.isEqualTo(b) mean (where a is an A and b is a B)?
What does b.isEqualTo(a) mean (where b is a B and a is an A)?
For the first two cases, possible answers are that a1.isEqualTo(a2) if and only if a1 == a2 and b1.isEqualTo(b2) if and only if b1 == b2.
For the second two cases, we have to decide if there's a way for an A to equal a B. The simplest solution (I think) is that an A can never equal a B.
So we can write the conformances like this:
extension A: CustomEquatable {
func isEqualTo(_ other: CustomEquatable) -> Bool {
return (other as? A) == self
}
}
extension B: CustomEquatable {
func isEqualTo(_ other: CustomEquatable) -> Bool {
return (other as? B) == self
}
}
The only difference in these two conformances is the cast-to type (on the right side of as?). So we can factor out the conformances into a protocol extension like this:
extension CustomEquatable where Self: Equatable {
func isEqualTo(_ other: CustomEquatable) -> Bool {
return (other as? Self) == self
}
}
With this protocol extension, we can make A and B conform to CustomEquatable without implementing isEqualTo for each:
extension A: CustomEquatable { }
extension B: CustomEquatable { }
To test the code:
let a1 = A(name: "a1")
let a2 = A(name: "a2")
let b1 = B(id: 1)
let b2 = B(id: 2)
a1.isEqualTo(a1) // true
a1.isEqualTo(a2) // false
b1.isEqualTo(b1) // true
b1.isEqualTo(b2) // false
a1.isEqualTo(b1) // false
b1.isEqualTo(a1) // false
Please watch WWDC 2015 Protocol-Oriented Programming in Swift from 37:25
This is almost literally taken from the video. You have to conditional downcast other to Self.
If it's the same type you can use == otherwise both objects are not equal anyway.
protocol CustomEquatable {
func isEqualTo(_ other: CustomEquatable) -> Bool
}
extension CustomEquatable where Self: Equatable {
func isEqualTo(_ other: CustomEquatable) -> Bool {
if let other = other as? Self { return self == other }
return false
}
}

Compare 2 structs/Objects implement the same Protocol?

let's say I have the following :
protocol P : Equatable {
var uniqueID : Int { get }
}
struct A : P {
var uniqueID = 1
}
struct B : P {
var uniqueID = 2
}
func ==<T : P>(lhs:T , rhs:T) -> Bool { return lhs.uniqueID == rhs.uniqueID }
Now when I write the following:
let a = A()
let b = B()
let c = a == b
I got error: binary operator '==' cannot be applied to operands of type 'A' and 'B'
is there any way to achieve this ?
you have to define the equality function with two generic types to allow different types to be compared, like this:
func ==<T: P, T2: P>(lhs: T , rhs: T2) -> Bool { return lhs.uniqueID == rhs.uniqueID }

How to construct generic without protocol in Swift?

I'm getting the following error...
'T' cannot be constructed because it has no accessible initializers
When compiling...
class Sub<T : Equitable> {
func def(v : T) -> Bool{
var d = T() // <- Error
return d == v
}
}
var s = Sub<Int>()
println(s.def(0), s.def(1)) // I'm expecting "true, false"
I understand that in order for a generic type to be initialized, it needs to conform to a protocol that contains an init() constructor. Such as...
protocol A : Equitable {
init()
}
class Sub<T : A> {
But then I would get get the error
Type 'Int' does not conform to protocol 'A'
at the line
var s = Sub<Int>()
So how would I go about making a value type such as Int or Bool conform to a protocol that can be initialized?
You need to extend Int like so for it to adopt protocol A:
class Sub<T : A> {
func def(v : T) -> Bool{
var d = T()
return d == v
}
}
protocol A : Equatable {
init()
}
extension Int: A {
}
var s = Sub<Int>()
s.def(0) // true
s.def(1)) // false