can swift functions and closures conform to Hashable? - ios

Suppose I want to have a Set of functions or closures. Here's how I would go about it:
typealias HandlerX = () -> ()
static var handlersX = Set<HandlerX>()
This produces the following compiler error:
Type 'HandlerX' (aka '() -> ()') does not conform to protocol 'Hashable'
Is this a dead end?

Yes, this is a dead end. Hashable isn't really your problem; there's no way to decide whether two closures are Equal (which is a base requirement of Hashable).

You can create a wrapper struct for closure
struct Hashed<T>: Hashable {
let value: T
let hash: Int
init(value: T, hash: Int) {
self.value = value
self.hash = hash
}
public func hash(into hasher: inout Hasher) {
hasher.combine(hash)
}
static func == (lhs: Hashed<T>, rhs: Hashed<T>) -> Bool {
lhs.hashValue == rhs.hashValue
}
}
But you need to understand the identity of your closures. For example, it can be the class that created it, or #filename + #line, or always unique with UUID()

Related

Swift - Making a type Hashable that has instance variable of protocol type

How to make a struct/class confirm to H.ashable/Equatable that has an instance variable of a protocol type. Let me provide an example of this:
protocol Filter {
associatedtype Input
func satisfies(input: Input) -> Bool
}
protocol FilterProcessor {
func satisfies(input: String) -> Bool
}
struct SelectionFilter: Filter {
let identifier: String
let processor: FilterProcessor
func satisfies(input: String) -> Bool {
return processor.satisfies(input: input)
}
}
struct ProcessorOne: FilterProcessor {
let uuid: String
let type: String
func satisfies(input: String) -> Bool {
// Some logic based on uuid and type
return false
}
}
struct ProcessorTwo: FilterProcessor {
let data: Int
func satisfies(input: String) -> Bool {
// Some logic based on data
return false
}
}
This works fine as long as you don't need Hashable/Equatable confirmance on SelectionFilter but things quickly get very complicated as soon as you do, like try to make a Set of SelectionFilter:
Set([
SelectionFilter(identifier: "abc", processor: ProcessorOne(uuid: "1234", type: "size")),
SelectionFilter(identifier: "def", processor: ProcessorTwo(data: 1234))
])
For the above to work SelectionFilter needs to be Hashable. We can do that taking only identifier into account but that would be wrong as actual hashing/equality depends upon the processor's data also. For that FilterProcessor needs to be Hashable and as soon as we do that the SelectionFilter can't use the FilterProcessor directly as it is now a generic protocol.
One solution is to use a type eraser for the FilterProcessor as AnyFilterProcessor, but then this AnyFilterProcessor needs to confirm Hashable. How do you do that since all this AnyFilterProcessor will have is a closure that points back to the actual satisfies method.
So there are two alternatives I could make one is to make SelectionFilter an enum and the other is to eliminate the FilterProcessor and make SelectionFilter a class and other subclasses:
Option1:
enumm SelectionFilter: Filter, Hashable {
case filterOne(identifier: String, processor: ProcessorOne)
case filterTwo(identifier: String, processor: ProcessorTwo)
}
extension ProcessorOne: Hashable {}
extension ProcessorTwo: Hashable {}
This works neatly but the problem is that enums are not scalable. We have some 20 odd processors, which means 20 cases and for doing anything we need to switch all 20 cases.
Option2:
class SelectionFilter: Filter, Hashable {
let identifier: String
init(identifier: String) {
self.identifier = identifier
}
func satisfies(input: String) -> Bool {
return true
}
static func == (lhs: SelFilter, rhs: SelFilter) -> Bool {
lhs.identifier == rhs.identifier
}
func hash(into hasher: inout Hasher) {
hasher.combine(identifier)
}
}
class FilterOne: SelectionFilter {
let uuid: String
let type: String
init(identifier: String, uuid: String, type: String) {
self.uuid = uuid
self.type = type
super.init(identifier: identifier)
}
override func satisfies(input: String) -> Bool {
return true
}
}
But the issue here is a lot of duplicate code as each SelectionFilter subclass will need to implement == and hash functions. Also, it is error-prone as any developer might forget to implement these functions in a subclass.
Both of these options work but I am really not liking them. Is there is another way to do it?

Typealias inside Protocol

I have a typealias inside my protocol. Do I have to add the typealias to the conforming type or is there an alternative? My goal is to declare the typealias once.
protocol SomeProtocol {
associatedtype T
typealias Closure = (T) -> Void
var blocks: [Closure] { get set }
}
struct SomeStruct<T>: SomeProtocol {
typealias Closure = (T) -> Void // <- do I have to add this?
var blocks: [Closure]
}
FYI this works. Im just looking for a simpler implementation of the typealias.
You don't necessarily need the typealias in the struct -- it can be inferred:
protocol SomeProtocol {
associatedtype T
typealias Closure = (T) -> Void
var blocks: [Closure] { get set }
}
struct SomeStruct<T>: SomeProtocol {
var blocks: [(T) -> Void]
}
let someStruct = SomeStruct<Int>(blocks: [{someInt in print(someInt)}])
someStruct.blocks[0](123)

Extend #objc protocol with Comparable in Swift

I am trying to extend my protocol Option with Comparable to use simple .sort() method.
Below short example only with Equatable to show errors.
#objc protocol Option: Equatable {
var title: String { get }
var enabled: Bool { get }
var position: Int { get }
}
func ==(lhs: Option, rhs: Option) -> Bool {
return lhs.position == rhs.position
}
The Option protocol must be marked as #objc or inherit from NSObjectProtocol because it will be used with UIKit.
Errors:
#objc protocol 'Option' cannot refine non-#objc protocol
'Equatable'
Protocol 'Option' can only be used as a generic constraint
because it has Self or associated type requirements
Do you have any suggestion how to solve this problem?
Equatable lives in the Swift world only, thus you cannot extend it to a protocol that will be used by Objective-C. Trying to do this results in error #1
Protocols that have a Self requirement (i.e. at least one method from the protocol declaration contains Self) cannot be used as arguments to functions, or to variable declarations, only as arguments to a generic clause, e.g. func doSomething<T: Option>(argument: T).
Removing Equatable from the Option protocol declaration, and declaring == as generic on Option will solve the compile errors. As for sorting, you can also overload the < operator, and sort via that operator (without needing to implement Comparable):
#objc protocol Option {
var title: String { get }
var enabled: Bool { get }
var position: Int { get }
}
func ==<T: Option>(lhs: T, rhs: T) -> Bool {
return lhs.position == rhs.position
}
func <<T: Option>(lhs: T, rhs: T) -> Bool {
return lhs.position < rhs.position
}
This allows you to pass objects that conform to the protocol to UIKit, and to also compare them within your swift code.
class A: NSObject, Option { .. }
class B: NSObject, Option { ... }
let a = A()
let b = B()
a == b // compiles, and returns true if a and b have the same position
let c: [Option] = [a, b]
c.sort(<) // returns a sorted array by the `position` field
One important note regarding the sorting code above: if you don't specify the type for c, then the compiler infers its type as [NSObject], and the sort call will not compile due to ambiguity of the < operator. You need to explicitly declare c as [Option] to take advantage of the overloaded operator.
The issue can be fixed by the new protocol oriented programming features introduced in swift 2.0
#objc protocol 'Option' cannot refine non-#objc protocol 'Equatable'
As the error states, the Equatable protocol is a swift protocol that you can't to Obj C context
Protocol 'Option' can only be used as a generic constraint because it
has Self or associated type requirements
You can achieve this in the following way:
#objc protocol Option {
var title: String { get }
var enabled: Bool { get }
var position: Int { get }
}
extension Equatable where Self : Option
{
}
extension Comparable where Self : Option
{
}
func ==(lhs: Option, rhs: Option) -> Bool
{
return lhs.position == rhs.position
}
func <(lhs: Option, rhs: Option) -> Bool
{
return lhs.position < rhs.position
}
func >(lhs: Option, rhs: Option) -> Bool
{
return lhs.position > rhs.position
}
And your class and implementation looks like:
class MyClass: Option
{
#objc var title: String = ""
#objc var enabled: Bool = true
#objc var position: Int = 0
init()
{
}
convenience init(title : String, enabled : Bool, position: Int)
{
self.init()
self.title = title
self.enabled = enabled
self.position = position
}
}
let firstObj = MyClass()
let secondObj = MyClass()
let optionArray : [Option] = [firstObj, secondObj]
// Sort array of options
optionArray.sort(<)

Overloading equivalence (==) operator for custom class in Swift

Is it possible to overload equivalence (==) operator for a custom class inside that custom class. However I know that it is possible to have this operator overloaded outside class scope. Appreciate any sample code.
Thanks in advance.
Add global functions. For example:
class CustomClass {
var id = "my id"
}
func ==(lhs: CustomClass, rhs: CustomClass) -> Bool {
return lhs == rhs
}
func !=(lhs: CustomClass, rhs: CustomClass) -> Bool {
return !(lhs == rhs)
}
To conform Equatable protocol in Swift 2
class CustomClass: Equatable {
var id = "my id"
}
func ==(left: CustomClass, right: CustomClass) -> Bool {
return left.id == right.id
}
To conform Equatable protocol in Swift 3
class CustomClass {
var id = "my id"
}
extension CustomClass: Equatable {
static func ==(lhs: CustomClass, rhs: CustomClass) -> Bool {
return lhs.id == rhs.id
}
}
No, operators are overloaded using global functions.

Type does not conform to an undefined protocol

In Xcode 6 Beta 2 I have written the following class:
class Item : Printable, Hashable {
var description:String {
return "..."
}
var hashValue:Int {
return 1
}
}
I am receiving an error stating that the Type 'Item' does not conform to the protocol 'Equatable' even though I have not tried to implement a protocol called 'Equatable.' Has anyone seen behavior like this? Any solution or workaround? thanks!
According to the Hashable docs: (see the very bottom of that page)
Types that conform to the Hashable protocol must provide a gettable Int property called hashValue, and must also provide an implementation of the “is equal” operator (==).
And according to the Equatable docs you do that by defining an operator overload function for == where the type you want is on each side of the operator.
func == (lhs: MyStruct, rhs: MyStruct) -> Bool {
return lhs.name == rhs.name
}
Which means your code something like this:
class Item : Printable, Hashable {
var description:String {
return "..."
}
var hashValue:Int {
return 1
}
}
func == (lhs: Item, rhs: Item) -> Bool {
return lhs.hashValue == rhs.hashValue
}
// Testing...
Item() == Item() //-> true
Assuming hashValue is what you think would make them equivalent, of course.
The Hashable protocol implements the Equatable protocol, hence the reason why the compiler complains

Resources