
Swift Charts y Acelerómetro
Extraer datos del acelerómetro del iPhone (o de cualquier otro dispositivo), es muy sencillo. Tan solo debemos importar el framework CoreMotion y crear una instancia de CMMotionManager. Aquí especificamos cada cuanto queremos obtener una muestra y de esta manera la printamos en un Chart en SwiftUI
Tabla de contenido

Hoy en SwiftBeta vamos a ver Swift Charts que ya vimos en el anterior video y esta vez en lugar de representar datos estáticos, vamos a representar datos en real time. Para ser más concreto, vamos a obtener información del acelerómetro del iPhone.
Pero ¿qué es un acelerómetro? tu dispositivo, en este caso un iPhone está compuesto por una barbaridad de tecnología, y hay un componente que se encarga de obtener información de la fuerza de aceleración o gravedad que tiene tu iphone en los distintos ejes: X, Y y Z. Si coges el iPhone y lo sacudes de golpe, verás que depende en el eje que lo hayas sacudido hay un valor más alto.
Es importante destacar que el código que implementemos deberá compilarse en un dispositivo físico, es decir, un dispositivo real. Ya que no podemos probar el acelerómetro en un simulador.
Si quieres apoyar el canal y que siga creando este tipo de contenido, suscríbete al canal!
Creamos proyecto en Xcode
Lo primero de todo que vamos hacer es crear nuestro proyecto en Xcode, recuerda seleccionar la opción de interfaz SwiftUI (ya que es el framework de UI que vamos a utilizar).
Una vez creado el proyecto, vamos a crear un fichero nuevo, nuestro ViewModel. Dentro de este ViewModel vamos a crear la lógica que obtendrá los datos de los diferentes ejes del acelerómetro de nuestro iPhone.
Una vez creado nuestro ViewModel importamos CoreMotion, este Framework nos va a conseguir los datos del acelerómetro. Y a continuación vamos a crear una struct que nos servirá como modelo de los datos que vamos a ir guardando en memoria:
struct DataModel: Identifiable {
let id: String = UUID().uuidString
let axis: String
var value: [Double]
mutating func add(_ newValue: Double) {
self.value.append(newValue)
}
}
Conformamos el protocolo Identifiable, ya que lo necesitaremos cuando usemos el ForEach en la vista. Y creamos dos propiedades, el eje en el que guardaremos el dato, que puede ser eje X, Y o Z. Y la última propiedad es un Array donde almacenaremos los valores del acelerómetro en ese eje.
Fíjate que también hemos creado un método que nos ayudará a añadir valores al array, es un método helper.
Vamos a continuar, dentro de nuestra clase ViewModel, vamos a crear una propiedad de tipo CMMotionManager. Este manager, este objeto nos obtendrá la información del acelerómetro. Vamos a crear también un init que llame al método que pondrá todo en marcha.
private let motionManager = CMMotionManager()
init() {
startAccelerometer()
}
private func startAccelerometer() {
// TODO:
}
Dentro del método startAccelerometer, vamos a meter la siguiente lógica:
private func startAccelerometer() {
guard motionManager.isAccelerometerAvailable else { // 1
print("Accelerometer data not available in this device")
return
}
motionManager.accelerometerUpdateInterval = 1/60 // 2
motionManager.startAccelerometerUpdates(to: .main) { accelerometerData, error in // 3
guard let error = error else {
self.updateProperties(with: accelerometerData) // 4
return
}
print("Error \(error.localizedDescription)") // 5
}
}
- Aquí comprobamos que el device en el que estamos probando nuestra app tiene un acelerómetro disponible. Si no lo tiene omitimos cualquier lógica, ya que no tendría sentido.
- Asignamos un valor a la propiedad accelerometerUpdateInterval para obtener muestras del acelerómetro. Un valor más alto indicaría obtener menos muestras (y la gráfica se dibujaría más lentamente)
- Llamamos al método startAccelerometerUpdate aquí es donde está toda la magia, ya que este método se va a llamar indefinidamente y va a ir obteniendo información de los 3 ejes del acelerómetro. Si no tenemos error, pasamos esta información a un método que crearemos justo a continuación.
- Si tenemos un error lo printamos por consola
Ahora, el compilador se queja, debemos crear el método updateProperties, pero anter vamos a crear 4 propiedades. Esta propiedas nos van a servir para almacenar los valores de cada eje del acelerómetro, y refrecar nuestro Chart con la nueva información:
@Published var data: [DataModel] = []
private var x: DataModel = .init(axis: "x", value: [])
private var y: DataModel = .init(axis: "y", value: [])
private var z: DataModel = .init(axis: "z", value: [])
Una vez hemos creado las propiedades, vamos a crear el método updateProperties del que se queja el compilador de Xcode:
private func updateProperties(with accelerometerData: CMAccelerometerData?) {
if let data = motionManager.accelerometerData {
let (x, y, z) = (data.acceleration.x, data.acceleration.y, data.acceleration.z)
self.x.add(x)
self.y.add(y)
self.z.add(z)
self.data = [self.x, self.y, self.z]
print("Eje X \(self.x)")
print("Eje Y \(self.y)")
print("Eje Z \(self.z)")
}
}
- Este método es muy sencillo, accede a los valores de la aceleración en cada eje
- Añade el valor de cada eje en las propiedades que acabamos de crear
- Añade los valores en el Array data de tipo @Published. Cada cambio en esta propiedad hará que se dibuje en nuestro Chart en SwiftUI.
- He añadido unos printas para que cuando creemos la instancia de ViewModel, veas por consola los valores del acelerómetro.
Empezamos a monitorizar valores del acelerómetro
Ahora vamos a nuestro ContentView y vamos a crear una instancia de ViewModel.
struct ContentView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
}
}
}
Al compilar, deberás ver por consola un montón de valores de los 3 ejes del acelerómetro. Ahora toca crear la parte visual, vamos a crear un Chart para representar estos datos de una manera que seamos capaces de entender qué ocurre.
import Charts
struct ContentView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
Label("Acelerómetro del iPhone", systemImage: "sensor.fill")
.font(.system(size: 20, weight: .bold, design: .rounded))
Chart(viewModel.data) { data in
ForEach(Array(data.value.enumerated()), id: \.offset) { index, element in
LineMark(x: .value("Index", index),
y: .value("Value", element))
}
.foregroundStyle(by: .value("Axis", data.axis))
}
.frame(height: 400)
}
}
Y si compilamos vemos como a medida que obtenemos datos del acelerómetro, aparecen en los distintos ejes, cada uno con su color y valor.

Reset de los datos del acelerómetro
Ahora vamos a crear un Button para que cuando lo pulsemos, todos los valores almacenados en memoria sean liberados.
Justo debajo de nuestro Chart añadimos el Button:
import Charts
struct ContentView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
Label("Acelerómetro del iPhone", systemImage: "sensor.fill")
.font(.system(size: 20, weight: .bold, design: .rounded))
Chart(viewModel.data) { data in
ForEach(Array(data.value.enumerated()), id: \.offset) { index, element in
LineMark(x: .value("Index", index),
y: .value("Value", element))
}
.foregroundStyle(by: .value("Axis", data.axis))
}
.frame(height: 400)
Button("Reset", action: {
viewModel.removeData()
})
.foregroundColor(.green)
.buttonStyle(.bordered)
}
}
Ahora vamos a nuestro ViewModel para crear el método removeData:
func removeData() {
data.removeAll()
x = .init(axis: "x", value: [])
y = .init(axis: "y", value: [])
z = .init(axis: "z", value: [])
}
Ahora si compilamos vemos como al pulsar el Button la información de nuestro Chart se resetea.
Y por seguridad, podéis añadir una condición en el método updateProperties para que llegado un gran número de muestras, se resetee automáticamente, es decir:
private func updateProperties(with accelerometerData: CMAccelerometerData?) {
if let data = motionManager.accelerometerData {
let (x, y, z) = (data.acceleration.x, data.acceleration.y, data.acceleration.z)
self.x.add(x)
self.y.add(y)
self.z.add(z)
if self.x.value.count > 300 {
removeData()
} else {
self.data = [self.x, self.y, self.z]
}
}
}
Conclusión
Hoy hemos aprendido a extraer información de nuestro dispositivo. En este caso ha sido un iPhone. Y la información que hemos extraido ha sido del acelerómetro.