Swift 5: Implemente operaciones de matriz genéricas en estructuras que implementen Equatable con el protocolo
Tengo 2 estructuras simples en mi aplicación que se ajustan a un protocolo, para habilitar algunas operaciones genéricas que necesito aplicar en ambos tipos:
import UIKit
protocol Track {
var url: URL { get set }
}
struct TrackFromMediaFile: Track {
var url: URL
}
struct TrackFromAsset: Track {
var url: URL
}
Ahora tengo una lista de estas estructuras:
var tracks = [Track]()
tracks.append(TrackFromMediaFile(url: URL(string: "http://www.remotefile")!))
tracks.append(TrackFromAsset(url: URL(string: "local://localfile")!))
Necesito encontrar el índice de una pista específica en esta lista de pistas:
// throws:
// Value of protocol type 'Track' cannot conform to 'Equatable'; only struct/enum/class types can conform to protocols
var track = tracks[0]
tracks.firstIndex(of: track)
Traté de seguir enfoques desde https://stackoverflow.com/a/46719045/319905 y https://developer.apple.com/videos/play/wwdc2015/408/ en vano, no pude adaptarlos a mi ejemplo.
¿Cómo puedo implementar el manejo de matrices de pistas de forma genérica?
Respuestas
No está muy claro qué tipo de igualdad está buscando. Si desea verificar tanto la igualdad de tipos como la igualdad de valores, lo haría de esta manera:
tracks.firstIndex(where: {
type(of: $0) == type(of: track) && $0.url == track.url
})
Si solo desea validar que urles igual, lo haría de esta manera:
tracks.firstIndex(where: { $0.url == track.url })
En Swift, Equatable significa que un valor se puede comparar con otros valores del mismo tipo. No se aplica a valores de diferentes tipos. Un protocolo no es un tipo, describe tipos, por lo que Track en sí no puede ser Equatable.
Si va a hacer mucho esto, a menudo es conveniente expresar "capaz de comprobar la igualdad con otros miembros del protocolo". Eso no es equiparable, pero ciertamente es algo que puedes expresar. Se parece a esto:
protocol Track {
var url: URL { get set }
func isEqual(to: Track) -> Bool
}
Para los tipos de seguimiento que ya son Equatable, puede proporcionar una implementación predeterminada:
extension Track where Self: Equatable {
func isEqual(to other: Track) -> Bool {
guard let other = other as? Self else { return false }
return self == other
}
}
Y marque sus tipos conformes como Equatable (swift creará automáticamente conformidades):
struct TrackFromMediaFile: Track, Equatable { ... }
struct TrackFromAsset: Track, Equatable { ... }
Con eso, el código es un poco mejor:
tracks.firstIndex(where: { $0.isEqual(to: track) })
Y si lo hace mucho, por supuesto, puede hacerlo un poco mejor aún:
extension Collection where Element == Track {
func firstIndex(of track: Track) {
firstIndex(where: { $0.isEqual(to: track) })
}
}
Y con eso, puede tener la sintaxis con la que comenzó:
tracks.firstIndex(of: track)