In the previous tutorial, we found how to use the CMPedometer and CMActivityManager to fetch the fitness tracking metrics like number of steps and the current activity state.

This is how the graph will look

Let us look at the change in the main storyboard from the previous tutorial

First create a new file called ChartViewController.Swift which is a subtype of UIViewController.

(File New -> iOS Source -> Cocoa Touch Class)

(File New -> iOS Source -> Cocoa Touch Class) In the identity inspector set the type to ChartViewController Go to top

Add a button dismiss to the viewcontroller

to the viewcontroller Create a IBAction from dismiss to the ChartViewController( Use the CTRL+Drag trick. CTRL+DRAG from dismiss button to the ChartViewController)

from dismiss to the ChartViewController( Use the CTRL+Drag trick. CTRL+DRAG from dismiss button to the ChartViewController) Drag a UIView from the object library on to the ChartViewController and setup the autolayout constraints as below



Select the UIView go to Identity inspector and change the Class to BarChartView .



CTRL+DRAG from uiview on to the ChartViewController and create a IBOutlet with name as chartView

What would be really nice is if we can track how many steps we have taken over the last 7 days. We will be using pedoMeter.queryPedometerDataFromDate(fromDate, toDate: toDate) over the last 7 days and group by the day to find how many steps we have been taking daily.

Since queryPedoMeter is async and we need to fetch data over the last 7 days it’s quite possible that the data might not be fetched by the time the view is loaded. One way to get around this problem is to reload the views when all the data is fetched.

One key point to note is that we should not be performing any operations which are time consuming in the main thread which handle all the touch events, otherwise the app will become sluggish. Apple has provided us with a mechanism to get around this problem using Grand Central Dispatch.

Some of the feature provided by GCD aka(Grand Central Dispatch) are queues and blocks .

There are different type of queues

Main Queue :

The main queue is automatically created by the system and associated with your application’s main thread. Your application uses one (and only one) of the following three approaches to invoke blocks submitted to the main queue: Calling dispatch_main Calling UIApplicationMain (iOS) or NSApplicationMain (OS X) Using a CFRunLoopRef on the main thread We can get access to the main queue by using dispatch_get_main_queue()

The main queue is automatically created by the system and associated with your application’s main thread. Your application uses one (and only one) of the following three approaches to invoke blocks submitted to the main queue: Global Concurrent Queue

Serial Queue

Global Concurrent queue and Serial queue differ in the way the blocks are processed. As you may have guessed Concurrent queue execute in parallel where as in Serial Queue the blocks are processed in the way they are submitted to the queue.

There are different ways in which blocks can be submitted to either of these queues. There are two main types dispatch_async and dispatch_sync .

dispatch_async submits the blocks and returns immediately where as dispatch_sync will wait till the block is completed.

Once we fetch the data we need to show the graph of the trend. We will be using iOS Charts to display a bar graph.

Add it to the podfile and perform pod install . Does pod install sounds like greek and latin to you?

If so please take a look at this tutorial before continuing Pod dependency manager

Lets looks at the code.

// // ChartViewController.swift // Steps // // Created by Shrikar Archak on 4/11/15. // Copyright (c) 2015 Shrikar Archak. All rights reserved. // import UIKit import Charts import CoreMotion class ChartViewController: UIViewController, ChartViewDelegate { @IBOutlet weak var chartView: BarChartView! var days:[String] = [] var stepsTaken:[Int] = [] let activityManager = CMMotionActivityManager() let pedoMeter = CMPedometer() var cnt = 0 override func viewDidLoad() { super.viewDidLoad() chartView.delegate = self; chartView.descriptionText = ""; chartView.noDataTextDescription = "Data will be loaded soon." chartView.drawBarShadowEnabled = false chartView.drawValueAboveBarEnabled = true chartView.maxVisibleValueCount = 60 chartView.pinchZoomEnabled = false chartView.drawGridBackgroundEnabled = true chartView.drawBordersEnabled = false getDataForLastWeek() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } @IBAction func dismiss(sender: AnyObject) { self.dismissViewControllerAnimated(true, completion: nil) } func getDataForLastWeek() { if(CMPedometer.isStepCountingAvailable()){ var serialQueue : dispatch_queue_t = dispatch_queue_create("com.example.MyQueue", nil) let formatter = NSDateFormatter() formatter.dateFormat = "d MMM" dispatch_sync(serialQueue, { () -> Void in let today = NSDate() for day in 0...6{ let fromDate = NSDate(timeIntervalSinceNow: Double(-7+day) * 86400) let toDate = NSDate(timeIntervalSinceNow: Double(-7+day+1) * 86400) let dtStr = formatter.stringFromDate(toDate) self.pedoMeter.queryPedometerDataFromDate(fromDate, toDate: toDate) { (data : CMPedometerData!, error) -> Void in if(error == nil){ println("\(dtStr) : \(data.numberOfSteps)") self.days.append(dtStr) self.stepsTaken.append(Int(data.numberOfSteps)) println("Days :\(self.days)") println("Steps :\(self.stepsTaken)") if(self.days.count == 7){ dispatch_sync(dispatch_get_main_queue(), { () -> Void in let xVals = self.days var yVals: [BarChartDataEntry] = [] for idx in 0...6 { yVals.append(BarChartDataEntry(value: Float(self.stepsTaken[idx]), xIndex: idx)) } println("Days :\(self.days)") println("Steps :\(self.stepsTaken)") let set1 = BarChartDataSet(yVals: yVals, label: "Steps Taken") set1.barSpace = 0.25 let data = BarChartData(xVals: xVals, dataSet: set1) data.setValueFont(UIFont(name: "Avenir", size: 12)) self.chartView.data = data self.view.reloadInputViews() }) } } } } }) } } }