Search code examples
swiftgenericsequatable

Common Equatable class on Swift


I need container for Any Equatable items in NOT Generic class (for example UI classes initial from storyboard). I need like this

var items: [Equatable]?

but it don't work, Equatable need Generic. the problem that there is no exist common Equatable class.


Ok - Go to generic! But if I do this

class Item<Value: Equatable>: Equatable {
   var value: Value
   init(_ value: Value) {
       self.value = value
   }
   //Equatable
   public static func ==(lhs: Item, rhs: Item) -> Bool {
       return (lhs.value == rhs.value)
   }
}

then I will be forced to specify the type in my nonGeneric-UI class. Like this

var items: [Item<WhatShouldBeHere?>]?

but again we come to the problem that there is no exist common Equatable class


Any solutions for container for All Equatable?


Solution

  • In lieu of existential types, you need to use a type eraser:

    public struct AnyEquatable: Equatable {
        public let value: Any
        private let equals: (Any) -> Bool
    
        public init<E: Equatable>(_ value: E) {
            self.value = value
            self.equals = { ($0 as? E) == value }
        }
    
        public static func == (lhs: AnyEquatable, rhs: AnyEquatable) -> Bool {
            return lhs.equals(rhs.value) || rhs.equals(lhs.value)
        }
    }
    

    example usage:

    let items = [
        AnyEquatable(1),
        AnyEquatable(1.23),
        AnyEquatable(true),
        AnyEquatable("some string")
    ]
    
    let desired = "some string"
    let desiredAsAnyEquatable = AnyEquatable(desired)
    let desiredDescription = String(reflecting: desired)
    
    for item in items {
        let itemDescription = String(reflecting: item.value)
        let isEqualToDesired = item == desiredAsAnyEquatable
        print("\(itemDescription) is \(isEqualToDesired ? "": "not ")equal to \(desiredDescription)")
    }
    

    Example output:

    1 is not equal to "some string"

    1.23 is not equal to "some string"

    true is not equal to "some string"

    "some string" is equal to "some string"