Sendable es un protocolo de Swift que marca los tipos seguros para transferir entre hilos. Al trabajar con concurrencia (actor, async/await, Task), el compilador de Swift requiere que todos los datos transferidos entre contextos aislados cumplan con Sendable. Esto evita la transferencia accidental de tipos no seguros que podrían provocar condiciones de carrera. El protocolo actúa como un contrato: un tipo que cumple con Sendable garantiza la ausencia de estado interno no sincronizado. Según WWDC 2021, el protocolo Sendable es un elemento obligatorio al diseñar una arquitectura segura multiproceso.
Puntos clave
Sendable es un protocolo de la biblioteca estándar de Swift (SE-0302) que marca los tipos seguros para transferir entre dominios de aislamiento. Un dominio de aislamiento puede ser un actor, una tarea Task o un contexto @MainActor. Sendable no contiene requisitos — actúa como un marcador de seguridad.
Antes de Swift 5.5, los desarrolladores podían transferir cualquier objeto entre hilos a través de DispatchQueue, y el compilador no verificaba si era seguro. Sendable cierra esta brecha: ahora el compilador rastrea las transferencias entre hilos y bloquea las no seguras. Esto hace que el código concurrente sea más fiable incluso antes de ejecutarse.
struct UserProfile: Sendable {
let name: String
let age: Int
}
class NonSendableClass {
var counter: Int = 0
}
En el ejemplo, UserProfile puede transferirse de forma segura entre hilos porque es una estructura con propiedades constantes de tipos Sendable. NonSendableClass provocará un error de compilación al intentar transferirlo mediante await o a un Task.
Los tipos Sendable en Swift se dividen en tres categorías: tipos valor con propiedades Sendable, clases finales con estado inmutable y funciones/clausuras marcadas con @Sendable. El compilador infiere automáticamente la conformidad con Sendable para los tipos valor.
Struct, enum y tupla se vuelven automáticamente Sendable si todas sus propiedades y valores asociados también son Sendable. Este es un enfoque conservador: si al menos una propiedad no cumple con Sendable, todo el tipo no se reconocerá como seguro.
| Tipo | Sendable | Condición |
|---|---|---|
| Struct | Sí (sin declaración explícita) | Todas las propiedades son Sendable |
| Enum | Sí (sin declaración explícita) | Todos los valores asociados son Sendable |
| Final class | Sí (con declaración explícita) | Todas las propiedades let son Sendable, sin var |
| Non-final class | No | No puede ser Sendable debido a herencia |
Todos los tipos incorporados de Swift — Int, String, Double, Bool, Optional, Array, Dictionary, Set — cumplen con Sendable. Esto hace que la composición de tipos sea segura por defecto. El desarrollador solo debe vigilar las clases personalizadas.
@unchecked Sendable es un mecanismo que permite a una clase declararse explícitamente como Sendable, omitiendo las verificaciones del compilador. El desarrollador asume la responsabilidad de la seguridad de hilos de dicha clase. Esto es útil para puentes con Objective-C y estructuras optimizadas.
@unchecked Sendable se usa cuando una clase garantiza internamente la seguridad mediante bloqueos u operaciones atómicas, pero el compilador no puede verificarlo estáticamente. Por ejemplo, una clase con os_unfair_lock o pthread_mutex_t — su seguridad de hilos está garantizada por el código, pero Swift no lo detecta.
final class AtomicCounter: @unchecked Sendable {
private var value: Int = 0
private let lock = NSLock()
func increment() {
lock.lock()
value += 1
lock.unlock()
}
}
Usar @unchecked Sendable debe hacerse con cuidado. Es una señal explícita para otros desarrolladores: «lo verifiqué, esto es seguro». Un error en la implementación puede provocar condiciones de carrera difíciles de detectar. Antes de usar @unchecked, asegúrate de que el tipo realmente no pueda reescribirse como un tipo valor.
Actor y Sendable son dos caras de la misma moneda. Actor aísla su estado, pero para intercambiar datos con el mundo exterior debe devolver tipos Sendable. Si un método de actor devuelve un tipo no Sendable, el compilador emite una advertencia o error.
Cuando un actor envía datos a código externo, estos datos cruzan el límite de aislamiento. Sendable garantiza que el destinatario pueda usarlos de forma segura fuera del actor. El actor permanece aislado — su estado interno no se expone.
struct AccountSnapshot: Sendable {
let id: UUID
let balance: Double
let lastUpdated: Date
}
actor BankActor {
private var balance: Double = 0
func snapshot() async -> AccountSnapshot {
return AccountSnapshot(
id: UUID(),
balance: balance,
lastUpdated: Date()
)
}
}
AccountSnapshot es una estructura Sendable que contiene solo propiedades let de tipos Sendable. Este enfoque es una buena práctica para extraer datos de un actor. La instantánea del estado se transfiere por valor y el actor no pierde el control sobre su estado.
@Sendable es un atributo para funciones y clausuras que garantiza que la clausura no captura datos no Sendable de forma mutable. Cuando una clausura se transfiere a un Task o método de actor, debe ser Sendable.
El compilador verifica que la clausura @Sendable no capture referencias mutables a clases. Se permite capturar propiedades let de tipos Sendable. Capturar una variable var de un tipo referencia provocará un error porque la clausura podría ejecutarse simultáneamente con la mutación.
func performAsync(operation: @Sendable () -> Void) {
Task {
await operation()
}
}
let constant = "Safe"
var counter = 0
// counter — capturar var muta counter, error de compilación
performAsync { // ❌ Mutación de variable capturada
print(constant)
}
La regla es simple: una clausura @Sendable solo puede capturar datos que sean Sendable y que no serán mutados desde fuera. Para clases, se permite capturar una referencia débil weak self si la clase no está marcada como Sendable. Esto evita ciclos de retención clásicos y condiciones de carrera.
Preguntas frecuentes
Sendable es un marcador que le dice al compilador: «este tipo se puede transferir de forma segura entre hilos». Las estructuras y constantes suelen ser Sendable por defecto, las clases no, a menos que se indique explícitamente.
Los tipos valor (struct, enum) con propiedades Sendable, las clases finales con estado inmutable y todos los tipos básicos de Swift: Int, String, Double, Bool, Array, Dictionary, Optional — cumplen con Sendable automáticamente.
@unchecked Sendable es una forma de declarar una clase como Sendable sin verificaciones estáticas del compilador. El desarrollador garantiza la seguridad por su cuenta, a menudo mediante bloqueos u operaciones atómicas.
Actor aísla su estado, pero al devolver datos al código externo, estos datos cruzan el límite de aislamiento. Sendable garantiza que el destinatario no se encuentre con condiciones de carrera al usar estos datos fuera del actor.
Añade el protocolo Sendable a una clase final, cuyas propiedades sean todas constantes de tipos Sendable. Si la clase usa bloqueos, se puede usar @unchecked Sendable, pero esto requiere precaución.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también