Fitness App iOS Swift Tutorial iOS8

iOS Swift Tutorial: Fitness tracking and iOS charts

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

images

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)
  • In the identity inspector set the type to ChartViewControllerimages
  • Add a button dismiss to the viewcontroller
  • Create a IBAction 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
    images
  • Select the UIView go to Identity inspector and change the Class to BarChartView.
    images
  • 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()

  • 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()
                                })

                            }
                        }
                    }
                    
                }
                
            })
        }
    }

}

Most of the code in the viewDidLoad is setting up the chart view to display the graph. On line 115 we create a serial queue. The nil indicated that we are creating a serial queue. Line 120-125 we just fetch the pedometer data grouped by each day. The self.days variable will hold the day for which we will be showing the step count and the self.stepsTaken will have the actual count. Once we have fetched all the data (when the self.days.count == 0). We setup the data for the chartView and reloadInputViews in the main queue. You might be wondering why we didn’t execute on the queue which we created, the reason being any update to the View should be done in the main queue.

About the author

Shrikar

Backend/Infrastructure Engineer by Day. iOS Developer for the rest of the time.

  • http://lm-infosec.com/ Inno Orikiiriza

    Hi Shrikar,

    Great work done, and I’m following your tutorials. May I ask whether it’s approved by Apple to submit Apps to the app store completely built based on Swift language?
    Secondly, do you have quick tips for one looking at improving on auto layout skills and same time developing models or the business logic of the app? Any recommended textbooks, I’m currently using “Programming iOS 8 by Neuburg”, any other better highly rated latest textbooks you could recommend, Sir?

    Thanks and Regards,

    Inno

    • http://shrikar.com shrikar

      Yes they approve both my apps are completely written in Swift. If you search for autolayout on this site you get multiple autolayout example. Also take a look at this video

      https://www.youtube.com/watch?v=E3glNbNnokw

      • http://lm-infosec.com/ Inno Orikiiriza

        Thanks Shrikar. I can now confidently proceed learning Swift to future project development purposes. Cheers!

      • http://lm-infosec.com Inno Orikiiriza

        Thanks a lot

  • Wayne Bruton

    Hi Shrikar,

    Loved this tutorial. Trying to do one using a radar chart, do you have a tutorial using a radar chart, I am really struggling

    Kind regards

    Wayne

  • Eduardo Oliveros Acosta

    look this video to customize charts https://www.youtube.com/watch?v=p_bp8gH1odg

/* ]]> */