Crear un protocolo que represente los objects hashable que se pueden activar o desactivar

Estoy intentando crear un protocolo simple que diga si un object está o no en estado "on" o en "off". La interpretación de lo que es depende del object de implementación. Para un UISwitch , es si el interruptor está encendido o apagado (duh). Para un UIButton , podría ser si el button está en el estado selected o no. Para un Car , podría ser si el motor del automobile está encendido o no, o incluso si se está moviendo o no. Entonces me propuse crear este sencillo protocolo:

 protocol OnOffRepresentable { func isInOnState() -> Bool func isInOffState() -> Bool } 

Ahora puedo extender los controles de interfaz de usuario antes mencionados de la siguiente manera:

 extension UISwitch: OnOffRepresentable { func isInOnState() -> Bool { return on } func isInOffState() -> Bool { return !on } } extension UIButton: OnOffRepresentable { func isInOnState() -> Bool { return selected } func isInOffState() -> Bool { return !selected } } 

Ahora puedo hacer una matriz de estos types de objects y hacer un bucle para comprobar si están encendidos o apagados:

 let booleanControls: [OnOffRepresentable] = [UISwitch(), UIButton()] booleanControls.forEach { print($0.isInOnState()) } 

¡Estupendo! Ahora quiero hacer un dictionary que UILabel estos controles a una UILabel para que pueda cambiar el text de la label asociada al control cuando el control cambia de estado. Entonces voy a declarar mi dictionary:

 var toggleToLabelMapper: [OnOffRepresentable : UILabel] = [:] // error: type 'OnOffRepresentable' does not conform to protocol 'Hashable' 

Oh! ¡Derecha! Tonto de mí. Ok, permítanme actualizar el protocolo usando la composition del protocolo (después de todo, los controles que quiero usar aquí son todos Hashable: UISwitch, UIButton, etc.):

 protocol OnOffRepresentable: Hashable { func isInOnState() -> Bool func isInOffState() -> Bool } 

Pero ahora tengo un nuevo set de errores:

 error: protocol 'OnOffRepresentable' can only be used as a generic constraint because it has Self or associated type requirements error: using 'OnOffRepresentable' as a concrete type conforming to protocol 'Hashable' is not supported 

Ok … Así que hago un poco de desbordamiento de stack cavando y buscando. Encuentro muchos artículos que parecen prometedores, como Set y protocolos en Swift , el uso de algún protocolo como un tipo concreto que se ajusta a otro protocolo no es compatible , y veo que hay algunos artículos geniales en el type erasure que parecen ser exactamente lo que Necesito: http://krakendev.io/blog/generic-protocols-and-their-shortcomings , http://robnapier.net/erasure , y https://realm.io/news/type-erased-wrappers- in-swift / solo para nombrar unos pocos.

Aquí es donde me quedo atrapado. He intentado leer todo esto, y he intentado crear una class que sea Hashable y que también se ajuste a mi protocolo OnOffRepresentable , pero no puedo entender cómo hacer que todo se conecte.

No sé si haría necesariamente que el protocolo OnOffRepresentable henetworkinge de Hashable . No parece que algo que quieras que se represente como activado o desactivado también debe ser aceptable. Entonces, en mi implementación a continuación, agrego la conformidad Hashable al contenedor de borrado de tipo solamente. De esta forma, puede hacer reference a los elementos OnOffRepresentable directamente siempre que sea posible (sin la advertencia "solo se puede usar en una restricción genérica"), y solo envolverlos dentro del borrador de tipo HashableOnOffRepresentable cuando necesite colocarlos en sets o usarlos como keys de dictionary .

 protocol OnOffRepresentable { func isInOnState() -> Bool func isInOffState() -> Bool } extension UISwitch: OnOffRepresentable { func isInOnState() -> Bool { return on } func isInOffState() -> Bool { return !on } } extension UIButton: OnOffRepresentable { func isInOnState() -> Bool { return selected } func isInOffState() -> Bool { return !selected } } struct HashableOnOffRepresentable : OnOffRepresentable, Hashable { private let wrapped:OnOffRepresentable private let hashClosure:()->Int private let equalClosure:Any->Bool var hashValue: Int { return hashClosure() } func isInOnState() -> Bool { return wrapped.isInOnState() } func isInOffState() -> Bool { return wrapped.isInOffState() } init<T where T:OnOffRepresentable, T:Hashable>(with:T) { wrapped = with hashClosure = { return with.hashValue } equalClosure = { if let other = $0 as? T { return with == other } else { return false } } } } func == (left:HashableOnOffRepresentable, right:HashableOnOffRepresentable) -> Bool { return left.equalClosure(right.wrapped) } func == (left:HashableOnOffRepresentable, right:OnOffRepresentable) -> Bool { return left.equalClosure(right) } var toggleToLabelMapper: [HashableOnOffRepresentable : UILabel] = [:] let anySwitch = HashableOnOffRepresentable(with:UISwitch()) let anyButton = HashableOnOffRepresentable(with:UIButton()) var switchLabel:UILabel! var buttonLabel:UILabel! toggleToLabelMapper[anySwitch] = switchLabel toggleToLabelMapper[anyButton] = buttonLabel 

Crear un protocolo con un tipo associatedType (o Hashable a otro protocolo que tenga un tipo associatedType como Hashable ) hará que ese protocolo no sea muy amigable con los generics.

Te sugeriré una solución muy simple

OnOffRrepresentable

En primer lugar, no necesitamos 2 funciones que digan exactamente lo contrario ¿no? 😉

Así que esto

 protocol OnOffRepresentable { func isInOnState() -> Bool func isInOffState() -> Bool } 

se convierte en esto

 protocol OnOffRepresentable { var on: Bool { get } } 

y por supuesto

 extension UISwitch: OnOffRepresentable { } extension UIButton: OnOffRepresentable { var on: Bool { return selected } } 

Vincular un OnOffRrepresentable a un UILabel

Ahora no podemos usar OnOffRepresentable como Key de un Dictionary porque nuestro protocolo debe ser Hashable . ¡Entonces usemos otra estructura de datos!

 let elms: [(OnOffRepresentable, UILabel)] = [ (UISwitch(), UILabel()), (UIButton(), UILabel()), ] 

Eso es.