ProgramaciónDesarrollador iOS Senior

¿Qué son los 'opaque types' (some) en Swift, cuándo y por qué se utilizan, y en qué se diferencian de los protocolos con associated type?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Historia de la cuestión:

En Swift antes de la versión 5, al devolver un valor que correspondía a un protocolo con associated type, a menudo se enfrentaba a limitaciones: no se podía utilizar el tipo como return type directamente, se requería type erasure. Para mejorar la legibilidad y el rendimiento, se introdujeron los opaque types: valores devueltos mediante la palabra clave some, que permiten describir abstracciones en interfaces públicas.

Problema:

Cuando es necesario ocultar el tipo real del valor de retorno, manteniendo la abstracción a través de un protocolo, pero evitando los costos del dynamic dispatch y el type erasure. Por ejemplo, al devolver colecciones, secuencias, componentes de vista.

Solución:

Los opaque types permiten devolver un tipo que corresponde a un protocolo, ocultando su implementación concreta. El compilador conoce el tipo real, pero la parte que llama no.

Ejemplo:

protocol Shape { func area() -> Double } struct Circle: Shape { var radius: Double func area() -> Double { Double.pi * radius * radius } } func makeCircle() -> some Shape { return Circle(radius: 3) } let s = makeCircle() print(s.area()) // funciona

Características clave:

  • Opaque type: siempre es el mismo tipo, oculto detrás de un protocolo, declarado en return mediante some
  • Más rápido y estricto que type erasure, permite trabajar con associated type en protocolos
  • No permite devolver diferentes tipos en diferentes ramas (el tipo debe ser el mismo para todos los return)

Preguntas engañosas.

¿En qué se diferencia opaque type (some Protocol) de un tipo de retorno Protocol?

El opaque type tiene una implementación concreta durante la compilación (aunque está oculta desde fuera). Al devolver Protocol, se utiliza dynamic dispatch, no hay type safety si hay associated type.

¿Se pueden devolver diferentes tipos utilizando some Protocol en una misma función?

No. Todos los return deben devolver el mismo tipo real:

func maker(flag: Bool) -> some Shape { if flag { return Circle(radius: 3) } else { return Square(size: 2) // Error: tipo de retorno no coincide } }

¿Puede associatedtype dentro de Protocol ser utilizado a través de some Protocol?

Sí. Esa es precisamente la razón por la que se necesita un opaque type:

protocol View { associatedtype Body } func makeView() -> some View { /* ... */ }

Errores típicos y anti-patrones

  • Confusión entre some Protocol y Protocol: diferentes casos y limitaciones
  • Violación de la regla de homogeneidad del tipo de retorno en todas las ramas
  • Aplicación de some donde sería más simple usar protocol o typealias

Ejemplo de la vida real

Caso negativo

La función devuelve un protocolo sin opaque, no permite usar métodos con associatedtype, se requiere un complicado type erasure, el código no compila o funciona de manera subóptima.

Ventajas:

  • Flexibilidad en abstracciones tipo AnySequence

Desventajas:

  • Pérdida de type safety, bajo rendimiento
  • No funcionan los tipos asociados

Caso positivo

ViewBuilder en SwiftUI utiliza some View, ocultando detalles, aumentando la seguridad de tipos, mejorando la velocidad de compilación y runtime.

Ventajas:

  • API legible, type safety, sin dynamic dispatch
  • Facilidad de mantenimiento

Desventajas:

  • No se pueden devolver diferentes tipos en una misma función