I am having trouble outputting the data for the magnetometer, accelerometer and gyroscope using Core Motion with SwiftUI. I'm assuming my problem has something to do with the startMagnetometerUpdates().
I've tried using source code found here on stack overflow, as well as on GitHub/google. The problem is all the code I'm finding uses UIKit instead of SwiftUI. Is it possible to implement this without using UIKit?
import CoreMotion
let motionManager = CMMotionManager()
var x = 0.0; var y = 0.0; var z = 0.0
func magnet() {
motionManager.magnetometerUpdateInterval = 1/60
motionManager.startMagnetometerUpdates()
if let magnetometerData = motionManager.magnetometerData {
x = magnetometerData.magneticField.x
y = magnetometerData.magneticField.y
z = magnetometerData.magneticField.z
}
}
struct Magnetometer: View {
var body: some View {
VStack {
Text("Magnetometer Data")
Text("X: \(x)")
Text("Y: \(y)")
Text("Z: \(z)")
}
}
}
struct Magnetometer_Previews: PreviewProvider {
static var previews: some View {
Magnetometer()
}
}
The output should just display the x, y and z values for the sensor and update on an interval of 1/60. The current output is 0.00000 for every value, which is because I set each variable to 0 already.
You have a couple of problems with your code.
Your first problem is that you need a binding between your model data and your view - By creating a binding, the view will be updated automatically when the model changes.
The second problem is that you are only accessing the magnetometer data once via motionManager.magnetometerData
rather than setting up a closure to monitor updates via startMagnetometerUpdates(to:withHandler:)
.
You can use ObservableObject
from the Combine
framework and @ObservedObject
in your view to create the appropriate binding.
Start by creating a class to wrap your motion manager:
import Foundation
import Combine
import CoreMotion
class MotionManager: ObservableObject {
private var motionManager: CMMotionManager
@Published
var x: Double = 0.0
@Published
var y: Double = 0.0
@Published
var z: Double = 0.0
init() {
self.motionManager = CMMotionManager()
self.motionManager.magnetometerUpdateInterval = 1/60
self.motionManager.startMagnetometerUpdates(to: .main) { (magnetometerData, error) in
guard error == nil else {
print(error!)
return
}
if let magnetData = magnetometerData {
self.x = magnetData.magneticField.x
self.y = magnetData.magneticField.y
self.z = magnetData.magneticField.z
}
}
}
}
This class conforms to ObservableObject
and @Publish
es its three properties, x,y and z.
Simply assigning new values to these properties in the magnetometer update closure will cause the publisher to fire and update any observers.
Now, in your view, you can declare an @ObservedObject
for your motion manager class and bind the properties.
struct ContentView: View {
@ObservedObject
var motion: MotionManager
var body: some View {
VStack {
Text("Magnetometer Data")
Text("X: \(motion.x)")
Text("Y: \(motion.y)")
Text("Z: \(motion.z)")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(motion: MotionManager())
}
}
Note that you will need to pass an instance of MotionManager
in your SceneDelegate.swift
file:
let contentView = ContentView(motion: MotionManager())