Swift Charts es el framework nativo de Apple para visualización de datos en SwiftUI, disponible desde iOS 16. Sustituye a las librerías de terceros para la mayoría de casos de uso y se integra de forma natural con el sistema de diseño de SwiftUI, incluyendo modo oscuro, Dynamic Type y accesibilidad sin configuración adicional.
La estructura básica: Chart y Mark
Un gráfico en Swift Charts se compone de un contenedor Chart y uno o más tipos de marca (mark). Cada marca representa un elemento visual: barra, línea, punto, área o regla:
import Charts
import SwiftUI
struct VentasMensuales: View {
let datos = [
(mes: "Ene", ventas: 120),
(mes: "Feb", ventas: 95),
(mes: "Mar", ventas: 180),
(mes: "Abr", ventas: 145),
(mes: "May", ventas: 210),
]
var body: some View {
Chart(datos, id: .mes) { item in
BarMark(
x: .value("Mes", item.mes),
y: .value("Ventas", item.ventas)
)
}
.frame(height: 300)
.padding()
}
}
BarMark, LineMark, PointMark y AreaMark
Cada tipo de marca tiene su propio uso. BarMark para barras, LineMark para líneas temporales, PointMark para dispersión y AreaMark para resaltar áreas bajo una curva:
struct GraficoLineas: View {
let temperaturas = [
(dia: 1, ciudad: "Madrid", temp: 24.0),
(dia: 2, ciudad: "Madrid", temp: 27.0),
(dia: 3, ciudad: "Madrid", temp: 22.0),
(dia: 1, ciudad: "Barcelona", temp: 26.0),
(dia: 2, ciudad: "Barcelona", temp: 28.0),
(dia: 3, ciudad: "Barcelona", temp: 25.0),
]
var body: some View {
Chart(temperaturas, id: .dia) { item in
LineMark(
x: .value("Día", item.dia),
y: .value("Temperatura", item.temp)
)
.foregroundStyle(by: .value("Ciudad", item.ciudad))
PointMark(
x: .value("Día", item.dia),
y: .value("Temperatura", item.temp)
)
.foregroundStyle(by: .value("Ciudad", item.ciudad))
}
}
}
foregroundStyle(by:) asigna colores automáticamente a cada serie usando la paleta del sistema. La leyenda se genera sola.
AreaMark es especialmente útil para rangos:
Chart(datos, id: .dia) { item in
AreaMark(
x: .value("Día", item.dia),
yMin: .value("Mínima", item.minTemp),
yMax: .value("Máxima", item.maxTemp)
)
.opacity(0.3)
LineMark(
x: .value("Día", item.dia),
y: .value("Media", item.mediaTemp)
)
}
RuleMark: líneas de referencia
RuleMark dibuja una línea horizontal o vertical fija, ideal para marcar umbrales o medias:
Chart(datos, id: .mes) { item in
BarMark(
x: .value("Mes", item.mes),
y: .value("Ventas", item.ventas)
)
}
.chartOverlay { _ in }
// Línea de objetivo horizontal en 150
Chart {
ForEach(datos, id: .mes) { item in
BarMark(
x: .value("Mes", item.mes),
y: .value("Ventas", item.ventas)
)
}
RuleMark(y: .value("Objetivo", 150))
.lineStyle(StrokeStyle(lineWidth: 2, dash: [5]))
.foregroundStyle(.red)
.annotation(position: .top, alignment: .trailing) {
Text("Objetivo").font(.caption).foregroundStyle(.red)
}
}
Escalas personalizadas y ejes
Las escalas de los ejes se personalizan con los modificadores chartXScale, chartYScale, chartXAxis y chartYAxis:
Chart(datos, id: .mes) { item in
BarMark(
x: .value("Mes", item.mes),
y: .value("Ventas", item.ventas)
)
.foregroundStyle(item.ventas > 150 ? Color.green : Color.blue)
}
.chartYScale(domain: 0...300)
.chartYAxis {
AxisMarks(values: .stride(by: 50)) { valor in
AxisGridLine()
AxisValueLabel { Text("(valor.as(Int.self) ?? 0)") }
}
}
.chartXAxis {
AxisMarks { valor in
AxisValueLabel(orientation: .vertical)
}
}
Interactividad con chartOverlay
Para mostrar un tooltip o resaltar el punto seleccionado al tocar la gráfica, se usa chartOverlay para interceptar gestos y chartProxy para convertir coordenadas de pantalla a valores del dominio:
struct GraficoInteractivo: View {
let datos: [(mes: String, ventas: Int)]
@State private var seleccionado: String?
var body: some View {
Chart(datos, id: .mes) { item in
BarMark(
x: .value("Mes", item.mes),
y: .value("Ventas", item.ventas)
)
.opacity(seleccionado == nil || seleccionado == item.mes ? 1 : 0.4)
}
.chartOverlay { proxy in
GeometryReader { geo in
Rectangle()
.fill(.clear)
.contentShape(Rectangle())
.gesture(
DragGesture(minimumDistance: 0)
.onChanged { value in
let xPos = value.location.x - geo[proxy.plotAreaFrame].origin.x
if let mes: String = proxy.value(atX: xPos) {
seleccionado = mes
}
}
.onEnded { _ in seleccionado = nil }
)
}
}
}
}
Resumen
Swift Charts elimina la dependencia de librerías externas para visualización de datos en apps Apple. BarMark, LineMark, PointMark, AreaMark y RuleMark cubren la mayoría de tipos de gráfico; foregroundStyle(by:) gestiona series múltiples automáticamente; y chartOverlay con chartProxy añade interactividad sin código de geometría complejo. El resultado es un framework declarativo que se adapta al sistema de diseño de Apple sin configuración adicional.
