La propiedad body es el elemento central del protocolo View en SwiftUI, que determina qué contenido se muestra en la pantalla. Según Apple Developer Documentation, 2024, body es el único requisito obligatorio del protocolo View y devuelve un tipo que se ajusta a este mismo protocolo. SwiftUI llama a body con cada cambio de estado para construir y comparar un nuevo árbol de elementos.
Puntos Clave
body es una propiedad computada que es el único requisito obligatorio del protocolo View. Cada estructura que se ajuste a View debe implementar body. La propiedad devuelve el contenido que SwiftUI muestra en la pantalla — puede ser texto, una imagen, un botón, un contenedor con elementos anidados o cualquier otro tipo que se ajuste al protocolo View.
La firma de body siempre es fija: var body: some View { get }. El tipo de retorno es some View (un tipo opaco), no un tipo concreto. Esto significa que diferentes Views pueden devolver diferentes tipos concretos en body, pero el compilador de Swift fija el tipo concreto para cada implementación en tiempo de compilación.
Según WWDC 2022, body es el punto de entrada para la descripción declarativa de la interfaz. A diferencia de UIKit, donde creas y configuras UIView de forma imperativa, en SwiftUI describes declarativamente lo que debe mostrarse, y SwiftUI mismo calcula cómo implementarlo.
body debe comportarse como una función pura — con las mismas entradas (propiedades de la estructura y estado) debe devolver el mismo árbol View. Si body depende de un estado mutable externo (variables globales, UserDefaults sin el envoltorio @AppStorage), el comportamiento se vuelve impredecible y SwiftUI podría redibujar la pantalla incorrectamente.
La propiedad computada body no almacena un valor — se calcula cada vez que se accede a ella. Cuando SwiftUI determina que el estado ha cambiado, recrea la estructura View y lee el nuevo valor de body para obtener el árbol de elementos actualizado para su visualización.
struct CounterView: View {
@State private var count = 0
var body: some View {
VStack {
Text("Contador: \(count)")
.font(.largeTitle)
Button("Incrementar") {
count += 1
}
.padding()
.background(.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
}
}
En este ejemplo, body devuelve un VStack que contiene un Text y un botón con modificadores. Cuando se presiona el botón, la propiedad @State count se incrementa, SwiftUI recrea la estructura CounterView y vuelve a llamar a body para obtener el árbol actualizado con el nuevo valor de Text.
Los modificadores (.font, .padding, .background, .foregroundColor, .cornerRadius) no modifican la View original sino que la envuelven en ModifiedContent — un nuevo tipo que añade la modificación. Cada modificador crea otro nivel de anidamiento, lo cual es importante considerar para el rendimiento.
some View en el tipo de retorno de body no es solo una convención sino un requisito del compilador. Swift requiere que todas las rutas de retorno en body tengan el mismo tipo concreto. Sin @ViewBuilder no puedes devolver Text en una rama y Button en otra — el compilador producirá un error.
struct ConditionalView: View {
var isReady: Bool
@ViewBuilder
var body: some View {
if isReady {
Text("Listo")
.foregroundColor(.green)
} else {
ProgressView()
}
}
}
@ViewBuilder en body permite usar lógica condicional (if/else, switch) sin errores de compilación. ViewBuilder envuelve automáticamente las diferentes ramas en ConditionalContent — un tipo especial que oculta las diferencias de tipos concretos. Esta es una capacidad clave para construir interfaces dinámicas.
Sin @ViewBuilder el compilador intenta inferir un único tipo para todas las rutas de retorno. Si los tipos difieren — se produce un error. Es por esto que SwiftUI aplica implícitamente @ViewBuilder a body en las declaraciones View, aunque en el código de usuario hay que añadir la anotación explícitamente para métodos y propiedades personalizados que devuelvan múltiples Views.
Usar some View en lugar de un tipo concreto no reduce el rendimiento — el compilador conoce el tipo exacto en tiempo de compilación y genera código directo sin despacho dinámico. AnyView, por el contrario, usa borrado de tipo (type erasure) con la sobrecarga de envolver en un contenedor existencial.
body es llamado por SwiftUI en tres escenarios principales: cuando la View se muestra por primera vez, cuando cambia @State/@Binding/@ObservedObject/@StateObject, y cuando la View padre pasa nuevos valores a través del inicializador. SwiftUI también puede llamar a body cuando cambian los valores de entorno (@Environment).
La frecuencia de las llamadas a body no debería preocuparte — SwiftUI optimiza el redibujado mediante el mecanismo de identidad. Cada View en la jerarquía tiene un identificador único. Si la identidad y los datos de entrada no han cambiado — body no se llama aunque la View padre se redibuje. Esto se logra mediante la comparación Equatable y la estabilidad estructural.
struct ParentView: View {
var body: some View {
ChildView(name: "Alice") // Identidad estable
}
}
struct ChildView: View {
let name: String
var body: some View {
Text("¡Hola, \(name)!")
}
}
En este ejemplo, si ParentView se redibuja pero pasa el mismo valor de name — ChildView.body no se llama. SwiftUI compara los datos de entrada de la estructura y, si no han cambiado, omite el redibujado del componente hijo. Este es el mecanismo de diferenciación de vistas.
Existen varios problemas que provocan llamadas inesperadas a body: usar clases sin ObservableObject, pasar closures creadas dentro de body (cada creación de closure genera una nueva identidad), y el uso incorrecto de EquatableView. Si body se llama con demasiada frecuencia — verifica la estabilidad de identidad de todos los componentes hijos.
Primera regla: body debe ser mínimo. Traslada la lógica compleja a propiedades computadas o métodos separados que devuelvan View. Esto mejora la legibilidad y permite a SwiftUI determinar con mayor precisión qué partes de la jerarquía han cambiado. Divide bodies grandes en subcomponentes con límites de responsabilidad claros.
Segunda regla: no uses body para realizar trabajo. Carga de datos, operaciones de red, escrituras en base de datos — todo esto debe ocurrir fuera de body, en tasks, modificadores onChange o a través de ObservableObject. body está destinado únicamente a la declaración de la interfaz.
Tercera regla: usa la propiedad EquatableView o un protocolo Equatable personalizado para Views si la comparación estructural estándar es insuficiente. Esto permite indicar explícitamente a SwiftUI cuándo una View hija necesita un redibujado y evitar llamadas innecesarias a body.
Cuarta regla: si body contiene cálculos complejos (formateo, filtrado, ordenación) — usa @State para almacenar en caché el resultado o traslada los cálculos a un método separado llamado desde onChange. Los cálculos repetidos en body con cada actualización de estado son una causa común de ralentización de animaciones.
Quinta regla: para listas (List, ForEach) asegura identificadores estables mediante el parámetro id. Sin identidad estable, ForEach recrea todos los elementos ante cualquier cambio, llamando a body para cada uno de ellos, incluso si solo cambió un elemento.
Preguntas Frecuentes
body es la propiedad computada del protocolo View que devuelve el contenido para mostrar. Es el único requisito obligatorio del protocolo. El tipo de retorno es some View, lo que permite a SwiftUI optimizar la jerarquía en tiempo de compilación.
Sí, SwiftUI llama a body con cada cambio de estado (@State, @Binding, @ObservedObject) o de datos de entrada. Este es un comportamiento normal de un framework declarativo. SwiftUI optimiza la frecuencia de llamadas mediante el mecanismo de identidad y la comparación Equatable.
some View es un tipo opaco que oculta la implementación concreta. El compilador fija el tipo en tiempo de compilación, asegurando el rendimiento de llamada directa. Esto proporciona flexibilidad: puedes cambiar el tipo de retorno sin cambiar la firma.
No, body no puede ser opcional — el tipo de retorno some View no permite nil. Si necesitas ocultar un elemento condicionalmente, usa lógica condicional dentro de @ViewBuilder o devuelve EmptyView, que no ocupa espacio en la jerarquía.
Cada modificador crea una nueva capa de ModifiedContent, aumentando la profundidad de la jerarquía. Para la mayoría de las pantallas (hasta 50 modificadores) el impacto es insignificante. Una cantidad excesiva de modificadores (cientos) puede ralentizar el diffing. Agrupa modificadores relacionados en extensiones personalizadas.
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