8

I am trying to record the data from the iPhone's accelerometer (my own iPhone 5s) and set a label on the screen to that data using String(format: "%.2f", data) where data is the value for the specific axis I want to record. To do this I set up a CMMotionManager and began recording accelerometer data, and I have a timer that constantly updates the text in the label. However, I am getting an error from Xcode: "fatal error: unexpectedly found nil while unwrapping an Optional value". Here is the relevant code:

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    //if accelerometer can be used, start it
    if (motionManager.accelerometerAvailable) {

        motionManager.accelerometerUpdateInterval = 0.1

        motionManager.startAccelerometerUpdates()
        let timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
    }



}

func update () {
    if (motionManager.accelerometerActive) {

        accelX.text = String(format: "%.2f", motionManager.accelerometerData.acceleration.x)
    }

}

The error stops when I change the accelX.text assignment to a simple string, so I think the optional variable creating the error is something to do with the accelerometer. That's as far as I know, however, so if you have any suggestions, or if I'm doing it completely wrong and there's a better and easier way, I will definitely appreciate it if you help me out.

Airspeed Velocity
  • 40,491
  • 8
  • 113
  • 118
  • 1
    At which line do you get the error? I guess it's at the `accelX.text = ...` line? First things first, the `accelerometerData` member of your `motionManager` can be `nil`, so you must check for that first before assuming there is value to be displayed. Like this: `if let accelData = motionManager.accelerometerData { accelX.text = String(format: "%.2f", accelData.acceleration.x) }`. – Romain Jan 04 '15 at 18:15
  • Note that instead of manually using a `NSTimer`, you can also ask iOS to update you periodically with accelerometer data. Further info here: https://developer.apple.com/library/ios/documentation/CoreMotion/Reference/CMMotionManager_Class/index.html#//apple_ref/occ/instm/CMMotionManager/startAccelerometerUpdatesToQueue:withHandler:. – Romain Jan 04 '15 at 18:23
  • That fixed it, thanks! I have a couple questions though. Why is accelerometerData an optional value (i.e. why does it not always have a value when the timer and accelerometer are at the same speed (10 fps)), and how did you know it was an optional value? –  Jan 04 '15 at 18:26
  • Check the Apple docs for that specific property: https://developer.apple.com/library/ios/documentation/CoreMotion/Reference/CMMotionManager_Class/index.html#//apple_ref/occ/instp/CMMotionManager/accelerometerData. It says `var accelerometerData: CMAccelerometerData! { get }`. The `!` means Optional, and the description below says it all. I think that's because when you ask iOS to start monitoring for acceleration data, at the beginning that data is not initialized, and becomes instantiated after a fraction of a second, I guess. – Romain Jan 04 '15 at 18:30
  • By the way, more precisely, `!` means "explicitly unwrapped optional", which means it is a regular optional which can be used without unwrapping, like you did. However, it can still be `nil` (as you witnessed) and can still cause a runtime error if you don't check for it first. – Romain Jan 04 '15 at 18:39
  • What is the use of an explicitly unwrapped optional if it still has to be checked (requiring the same code as a normal ? optional)? –  Jan 04 '15 at 19:14
  • If you have more general questions like this you can always search for it on StackOverflow, or ask your own... Anyway, that's actually a good question, which has already been asked, and has got a few good answers: http://stackoverflow.com/questions/24006975/why-create-implicitly-unwrapped-optionals. – Romain Jan 04 '15 at 22:10

2 Answers2

12

NSHipster has a good article to talk about the core motion: http://nshipster.com/cmdevicemotion/

A better way to regularly update UI with motion data is to use the patter as shown in below:

if manager.accelerometerAvailable {
     manager.accelerometerUpdateInterval = 0.1
     manager.startAccelerometerUpdatesToQueue(NSOperationQueue.mainQueue()) {
     [weak self] (data: CMAccelerometerData!, error: NSError!) in
          accelX.text = String(format: "%.2f", data.acceleration.x)
     }
}
Loanb222
  • 841
  • 1
  • 11
  • 29
Xiaojun
  • 833
  • 6
  • 4
3

you can get value of x or y or z and print alert of value you want

motionManager.accelerometerUpdateInterval = 5.0
motionManager.startAccelerometerUpdates(to: OperationQueue.current!) { (data, error) in
        if let myData = data{
            print(myData)
            if myData.acceleration.y < -0.2{
                let alert = UIAlertController(title: "Message ", message: "new text", preferredStyle: UIAlertControllerStyle.alert)
                alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
                self.present(alert, animated: true, completion: nil)

            }
        }
}
Roshana Pitigala
  • 8,437
  • 8
  • 49
  • 80
gihanshox
  • 56
  • 2