Search code examples
swiftfirebasefirebase-realtime-databasecore-motion

Core Motion Pedometer Save to Firebase


Within this code is a working pedometer which shows the speed of movement and the current steps. What I'm wondering is how do I save my steps to my firebase database? My IBAction Button Save does submit to my firebase database, but it submits the number 0. How can I submit the number that the stepCountlabel shows?

import UIKit
import CoreMotion
import Dispatch
import Firebase

class PedometerViewController: UIViewController {

// Declarations of Pedometer
private let activityManager = CMMotionActivityManager()
private let pedometer = CMPedometer()
private var shouldStartUpdating: Bool = false
private var startDate: Date? = nil
var stepNumberVule: Int = 0 // FIREBASE CODE

// Storyboard connections
@IBOutlet weak var startButton: UIButton!
@IBOutlet weak var stepsCountLabel: UILabel!
@IBOutlet weak var activityTypeLabel: UILabel!

// FIREBASE CODE - I WANT TO WORK - ADD STEPS TO FIREBASE DATABASE
func getPedValue() {
    var ref: DatabaseReference!
    ref = Database.database().reference()
    let userID = Auth.auth().currentUser?.uid
    ref.child("user").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let ped:Int = value?["pedometer"] as? Int ?? 0
 self.stepNumberVule = ped
 self.stepsCountLabel.text = ("\(self.stepNumberVule)")
    }) { (error) in
        print(error.localizedDescription)
    }
    }

@IBAction func Save(_ sender: Any) {
    var ref: DatabaseReference!
    ref = Database.database().reference()
    let user = Auth.auth().currentUser!.uid
    let key = ref.child("user").child(user)
    ref.child("user/\(user)/pedometer").setValue(self.stepNumberVule)

}
// END OF FIREBASE CODE


// Do any additional setup after loading the view.
override func viewDidLoad() {
    super.viewDidLoad()
    startButton.addTarget(self, action: #selector(didTapStartButton), for: .touchUpInside)
}
// Do additional tasks associated with presenting the view
    override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    guard let startDate = startDate else { return }
    updateStepsCountLabelUsing(startDate: startDate)
}
// Go! button tapped
    @objc private func didTapStartButton() {
    shouldStartUpdating = !shouldStartUpdating
    shouldStartUpdating ? (onStart()) : (onStop())
}
}

  // Pedometer Extension and its private fuctions (functions are       explained explicitly so no need for comments)
  extension PedometerViewController {
private func onStart() {
    startButton.setTitle("Stop", for: .normal)
    startDate = Date()
    checkAuthorizationStatus()
    startUpdating()
}

private func onStop() {
    startButton.setTitle("Start", for: .normal)
    startDate = nil
    stopUpdating()
 }

private func startUpdating() {
    if CMMotionActivityManager.isActivityAvailable() {
        startTrackingActivityType()
    } else {
        activityTypeLabel.text = "Not available"
 }

    if CMPedometer.isStepCountingAvailable() {
        startCountingSteps()
    } else {
        stepsCountLabel.text = "Not available"
    }
 }

private func checkAuthorizationStatus() {
    switch CMMotionActivityManager.authorizationStatus() {
    case CMAuthorizationStatus.denied:
        onStop()
        activityTypeLabel.text = "Not available"
        stepsCountLabel.text = "Not available"
    default:break
    }
 }

private func stopUpdating() {
    activityManager.stopActivityUpdates()
    pedometer.stopUpdates()
    pedometer.stopEventUpdates()
 }

private func on(error: Error) {
    //handle error
 }

// Update step count
private func updateStepsCountLabelUsing(startDate: Date) {
    pedometer.queryPedometerData(from: startDate, to: Date()) {
        [weak self] pedometerData, error in
        if let error = error {
            self?.on(error: error)
        } else if let pedometerData = pedometerData {
            DispatchQueue.main.async {
                self?.stepsCountLabel.text = String(describing: pedometerData.numberOfSteps)
            }
        }
    }
  }
// Activity type
private func startTrackingActivityType() {
    activityManager.startActivityUpdates(to: OperationQueue.main) {
        [weak self] (activity: CMMotionActivity?) in
        guard let activity = activity else { return }
        DispatchQueue.main.async {
            if activity.walking {
                self?.activityTypeLabel.text = "You're walking, go faster!"
            } else if activity.stationary {
                self?.activityTypeLabel.text = "You're still, stop being lazy"
            } else if activity.running {
                self?.activityTypeLabel.text = "You're running, nice one!"
            } else if activity.automotive {
                self?.activityTypeLabel.text = "Driving doesnt count..."
            }
        }
    }
}
// Start counting steps
private func startCountingSteps() {
    pedometer.startUpdates(from: Date()) {
        [weak self] pedometerData, error in
        guard let pedometerData = pedometerData, error == nil else { return }

        DispatchQueue.main.async {
            self?.stepsCountLabel.text = pedometerData.numberOfSteps.stringValue
        }
    }
}

Solution

  • So you have global variable stepNumberVule and every time when your Save action gets called, you're saving the value of this variable to the Firebase, but the problem here is that you're not updating this value in the updateStepsCountLabelUsing(startDate: Date) or startCountingSteps(). If you want to save the exact number of steps, you should update the stepNumberVule from pedometer data.

    E.g:

    Create function

    private func updateStepCounterValue(_ numberOfSteps: NSNumber) {
        stepNumberVule = numberOfSteps.intValue
        stepsCountLabel.text = numberOfSteps.stringValue
    }
    

    Then, call it to update your label and variable inside:

    // Start counting steps
    private func startCountingSteps() {
        pedometer.startUpdates(from: Date()) {
            [weak self] pedometerData, error in
    
            guard let pedometerData = pedometerData, error == nil else { return }
            DispatchQueue.main.async {
                 self?.updateStepCounterValue(pedometerData.numberOfSteps)
            }
        }
    }
    

    and

    private func updateStepsCountLabelUsing(startDate: Date) {
        pedometer.queryPedometerData(from: startDate, to: Date()) {
            [weak self] pedometerData, error in
    
            if let error = error {
                self?.on(error: error)
            } else if let pedometerData = pedometerData {
                DispatchQueue.main.async {
                    self?.updateStepCounterValue(pedometerData.numberOfSteps)
                }
            }
        }
    }
    

    In this way, your stepNumberVule will always be up to date with your current data from the pedometer.