Actores, @MainActor y Sendable
Protege datos compartidos de las condiciones de carrera con actores, mantén la UI en el hilo principal con @MainActor, y entiende Sendable, la marca de lo seguro.
Cuando varias tareas corren a la vez y tocan los mismos datos, aparecen las condiciones de carrera: bugs no deterministas, casi imposibles de reproducir. Los actores son la respuesta de Swift: protegen su estado para que solo se acceda de forma ordenada.
- Qué es un actor y qué problema resuelve.
- Aislamiento: por qué accedes a un actor con
await. @MainActorpara el trabajo de interfaz.Sendable: qué puede cruzar entre tareas con seguridad.
El problema: condiciones de carrera
Dos tareas incrementando el mismo contador a la vez pueden pisarse y perder cuentas. Un actor garantiza que solo una tarea toca su estado interno cada vez:
actor Contador {
private var valor = 0
func incrementar() { valor += 1 }
func total() -> Int { valor }
}
Aislamiento: el await al acceder
El estado de un actor está aislado: desde fuera, accedes con await, porque puede que tengas que esperar tu turno. Swift lo impone en compilación:
let contador = Contador()
await contador.incrementar() // desde fuera, siempre con await
let n = await contador.total()
La clave: el compilador no te deja acceder al estado de un actor sin pasar por su cola ordenada. No es una convención que puedas olvidar — es una regla del sistema de tipos. Las condiciones de carrera, esa categoría de bugs que aterroriza a los desarrolladores porque aparecen una vez de cada mil y nunca en el depurador, se vuelven imposibles por construcción. Escribes concurrencia agresiva con la tranquilidad de que Swift te avisa en compilación si algo no es seguro.
@MainActor: la interfaz vive en el hilo principal
Todo lo que toca la UI debe ejecutarse en el hilo principal. @MainActor marca código que siempre corre ahí:
@MainActor
class VistaModelo {
var titulo = "" // seguro tocarlo desde la UI
func actualizar() { titulo = "Listo" }
}
Novedad importante de las versiones recientes: por defecto, tu código de app corre en el hilo principal (aislado a @MainActor) salvo que elijas explícitamente lanzar concurrencia. Esto invierte la carga: ya no tienes que anotar @MainActor por todas partes; es el punto de partida. Solo cuando mandas trabajo pesado a segundo plano entras en territorio concurrente. El resultado es que empezar es mucho más sencillo, y la concurrencia estricta deja de ser una barrera para principiantes.
Sendable: lo que puede cruzar con seguridad
Cuando pasas un valor de una tarea a otra, Swift necesita saber que es seguro compartirlo. Los tipos que lo son conforman a Sendable. Los tipos de valor (structs de datos simples) suelen serlo automáticamente:
struct Usuario: Sendable { // seguro de pasar entre tareas
let nombre: String
let edad: Int
}
Las structs con solo propiedades Sendable lo son solas. Las clases mutables no lo son (por eso preferimos structs, otra vez el Nivel 1). Si necesitas compartir una clase entre tareas, protégela con un actor o hazla inmutable.
Sendable es cómo el compilador rastrea qué es seguro mover entre hilos. Si ves un error de “no conforma a Sendable”, te está diciendo “esto no es seguro compartir así”. La solución suele ser: hazlo un tipo de valor inmutable, o envuélvelo en un actor. Entender Sendable es entender el modelo de seguridad completo de la concurrencia de Swift.
- Crea un
actorcon un contador privado y métodos para incrementarlo y leerlo. - Accede a él desde fuera con
awaity observa que el compilador lo exige. - Marca una clase de modelo de vista con
@MainActor. - Define una
struct Sendabley razona por qué es segura para compartir entre tareas.