Todo iOS Developer experimentado sabe que una aplicación de calidad no solo brilla cuando está llena de datos, imágenes y contenido dinámico, sino también cuando no hay absolutamente nada que mostrar. Manejar correctamente la ausencia de datos es un pilar fundamental en el diseño de experiencia de usuario (UX). Históricamente, en la programación Swift, manejar estas pantallas vacías requería crear vistas personalizadas desde cero. Sin embargo, Apple ha revolucionado esta práctica introduciendo una solución nativa y elegante.
En este tutorial, exploraremos a fondo qué son y cómo mostrar Empty States con ContentUnavailableView en SwiftUI, una herramienta indispensable para cualquier desarrollador que trabaje en el ecosistema de Apple. Aprenderemos a implementar esta vista no solo para el iPhone, sino creando código en Swift que se adapta perfectamente a iOS, macOS y watchOS utilizando Xcode.
¿Qué son los Empty States y por qué son cruciales?
Los Empty States (o estados vacíos) son aquellas pantallas en tu aplicación donde, por diversas razones, no hay contenido para mostrarle al usuario. Estas situaciones pueden ocurrir por tres razones principales:
- Primer uso (Onboarding): El usuario acaba de descargar la app y aún no ha creado contenido (por ejemplo, una lista de tareas sin ninguna tarea registrada).
- Errores del usuario o del sistema: No hay conexión a internet, o una búsqueda no arrojó ningún resultado.
- Acciones completadas: El usuario ha limpiado su bandeja de entrada y no quedan correos por leer.
Para un iOS Developer, ignorar estos estados es un error grave. Una pantalla en blanco sin contexto genera frustración; el usuario puede pensar que la aplicación se ha congelado, que está cargando infinitamente o que simplemente está rota. Un buen Empty State debe cumplir tres objetivos:
- Informar: Explicar claramente por qué la pantalla está vacía.
- Educar: Enseñar al usuario cómo poblar esa pantalla.
- Incentivar: Proporcionar un botón o un llamado a la acción (Call to Action) claro para solucionar el estado vacío.
El pasado: Programación Swift antes de iOS 17
Antes de las recientes actualizaciones de SwiftUI, crear un estado vacío implicaba escribir mucho código repetitivo en Xcode. Tenías que combinar un VStack, un Image con un ícono de SF Symbols, varios Text con tipografías específicas (.font(.title), .foregroundColor(.secondary)), y manejar el espaciado de forma manual.
Aunque la programación Swift facilitaba la creación de componentes reutilizables, cada equipo de desarrollo terminaba inventando su propia rueda. Esto no solo consumía tiempo, sino que a menudo resultaba en inconsistencias visuales dentro de la misma aplicación o en comparativa con el sistema operativo.
La solución moderna: ContentUnavailableView en SwiftUI
A partir de iOS 17, macOS 14 y watchOS 10, Apple introdujo ContentUnavailableView. Se trata de una vista nativa de SwiftUI diseñada específicamente para estandarizar y simplificar la creación de estados vacíos.
Al utilizar Empty States con ContentUnavailableView en SwiftUI, delegas al sistema operativo la responsabilidad de los márgenes, los tamaños de fuente, los colores semánticos y la adaptabilidad. El sistema sabe exactamente cómo debe lucir un mensaje de “No hay contenido” en la pantalla gigante de un Mac, en la pantalla de un iPhone o en la diminuta esfera de un Apple Watch.
Implementación básica en Xcode
La forma más sencilla de utilizar esta vista en Xcode es a través de sus inicializadores de conveniencia. Imagina que tienes una aplicación de notas y el usuario realiza una búsqueda que no coincide con ninguna de sus entradas.
import SwiftUI
struct SearchView: View {
@State private var searchText = ""
@State private var searchResults: [String] = []
var body: some View {
NavigationStack {
Group {
if searchResults.isEmpty && !searchText.isEmpty {
// Aquí brilla ContentUnavailableView
ContentUnavailableView(
"No hay resultados para '\(searchText)'",
systemImage: "doc.text.magnifyingglass",
description: Text("Intenta buscar con otros términos o verifica la ortografía.")
)
} else {
List(searchResults, id: \.self) { result in
Text(result)
}
}
}
.navigationTitle("Buscar Notas")
.searchable(text: $searchText)
}
}
}
En este bloque de código Swift, ContentUnavailableView toma tres parámetros clave:
- Title (Título): Una cadena de texto clara y concisa.
- System Image (Imagen del Sistema): Un ícono de SF Symbols que refuerza el mensaje visualmente.
- Description (Descripción): Un texto secundario que proporciona más contexto o instrucciones.
El motor de SwiftUI automáticamente aplicará el peso de fuente correcto al título, usará un color gris/secundario para la descripción y escalará el ícono proporcionalmente.
Vistas Avanzadas: Añadiendo Acciones y Personalización
Como iOS Developer, a menudo necesitarás ir más allá de un simple mensaje. Querrás que el usuario interactúe y solucione el estado vacío. ContentUnavailableView está diseñado con ViewBuilders para ofrecer máxima flexibilidad.
Supongamos que tu aplicación requiere una conexión a internet para descargar un feed de noticias, pero la solicitud de red falla. Aquí te mostramos cómo implementar un estado vacío interactivo:
import SwiftUI
struct NewsFeedView: View {
@State private var isOffline = true
@State private var isRetrying = false
var body: some View {
if isOffline {
ContentUnavailableView {
Label("Sin Conexión a Internet", systemImage: "wifi.exclamationmark")
} description: {
Text("No hemos podido cargar las noticias recientes. Por favor, verifica tu conexión e intenta nuevamente.")
} actions: {
Button(action: {
retryConnection()
}) {
if isRetrying {
ProgressView()
} else {
Text("Reintentar")
}
}
.buttonStyle(.borderedProminent)
}
} else {
// Tu vista de contenido aquí
Text("Feed de Noticias Cargado")
}
}
func retryConnection() {
isRetrying = true
// Lógica de red simulada
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
isRetrying = false
// isOffline = false (si la conexión es exitosa)
}
}
}
Analizando los ViewBuilders:
label: Reemplaza el título estático por cualquier vista. Usar unLabelaquí es la práctica estándar, ya que combina un título y un ícono.description: Permite usar texto con formato avanzado (como negritas parciales o enlaces) o incluso pequeñas vistas adicionales.actions: El lugar perfecto para colocar botones. SwiftUI apilará estos botones de manera vertical y con el espaciado correcto según las guías de diseño de la interfaz humana de Apple (HIG).
Estados predefinidos en SwiftUI
Apple ha incluido un estado predefinido específico para las búsquedas, ya que es uno de los casos de uso más comunes. Si solo necesitas un estado de “Búsqueda sin resultados” genérico, puedes usar el inicializador estático:
ContentUnavailableView.search
También puedes proporcionar el texto que se estaba buscando para que el sistema genere automáticamente el mensaje adecuado:
ContentUnavailableView.search(text: "Recetas de cocina")
Esto generará una interfaz completamente traducida al idioma del dispositivo del usuario de forma automática, mostrando un ícono de lupa y el texto: “No hay resultados para ‘Recetas de cocina'”. ¡Esto ahorra muchísimo tiempo en la programación Swift!
Magia Multiplataforma: iOS, macOS y watchOS
Una de las grandes ventajas de SwiftUI es su paradigma de “escribe una vez, aplica en cualquier lugar”. La implementación de Empty States con ContentUnavailableView en SwiftUI es el ejemplo perfecto de esto.
En iOS
En el iPhone o iPad, ContentUnavailableView ocupa el centro de la pantalla disponible. Si se encuentra dentro de un NavigationStack, respetará la barra de navegación grande o pequeña (inline), ajustando sus márgenes dinámicamente para que visualmente se sienta en el “centro óptico” de la pantalla, que suele estar ligeramente más arriba que el centro matemático.
En macOS
Cuando compilas el mismo código en Xcode para un Mac, el comportamiento cambia sutilmente para adaptarse a la experiencia de escritorio. Los íconos y las fuentes se escalan para coincidir con el diseño tipográfico de macOS (que suele ser más pequeño y nítido). Además, si tienes este estado vacío en el panel de detalle de un NavigationSplitView (muy común en apps de Mac), el fondo se adaptará a la translucidez típica de macOS.
En watchOS
El Apple Watch presenta un desafío único debido al tamaño de su pantalla. Un diseño de estado vacío con ícono, título, descripción y botón podría desbordar la pantalla del reloj. Sin embargo, al usar ContentUnavailableView, SwiftUI colapsa los márgenes, reduce dramáticamente el tamaño del systemImage y asegura que el texto se adapte y sea legible. Como desarrollador, no tienes que usar un sinfín de etiquetas #if os(watchOS); el framework toma la decisión correcta por ti.
Mejores prácticas para el diseño de Empty States
Para asegurar que estás aprovechando al máximo esta herramienta en tus proyectos de Xcode, ten en cuenta las siguientes recomendaciones:
- No culpes al usuario: Si ocurre un error, mantén un tono amigable. En lugar de “Error del usuario: Búsqueda inválida”, utiliza “No encontramos lo que buscas”.
- Usa SF Symbols descriptivos: El ecosistema de Apple incluye miles de íconos. No te limites a la lupa o al signo de exclamación. Usa un ícono de “bandeja” para listas vacías o un ícono de “silueta de persona” para una lista de contactos vacía.
- Ofrece una salida: Siempre que sea posible, incluye el bloque
actions. Un estado vacío sin botón es un callejón sin salida en el flujo de navegación. Ofrece botones como “Crear nuevo”, “Refrescar” o “Explorar sugerencias”. - Cuidado con los parpadeos (Flickering): Al solicitar datos de red, asegúrate de tener un estado de carga (loading) separado del estado vacío. Si pasas directamente a evaluar la variable (que inicialmente está vacía), el usuario verá un
ContentUnavailableViewdurante una fracción de segundo antes de que aparezcan los datos.
Conclusión
Dominar los Empty States con ContentUnavailableView en SwiftUI es un paso esencial para madurar como iOS Developer. La programación Swift ha llegado a un punto donde el código necesario para crear interfaces hermosas e interactivas es mínimo, permitiéndonos enfocarnos en la lógica de negocio y en la experiencia del usuario.







