Saving HealthKit Data & Closures

iOS 8 HealthKit Santiapps Marcio Valenzuela
iOS 8 HealthKit

This code bit saves:

  healthKitStore.saveObject(bmiSample, withCompletion: { (success, error) -> Void in
    if( error != nil ) {
      println("Error saving BMI sample: \(error.localizedDescription)")
    } else {
      println("BMI sample saved successfully!")
    }
  })

The method signature is:

saveObject(object: HKObject!, withCompletion completion: ((Bool, NSError!) -> Void)!)

This method takes an HKObject which is bmiSample

and it takes a completion closure which itself takes a bool & error and returns void.

So in our method call, we pass in the bmiSample as the HKObject and for the success and error completion block we say:

if error is NOT nil then log that error’s description,

else log that the bmiSample was saved successfully.

 

 

This code bit reads:

// 2. Call the method to read the most recent weight sample
self.healthManager?.readMostRecentSample(sampleType, completion: { (mostRecentWeight, error) -> Void in
 
  if( error != nil )
  {
    println("Error reading weight from HealthKit Store: \(error.localizedDescription)")
    return;
  }
 
  var weightLocalizedString = self.kUnknownString;
  // 3. Format the weight to display it on the screen
  self.weight = mostRecentWeight as? HKQuantitySample;
  if let kilograms = self.weight?.quantity.doubleValueForUnit(HKUnit.gramUnitWithMetricPrefix(.Kilo)) {
    let weightFormatter = NSMassFormatter()
    weightFormatter.forPersonMassUse = true;
    weightLocalizedString = weightFormatter.stringFromKilograms(kilograms)
  }
 
  // 4. Update UI in the main thread
  dispatch_async(dispatch_get_main_queue(), { () -> Void in
    self.weightLabel.text = weightLocalizedString
    self.updateBMI()
 
  });
});

Here we are calling the .readMostRecentSample method which has this signature:

func readMostRecentSample(sampleType:HKSampleType , completion: ((HKSample!, NSError!) -> Void)!)

This method takes an HKSampleType which is sampleType

and it takes a completion closure which itself takes an HKSample & error and returns void.

So in our method call, we pass in the sampleType as the HKSample and for the success and error completion block we say:

if error is NOT nil then log that error’s description,

else get that mostRecentWeight as self.weight, format it and set it as the label’s text.

How does this method get the actual value from the HKHealthStore?  Inside itself, it executes an HKQuery which itself says this:

let sampleQuery = HKSampleQuery(sampleType: sampleType, predicate: mostRecentPredicate, limit: limit, sortDescriptors: [sortDescriptor])
    { (sampleQuery, results, error ) -> Void in
 
      if let queryError = error {
        completion(nil,error)
        return;
      }
 
      // Get the first sample
      let mostRecentSample = results.first as? HKQuantitySample
 
      // Execute the completion closure
      if completion != nil {
        completion(mostRecentSample,nil)
      }
  }
  // 5. Execute the Query
  self.healthKitStore.executeQuery(sampleQuery)

Give me:

a.  sampleType

b.  a predicate, limit, sortDescriptors

c. and a completion closure which takes a sampleQuery, results & error

such that;

if error it NOT nil, set the readMostRecentSample method’s completion closure to nil,error

otherwise set the completion closure with the results and no error.

Advertisements

HealthKit for iOS8: Part 7

iOS 8 HealthKit Santiapps Marcio Valenzuela
iOS 8 HealthKit

4. CoreData for other non-health stats

You made it to the end!  Ok, so we are basically going to be adding another store to our app and reading and writing data to THAT store as well.

First let’s add a new tab and make it a UITableViewController as well.  It will have dynamically populated cells.

HealthKit for iOS8
HealthKit for iOS8

Now embed it!

HealthKit for iOS8
HealthKit for iOS8

 

Your final storyboard should look like this:

HealthKit for iOS8
HealthKit for iOS8

 

Add a new Swift class called Swimming Data and set that new UITableViewController scene to its class.  Make that class file look like this:

import Foundation

import UIKit

class SwimmingData: UITableViewController {

}

Now we must add CoreData.  To do this we need to create a CoreData stack and a xcdatamodeld file.  First thing is first, let’s add the xcdatamodeld file by New->File->CoreData->DataModel.  Name it SwimModel.  Create an entity called Swim and add the following attributes:

  • pace : Int16
  • date : Date
  • laps : Int16
  • meters : Double
  • totalTime : Double

Now with the xcdatamodeld file selected, go to Editor and select Create NSManagedObject subclass:

HealthKit for iOS8
HealthKit for iOS8

 

Make sure SwimModel is selected, click Next, make sure to select the Swim entity, click Next and you should get a Swim.swift class like this:

import Foundation
import CoreData
class Swim: NSManagedObject {

@NSManaged var date: NSDate
@NSManaged var laps: NSNumber
@NSManaged var totalTime: NSNumber
@NSManaged var meters: NSNumber
@NSManaged var pace: NSNumber
}

Perfect!  All you need now is your stack!  To do this, again create a New->File->Source->Swift File-> and name it CoreDataStack.  Now replace everything in there with this:

import CoreData

class CoreDataStack {

let context:NSManagedObjectContext
let psc:NSPersistentStoreCoordinator
let model:NSManagedObjectModel
let store:NSPersistentStore?

 

init() {

//1

let bundle = NSBundle.mainBundle()

let modelURL = bundle.URLForResource("SwimModel", withExtension:"momd")

model = NSManagedObjectModel(contentsOfURL: modelURL!)!

 

//2

psc = NSPersistentStoreCoordinator(managedObjectModel:model)

 

//3

context = NSManagedObjectContext()

context.persistentStoreCoordinator = psc

 

//4

let documentsURL = applicationDocumentsDirectory()

let storeURL = documentsURL.URLByAppendingPathComponent("SwimFit4")

 

let options = [NSMigratePersistentStoresAutomaticallyOption: true]

 

var error: NSError? = nil

store = psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: options, error:&error)

 

if store == nil {

println("Error adding persistent store: \(error)")

abort()

}

}

 

func saveContext() {

var error: NSError? = nil

if context.hasChanges && !context.save(&error) {

println("Could not save: \(error), \(error?.userInfo)")

}

}

 

func applicationDocumentsDirectory() -> NSURL {

let fileManager = NSFileManager.defaultManager()

 

let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask) as [NSURL]

return urls[0]

}

}

Now since this is not a CoreData tutorial, I will not go into the details, but every CoreData project needs a MOM, MOC and PSC.  That is what we initialize here.

Now we can begin writing to our MOC and PSC.  To test run it, let’s hardcode a value.  Go to the SwimmingData Class and first give it an import CoreData at the top.  Now declare a property for your stack inside your class of course:

lazy var coreDataStack = CoreDataStack()

var workouts = NSMutableArray()

We are creating a CoreDataStack instance and we are creating a mutable array.

Then give it a viewDidLoad method like this:

 

override func viewDidLoad() {

super.viewDidLoad()

//Create Sample Swim object

var description = NSEntityDescription.entityForName("Swim", inManagedObjectContext:coreDataStack.context)

var sampleSwim = Swim(entity:description!, insertIntoManagedObjectContext:coreDataStack.context)

sampleSwim.laps = 24

sampleSwim.meters = 50

sampleSwim.totalTime = 40

sampleSwim.pace = 6

sampleSwim.date = NSDate()

coreDataStack.saveContext()

 

//Add object to array

self.workouts.addObject(sampleSwim)

 

//Refresh UI

self.tableView.reloadData()

}

Now add an identifier for the cell like so:

let JournalViewControllerTableViewCellReuseIdentifier: NSString = "Cell"

as a property at the top of the class.

So of course don’t forget to set the identifier in your storyboard scene.  Finally, implement both datasource methods:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

return self.workouts.count

}

override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell {

 

let cell: UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath:indexPath!) as UITableViewCell

 

var myWorkout: Swim = self.workouts[indexPath!.row] as Swim

let dateFormatter = NSDateFormatter()

dateFormatter.dateFormat = "yyyy'-'MM'-'dd HH':'mm':'ss"

let date = dateFormatter.stringFromDate(myWorkout.date as NSDate)

println(date)

cell.textLabel.text = date

 

cell.detailTextLabel!.text = myWorkout.totalTime.stringValue

 

return cell;

}

And don’t forget to set your cell type to Right Detail in your storyboard.  If you Build & Run and switch to the newly created tab, you might get a crash saying:

Unable to load class Swim …

This is because you need to fully qualify the class name in CoreData, so select your xcdatamodeld file and with your Swim entity selected, make sure to append the Class name in the inspector on the right like so:

HealthKit for iOS8
HealthKit for iOS8

Basically you need to ensure that you append your project name to the Class name field.

Now run your app and go over to the Workouts tab and see your hardcoded workout in the tableview.

Before we move on, let’s take a few minutes to work on some details.  While this provides the info required by the user, it would be nice to polish it up a bit.  First, we should add the letters “mins” to the totalTime displayed in the cell.  Second, it would be nice to format the date a little more such that its more human readable.  So go back to your cellForRowAtIndexPath and make the following changes:

override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell {

let cell: UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath:indexPath!) as UITableViewCell

var myWorkout: Swim = self.workouts[indexPath!.row] as Swim

var formatString = NSDateFormatter.dateFormatFromTemplate("EdMMM", options: 0, locale: NSLocale.currentLocale())

let dateFormatter = NSDateFormatter()

dateFormatter.dateFormat = formatString

let date = dateFormatter.stringFromDate(myWorkout.date as NSDate)

println(date)

cell.textLabel.text = date

cell.detailTextLabel!.text = myWorkout.totalTime.stringValue + " mins"

return cell;

}

There, now the user has a little mode detailed info of the data displayed.  We could go on and modify the cell to hold more data or even be selectable such that it would segue into a detail view controller to display all the info.

WRITING TO CORE DATA

Now all that is left to do is actually, remove that viewDidLoad code that writes to CoreData and instead, write to CoreData from our Workout view controller.  So back in WorkoutViewController, first import CoreData at the top, then add this property:

lazy var coreDataStack = CoreDataStack()

and finally, in the saveMyWorkout method, after we calculate our joules burned, or before, it doesn’t matter, add this code:

//E - Perhaps just store laps and meters per lap = total metes in some extra field within the SwimFit app to display it.

var description = NSEntityDescription.entityForName("Swim", inManagedObjectContext:coreDataStack.context)

var sampleSwim = Swim(entity:description!, insertIntoManagedObjectContext:coreDataStack.context)

var numberFormatter = NSNumberFormatter()

var nolaps:NSNumber? = numberFormatter.numberFromString(numberOfLapsValue!)

if let nolaps = nolaps {

sampleSwim.laps = Int(nolaps)

}

var nometers:NSNumber? = numberFormatter.numberFromString(metersPerLapValue!)

if let nometers = nometers {

sampleSwim.meters = Double(nometers)

}

var totime:NSNumber? = numberFormatter.numberFromString(workoutDurationValue!)

if let totime = totime {

sampleSwim.meters = Double(totime)

}

sampleSwim.pace = pace

sampleSwim.date = NSDate()

coreDataStack.saveContext()

This will save that other data, which is not HealthKit or health store data, into CoreData for later use.  Now let’s just go modify our SwimmingData view controller to make it fetch.

You already added a CoreDataStack variable to your SwimmingData view controller, so just add a fetchRequest var like this, right below the CoreDataStack var:

var coreDataStack: CoreDataStack!

var fetchRequest: NSFetchRequest!

Now in viewDidLoad add this neat code:

fetchRequest = coreDataStack.model.fetchRequestTemplateForName("FetchRequest")

This is a stored fetch request and to use it you must head over to the xcdatamodeld.file and create a new FetchRequest, leave its name as FetchRequest and now from the editor leave Swim as the selected entity to fetch from.  Now go back to SwimmingData and add this method:

//MARK - Helper methods

func fetchAndReload(){

var error: NSError?

let results = coreDataStack.context.executeFetchRequest(fetchRequest, error: &error) as [Swim]?

if let fetchedResults = results {

workouts = fetchedResults.copy() as NSMutableArray

} else {

println("Could not fetch \(error), \(error!.userInfo)")

}

tableView.reloadData()

}

And now call this method from viewDidLoad.  This will load your fetched data from CoreData into your tableview.

This will fetch the items in the order they were inserted, but you can also add a sort descriptor.

Add this lazy property at the top of your SwimmingData class:

lazy var dateSortDescriptor: NSSortDescriptor = {

var sd = NSSortDescriptor(key: "Swim.laps",

ascending: true) return sd
}()

Then in the viewDidLoad add this as a property of your fetchRequest:

fetchRequest.sortDescriptors =[dateSortDescriptors]

 NSFETCHEDRESULTSCONTROLLER OPTION

Alternatively you can also use NSFetchedResultsController.  NSFRC is a neat object that is created specifically for fetching and manipulating data from a CoreData query.  Its special in many respects but mainly because it works nicely with table views.  It can store information about table structure and can allow for interaction between its data and the tableview at the same time.  So add this property to the top of your class:

var fetchedResultsController : NSFetchedResultsController!

In your viewDidLoad:

//1
let fetchRequest = NSFetchRequest(entityName: "Swim")
let sortDescriptor = NSSortDescriptor(key: "totalTime", ascending: false) fetchRequest.sortDescriptors = [sortDescriptor] 
//2 fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: coreDataStack.context, sectionNameKeyPath: nil, cacheName: nil) 
//3 var error: NSError? = nil 
if (!fetchedResultsController.performFetch(&error)) { 
println("Error: \(error?.localizedDescription)") } 

Now replace your datasource methods with:

func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return fetchedResultsController.sections!.count
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let sectionInfo = fetchedResultsController.sections![section] as NSFetchedResultsSectionInfo
return sectionInfo.numberOfObjects
}

Finally, the way you access data to populate your cell in cellForRowAtIndexPath is:

let mySwim = fetchedResultsController.objectAtIndexPath(indexPath) as Swim

cell.textLabel.text = date
cell.detailLabel.text = mySwim.totalTime.stringValue + " mins"

}

where date would be the formatted value of course.

Well it has been a long journey.  I hope you learned enough about Healthkit to feel comfortable enough to start working on your own app.

Have a good one!

HealthKit for iOS8: Part 6

iOS 8 HealthKit Santiapps Marcio Valenzuela
iOS 8 HealthKit

Here is the start of our WorkOut view controller:

import Foundation
import UIKit
import HealthKit

class WorkoutViewController: UITableViewController, UITextFieldDelegate {

@IBOutlet var numberOfLapsTextField: UITextField!
@IBOutlet var metersPerLapTextField: UITextField!
@IBOutlet var workoutDurationTextField: UITextField!
@IBOutlet var paceTextField: UITextField!
var numberOfLapsValue: NSString?
var metersPerLapValue: NSString?
var workoutDurationValue: NSString?
var userWeight: Double?
var  healthStore:HKHealthStore?
}

We import what we need, we subclass UITableViewController and add the Text Field delegate protocol.  Here I have created 4 labels for:

  • numberOfLaps
  • metersPerLap
  • workoutDuration
  • pace

These labels have an underlying variable for each.  The reason the first 3 are strings is because these are not health kit data per se.  These will be stored in CoreData.  However they really should be NSNumbers because it would be quite nice to store them and take advantage of CoreData’s ability to retrieve ordered data and statistical data in its fetches as well.

Finally we declare our health store property.

First let’s look at our lifecycle methods:

override func viewDidLoad() {

super.viewDidLoad()

self.fetchUsersWeight()

}

func textFieldShouldReturn (textField: UITextField) -> (ObjCBool) {

textField.resignFirstResponder()

if self.numberOfLapsTextField != nil && self.metersPerLapTextField != nil && self.workoutDurationTextField != nil {

}

return true;

}

In viewDidLoad we call a fetchUsersWeight method because we will need that to calculate calories burned.  Then we implement textFieldShouldReturn for each label.  Let’s take a look at that first method called:

func fetchUsersWeight() -> () {

var todayPredicate: NSPredicate = self.predicateForSamplesToday()

var weightType: HKQuantityType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)

self.healthStore?.aapl_mostRecentQuantitySampleOfType(weightType, predicate: todayPredicate, completion: { (weight, error) -> () in

if weight == nil {

NSLog("Sorry, weight is empty...")

return

}

if let someWeight = weight {

var weightUnit: HKUnit = HKUnit.poundUnit()

self.userWeight = weight!.doubleValueForUnit(weightUnit)

}

})

}

Once again we make a fetch using the same extension method and helper predicate method as before.  This exemplifies the use of extensions because we need to include the predicate method in this Workout class, not so the extension.  So go ahead and add the predicate method now:

func predicateForSamplesToday () -> (NSPredicate) {

let calendar: NSCalendar = NSCalendar.currentCalendar()

let now: NSDate = NSDate()

let startDate: NSDate = calendar.startOfDayForDate(now)

let endDate: NSDate = calendar.dateByAddingUnit(.CalendarUnitDay, value:1, toDate:startDate, options:nil)!

return HKQuery.predicateForSamplesWithStartDate(startDate, endDate:endDate, options:HKQueryOptions.StrictStartDate)

}

Ok so the user has 2 options here: Done or Cancel.  Cancel is easy:

@IBAction func cancel(sender: AnyObject) -> () {

self.navigationController?.popViewControllerAnimated(true)

}

Now let’s take a look at Done:

@IBAction func saveMyWorkout(sender: AnyObject) -> () {

//1.  Enter the values for your workout & capture

numberOfLapsValue = numberOfLapsTextField.text

metersPerLapValue = metersPerLapTextField.text

workoutDurationValue = workoutDurationTextField.text

//A - Need to fetch the user's weight in kgs from healthstore

var myWeight: Double = self.userWeight! * 0.453

//B - Need to convert time worked out into hours

//C - Need to select pace from some sort of switch

var pace: Double = (paceTextField!.text as NSString).doubleValue

//D - Throw away numberOfLaps = (numberOfLapsValue! as NSString).doubleValue & (metersPerLapValue! as NSString).doubleValue

var totalCaloriesBurnedByWorkout: Double =  ( (myWeight * pace) ) * ((workoutDurationValue! as NSString).doubleValue)/60

var totalJoulesBurnedByWorkout: Double = totalCaloriesBurnedByWorkout*4.184

//1.5 Set to EnergyVC before saving

let energyVCInstance: EnergyViewController = self.navigationController!.viewControllers[0] as EnergyViewController

energyVCInstance.activeEnergyBurnedValueLabel?.text = NSString(format:"%.2f",totalJoulesBurnedByWorkout)

energyVCInstance.activeEnergyBurned = totalJoulesBurnedByWorkout

energyVCInstance.refreshControl?.endRefreshing()

//2.  Dismiss and set values on EnergyVC such that they can be saved to the healthStore

//Save object to healthstore

// MUST DEFINE HKQUANTITY_TYPE

var quantityType: HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned)

//MUST DEFINE HKQUANTITY

var quantity: HKQuantity = HKQuantity(unit: HKUnit.jouleUnit(), doubleValue:totalJoulesBurnedByWorkout)

//DATE & METADATA

var now: NSDate = NSDate()

var metadata: NSDictionary = ["HKMetadataKey":"Swim Session"]

//WORKOUT TYPE?

var workoutType = HKWorkoutActivityType.Swimming

//This creates the object to SAVE

var energyBurnt: HKQuantitySample = HKQuantitySample(type: quantityType, quantity:quantity, startDate:now, endDate:now, metadata:metadata)

NSLog("Before saving to the healthstore...")

self.healthStore?.saveObject(energyBurnt, withCompletion: { (success, error) in

NSLog("Saving to the healthstore...")

if (error != nil) {

NSLog("The error is: \(error)")

}

dispatch_async(dispatch_get_main_queue(), {

if success {

// This was for updating a uitableview

// Alert User

let alertController = UIAlertController(title: "Success!", message: "Data Saved", preferredStyle: UIAlertControllerStyle.Alert)

alertController.addAction(UIAlertAction(title: "Confirm", style: UIAlertActionStyle.Default, handler: {action in

println("confirm was tapped")

//dismiss this vc

self.navigationController?.popViewControllerAnimated(true)

}))

self.presentViewController(alertController, animated: true, completion: nil)

} else {

NSLog("An error occured saving your workout burn. In your app, try to handle this gracefully. The error was: %@.", error)

abort()

}

})

})

}

This time I take you step by step, once again, inside the same method so as to drive the point home.  First we take all necessary data from labels to make our calculations.  We make our calculation and populate totalJoulesBurnedByWorkout.

Then we set the properties in our EnergyViewController from here before we return.

And before returning, we save our data to the health store, creating an identifier, then a quantity, then a date, then some metadata and finally calling saveObject.  In the saveObject method we return an alertController for success and pop our Workout view controller, because remember this is a navigation stack, not a modally presented view controller.  Else we log an error.

In the final part, we will add CoreData to our project and save laps and meters data into CoreData as well.

C ya!

Catch up to Swift Programming @Udemy

Udemy & Santiapps: Learn C++, Swift, ObjC & Arduino: Marcio Valenzuela
Udemy & Santiapps

Swift is a new programming language from Apple which is quickly gaining track.  The folks at udemy bring you this very useful set of tips for those interested in Swift:

Speed Up your Swift Learning @Udemy Santiapps Marcio Valenzuela
Speed Up your Swift Learning

 

Here is some more great content this time on Swift programming from the guys @Udemy.  Enjoy these great tips!

https://www.udemy.com/swift-learn-apples-new-programming-language-by-examples/#tentips

HealthKit for iOS8: Part 5

iOS 8 HealthKit Santiapps Marcio Valenzuela
iOS 8 HealthKit

Jumping right in, our Energy view controller class starts out like this:

import Foundation

import UIKit

import HealthKit

class EnergyViewController: UITableViewController {

var energyFormatter: NSEnergyFormatter {

var energyFormatter: NSEnergyFormatter?

var onceToken: dispatch_once_t = 0

dispatch_once(&onceToken, {

energyFormatter = NSEnergyFormatter()

energyFormatter?.unitStyle = NSFormattingUnitStyle.Long

energyFormatter?.forFoodEnergyUse = true

energyFormatter?.numberFormatter.maximumFractionDigits = 2

})

return energyFormatter!

}

// No required initWithCoder pasted in...

var  healthStore:HKHealthStore?

@IBOutlet weak var activeEnergyBurnedValueLabel: UILabel?

@IBOutlet weak var restingEnergyBurnedValueLabel: UILabel?

@IBOutlet weak var consumedEnergyValueLabel: UILabel?

@IBOutlet weak var netEnergyValueLabel: UILabel?

var activeEnergyBurned: Double = 0

var restingEnergyBurned: Double = 0

var consumedEnergy: Double = 0

var netEnergy: Double = 0

}

We take care of our imports, subclass UITableViewController, create our NSEnergyFormatter again, our health store property and then we create 4 UILabel outlets to display the values and 4 Doubles to store our energy values to be displayed.  What we are going to do here is just as important as how.  We will have 3 values which will compute a 4th one.  The 4th value is Net Energy which could be positive if we eat more or negative if we exercise more.  The other 3 are:

  • Active Energy Burned which will be populated from any workout sessions.
  • Resting Energy Burned which will be calculated automatically for us based on our profile.
  • Consumed Energy which will be input by the user in the Journal via the FoodPicker.

Ok let’s cover the view controller lifecycle methods.  viewDidLoad will be empty because instead we will use viewWillAppear:

override func viewWillAppear(animated: Bool) {

super.viewWillAppear(animated)

self.refreshControl?.addTarget(self, action:"refreshStatistics", forControlEvents:UIControlEvents.ValueChanged)

self.refreshStatistics()

NSNotificationCenter.defaultCenter().addObserver(self, selector:"refreshStatistics", name:UIApplicationDidBecomeActiveNotification, object:nil)

}

override func viewDidDisappear(animated: Bool) {

NSNotificationCenter.defaultCenter().removeObserver(self, name:UIApplicationDidBecomeActiveNotification, object:nil)

}

Here we set our refreshControl to call the refreshStatistics method.  Then we also add ourselves as observer to notifications when the application becomes active.  As always, we clean up on exit when the view disappears.

Now the method that gets called on every viewWillAppear and you’ll understand why it couldn’t be placed in the viewDidLoad:

func refreshStatistics () -> () {

self.refreshControl?.beginRefreshing()

var energyConsumedType: HKQuantityType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDietaryEnergyConsumed)

var activeEnergyBurnType: HKQuantityType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned)

self.fetchSumOfSamplesTodayForType(energyConsumedType, unit:HKUnit.jouleUnit(), withCompletion: { (totalJoulesConsumed:Double, error:NSError?) -> () in

 

self.fetchSumOfSamplesTodayForType(activeEnergyBurnType, unit: HKUnit.jouleUnit(), withCompletion: { (activeEnergyBurned, error:NSError?) -> () in

self.fetchTotalBasalBurn({ (basalEnergyBurn, error) -> () in

if (basalEnergyBurn == nil) {

NSLog("An error occurred trying to compute the basal energy burn. In your app, handle this gracefully. Error: \(error)")

}

if (basalEnergyBurn != nil) {

// Update the UI with all of the fetched values.

dispatch_async(dispatch_get_main_queue(), {

self.activeEnergyBurned = activeEnergyBurned

self.activeEnergyBurnedValueLabel!.text = self.energyFormatter.stringFromJoules(self.activeEnergyBurned)

self.restingEnergyBurned = basalEnergyBurn!.doubleValueForUnit(HKUnit.jouleUnit())

self.restingEnergyBurnedValueLabel!.text = self.energyFormatter.stringFromJoules(self.restingEnergyBurned)

self.consumedEnergy = totalJoulesConsumed

self.consumedEnergyValueLabel!.text = self.energyFormatter.stringFromJoules(self.consumedEnergy)

self.netEnergy = self.consumedEnergy - self.activeEnergyBurned - self.restingEnergyBurned

self.netEnergyValueLabel!.text = self.energyFormatter.stringFromJoules(self.netEnergy)

self.refreshControl?.endRefreshing()

}) //END OF DISPATCH

}

}) //END OF self.fetchTotalBasalBurn

}) //END OF self.fetchSumOf2..

}) //END OF self.fetchSumOf1...

}

First we set our control to begin refreshing.  Next we create 2 type identifiers for DietaryEnergyConsumed and ActiveEnergyBurned.  Next we call 2 fetches, first for energyConsumedType and then for activeEnergyBurnType.  Then we throw in a basal burn calculation.  We will look at the basal or resting energy burn methods next but, just notice that if they return nil, we log an error, if its not nil, we set our labels to the values of the calculated energies.  Finally we end refreshing.

Ok so let’s look at the meaty methods.  First, the fetchSumOfSamplesTodayForType method:

func fetchSumOfSamplesTodayForType(quantityType:HKQuantityType, unit:HKUnit, withCompletion completion:((Double, NSError?) -> Void)? ) {

let predicate: NSPredicate = self.predicateForSamplesToday()

let query = HKStatisticsQuery(quantityType: quantityType, quantitySamplePredicate: predicate, options: .CumulativeSum) {query, result, error in

let sum = result?.sumQuantity()

if completion != nil {

let value: Double = sum?.doubleValueForUnit(unit) ?? 0.0    //NOTE: use 0.0 when sum is nil

completion!(value, error)

}

}

self.healthStore?.executeQuery(query)

}

Here we create a HKStatisticsQuery fetch which is a bit different from the regular HKQuery.  The reason is that we want to return the .CumulativeSum of all samples, not just the latest one.  We could fetch all samples for today but we would have to add them manually.  Since cumulative sums is quite common, HealthKit has a method for that.  The query returns a result and we set it to sum.  We then return that value in the completion handler.

Now the basal burn or resting energy burn is composed of 3 methods and an extension:

func fetchTotalBasalBurn( completion:(basalEnergyBurn: HKQuantity?, error: NSError?) -> ()?) {

var todayPredicate: NSPredicate = self.predicateForSamplesToday()

var weightType: HKQuantityType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)

var heightType: HKQuantityType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight)

self.healthStore?.aapl_mostRecentQuantitySampleOfType(weightType, predicate:nil, completion: { (weight, error) -> () in

if weight == nil {

completion(basalEnergyBurn: nil, error:error)

return

}

self.healthStore?.aapl_mostRecentQuantitySampleOfType(heightType, predicate: todayPredicate, completion: { (height, error) -> () in

if height == nil {

completion(basalEnergyBurn: nil, error:error)

return

}

var innerError: NSError?    //NOTE: this var receives errors from dateOfBirthWithError or biologicalSexWithError

var dateOfBirth: NSDate? = self.healthStore?.dateOfBirthWithError(&innerError)

if dateOfBirth == nil {

completion(basalEnergyBurn: nil, error:innerError)

return

}

var biologicalSexObject: HKBiologicalSexObject? = self.healthStore?.biologicalSexWithError(&innerError)

if biologicalSexObject == nil {

completion(basalEnergyBurn: nil, error:error)

return

}

var basalEnergyBurn: HKQuantity = self.calculateBasalBurnTodayFromWeight(weight!, height:height!, dateOfBirth:dateOfBirth, biologicalSex:biologicalSexObject)!

completion(basalEnergyBurn: basalEnergyBurn, error: nil)

})

})

}

It may look daunting…oh who are we kidding, it is.  And it only gets worse!

First we create type identifiers for BodyMass and height.  We also create a predicate calling a separate method which we will see later.  Next we fetch the most recent quantity from the health store.  You will recall we did the exact same thing in the Profile view controller.  We are doing the same thing here but using a helper method thats included in an extension that others can use, not just this method.  So first we ask the health store for the weightType, if nil we return nil in the completion handler.  Next we ask for the weight and ditto with the completion handler.  Next we ask for dateOfBirth and biologicalSex.  This is the same we way asked for DOB in the Profile view controller.  But we have to return a completion handler on both calls, so we do.  Finally we set our basal burn HKQuantity to be saved to the health store by calling the calculateBasalBurnTodayFromWeight and we pass in our values for height, weight, sex and dob.  Once again we return a fully qualified completion handler this time.

So let’s take a look at the calculateBasalBurnTodayFromWeight method:

func calculateBasalBurnTodayFromWeight(weight:HKQuantity?, height:HKQuantity?, dateOfBirth:NSDate?, biologicalSex:HKBiologicalSexObject?) -> (HKQuantity?) {

// Only calculate Basal Metabolic Rate (BMR) if we have enough information about the user

if weight == nil || height == nil || dateOfBirth == nil || biologicalSex == nil {

return nil

}

var lenghtUnit: HKUnit = HKUnit(fromString: "cm")

var someHeight = height?.doubleValueForUnit(lenghtUnit)

let heightInCentimeters: Double = someHeight!

let weightInKilograms: Double = weight!.doubleValueForUnit(HKUnit.gramUnitWithMetricPrefix(HKMetricPrefix.Kilo))

let now: NSDate = NSDate()

let ageComponents: NSDateComponents = NSCalendar.currentCalendar().components(.CalendarUnitYear, fromDate:dateOfBirth!, toDate:now, options:nil)

let ageInYears:Int = ageComponents.year

// BMR is calculated in kilocalories per day.

let BMR: Double = self.calculateBMRFromWeight(weightInKilograms, heightInCentimeters:heightInCentimeters, ageInYears:ageInYears, biologicalSex:(biologicalSex?.biologicalSex)!)

// Figure out how much of today has completed so we know how many kilocalories the user has burned.

let startOfToday:NSDate = NSCalendar.currentCalendar().startOfDayForDate(now)

let endOfToday:NSDate = NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitDay, value:1, toDate:startOfToday, options:nil)!

let secondsInDay: NSTimeInterval = endOfToday.timeIntervalSinceDate(startOfToday)

let percentOfDayComplete: Double = now.timeIntervalSinceDate(startOfToday) / secondsInDay

let kilocaloriesBurned: Double = BMR * percentOfDayComplete

return HKQuantity(unit: HKUnit.kilocalorieUnit(), doubleValue: kilocaloriesBurned)

}

First we make sure we have all the parameters needed to calculate basal or resting energy.  Remember, this is the energy burned by your body, just by carrying out its normal functions, breathing, metabolism etc.  No exercise is included here.

Then we create a unit for the height and format our height.  Ditto for our weight!  Next we calculate our age once again.  Finally we create a BMR variable which will be set by calling yet another method.   The BMR is Basal Metabolic Rate and it is calculated as a percentage of the day that has expired so far.  So basically you burn calories no matter what you do, unless you are dead.  In the morning your body will have burned a certain % of those calories.  As the day progresses, we burn more and so on and so forth.

This means we need to calculate how many seconds (NSTimeInterval) have passed since the beginning of the day.  This gives us a % of the day completed.  We then simply multiply that ratio by the BMR that a person with that weight, height, sex and age is supposed to have in a day and we get our result.

Finally we return an HKQuantity for the kilocalories burned according to the BMR and the % of day complete.

Here is a small method to take a break:

func predicateForSamplesToday () -> (NSPredicate) {

let calendar: NSCalendar = NSCalendar.currentCalendar()

let now: NSDate = NSDate()

let startDate: NSDate = calendar.startOfDayForDate(now)

let endDate: NSDate = calendar.dateByAddingUnit(.CalendarUnitDay, value:1, toDate:startDate, options:nil)!

return HKQuery.predicateForSamplesWithStartDate(startDate, endDate:endDate, options:HKQueryOptions.StrictStartDate)

}

What we are doing here is creating a start and end date for today instead of having to re-write that code everytime.

So how do you calculate BMR:

func calculateBMRFromWeight(weightInKilograms:Double, heightInCentimeters:Double, ageInYears:Int, biologicalSex:HKBiologicalSex) -> (Double) {

var BMR:Double

if (biologicalSex == HKBiologicalSex.Male) {

BMR = 66.0 + (13.8*weightInKilograms) + (5*heightInCentimeters) - (6.8*Double(ageInyears))

} else {

BMR = 655.0 + (9.6*weightInKilograms) + (1.8*heightInCentimeters) - (4.7*Double(ageInyears))

}

return BMR

}

It’s sex dependent and basically a simple linear formula.  Big whoop.  Ok.

Now let’s look at the meaty helper extension we saw earlier:

extension HKHealthStore {

func aapl_mostRecentQuantitySampleOfType(quantityType: HKQuantityType, predicate:NSPredicate?, completion:( (HKQuantity?, NSError?) -> () )? )  {

var timeSortDescriptor: NSSortDescriptor = NSSortDescriptor(key:HKSampleSortIdentifierEndDate, ascending:false)

var query =   HKSampleQuery(sampleType: quantityType, predicate: nil, limit: 1, sortDescriptors: [timeSortDescriptor]) { query, results, error in

if (results == nil) {

if (completion != nil) {

completion?(nil, error)

} //END OF IF 2...

return

} // END OF IF 1...

if ((completion) != nil) {

// If quantity isn't in the database, return nil in the completion block.

var resultados = results as NSArray

var quantitySample: HKQuantitySample = resultados.firstObject as HKQuantitySample

var quantity: HKQuantity = quantitySample.quantity

completion?(quantity, error)

} //END OF IF

} // HKSAMPLEQUERY

self.executeQuery(query)

} // END OF FUNCTION

} //END OF EXTENSION

We create a sort descriptor as we did in the Profile, because we want to order our results by the latest.  Then we create our simple HKQuery with whatever quantityType was passed in, the predicate, limit and sortDescriptor as well as the completion handler.  If results are nil, we set our completion handler value to nil and return.  Otherwise, we set our completion handler to whatever the most recent quantity fetched is because once again we take the firstObject from that results array.

Ok the only thing left here is to prepareForSegue:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

var workoutViewController: WorkoutViewController = segue.destinationViewController as WorkoutViewController//viewController as WorkoutViewController

workoutViewController.healthStore = self.healthStore!

}

The reason we segue is that we need to take the user to the point where he can enter the only value missing.  We already calculated the resting energy value internally based on the user’s profile data.  Then we calculated the consumed energy based on what the user picked as his or her consumed foods.  So now we need to know how much energy he burnt in his workout.

Race you there!

 

Implicitly Unwrapped Optionals

As an ObjC programmer, I despised the introduction of Optionals in Swift.

However, I jumped on the Swift bandwagon on Day 1.

So I understood the basic concepts:

  1. If a value may at some point not have a value, declare it as optional.
  2. If a value has been declared optional, you must test its contents.
    1. You can test with if == nil
    2. You can if let test
    3. Or you can force unwrap

I also read about implicitly unwrapped optionals but I confused them with force-unwrapping.  It also threw me off about when and what to use them for.

Recently I’ve been working on an app that involves a dance between a data holder (AppDelegate), a view (GraphView) and a view controller (GraphViewController).  Plus there is a data setter (Swimming) which kinda complicates things.  These view controllers reside in tabs in the app.  And the functionality works kinda like this:

Swift : Implicitly Unwrapped Optionals iOS8
Swift : Implicitly Unwrapped Optionals iOS8

At the start of the app, the AppDelegate creates an array of data to be plotted (graphArray).  This is needed such that if the user then automatically jumps to the GraphViewController, that array will have a value.

NOTE:True, this could also happen in the GraphViewController’s initWithCoder.  But since Im using the AppDelegate as the data holder, I decided to declare and assign it here.

The user then has the option to go to the SwimmingVC tab or the GraphVC tab.

  • If the user goes straight to the GraphVC tab, it would require a set of values to plot in the graph.  This placeholder data was set in the AppDelegate.
  • If the user goes to the SwimmingVC tab, the real data is fetched from the database and used to set the AppDelegate’s graphArray property.  Essentially this overrides the placeholder data set at the start.

So no matter if the user went straight to the GraphVC or if he stopped by the SwimmingVC to fetch actual data, the GraphView would have a value.

The issue was that I had to define a property in the GraphView class to hold the data.  Again, not strictly following MVC because the View is using a local copy of the data, but that’s what was going on.

So I had to set the GraphView’s graphPoints property to: var graphPoints!

This means that this variable WILL have a value and can be implicitly unwrapped for the remainder of the class code (GraphView), instead of me having to go thru all the code and if/let test each use of self.graphPoints.

Nice!

HealthKit for iOS8: Part 3

HealthKit iOS8 by Santiapps.com
HealthKit iOS8

Great!  Now let’s move on to the Journal View Controller.  It’ll be a good relax! 🙂 Ok we start out importing the frameworks we need:

import UIKit
import HealthKit

Ok now let’s declare our class properties:

class JournalViewController: UITableViewController {     
let JournalViewControllerTableViewCellReuseIdentifier: NSString = "Cell"     
var foodItems: NSMutableArray?     
var energyFormatter: NSEnergyFormatter {         
var energyFormatter: NSEnergyFormatter?         
var onceToken: dispatch_once_t = 0         
dispatch_once(&onceToken, {             
energyFormatter = NSEnergyFormatter()             
energyFormatter?.unitStyle = NSFormattingUnitStyle.Long             energyFormatter?.forFoodEnergyUse = true             
energyFormatter?.numberFormatter.maximumFractionDigits = 2         })         
return energyFormatter!     
}      
var  healthStore:HKHealthStore? 
}

The first thing to note, is that once again this is UITableViewController.  However, unlike Profile, this tableview will contain dynamic, prototype cells.  Thus we need to dequeue our cells to save memory.  The first constant we need is one for our table view cell identifier.  Then we create a mutable array to hold our selected food items, which will come from the user selecting them in the FoodPicker. The next property is a bit strange.  NSEnergyFormatter is something new and health kit uses it to format energy data.  This is a computed property, which means it is computed each time you call for it.  This is basically creating an energyFormatter much in the same way you would create an dateFormatter from NSDateFormatter. Then finally there is the healthstore property which we need and set from the AppDelegate as you recall. Next up we have our view controller lifecycle methods which kick things off:

override func viewDidLoad() {         
super.viewDidLoad()         
self.foodItems = NSMutableArray()         
self.updateJournal()         
NSNotificationCenter.defaultCenter().addObserver(self, selector: "updateJournal", name: UIApplicationDidBecomeActiveNotification, object: nil)     }     
override func viewDidDisappear(animated: Bool) {         
NSNotificationCenter.defaultCenter().removeObserver(self, name:UIApplicationDidBecomeActiveNotification, object:nil)     
}

Our viewDidLoad does 2 things; instantiates a mutable array for foodItems so the user can start putting things in there and it calls updateJournal which we will write next.  This view controller also sets itself up as an observer for notifications fired whenever the app becomes active and finally in the viewDidDisappear method we remove ourselves as an observer. Now let’s write updateJournal.  The purpose of this method will be to basically update this tableview with data gotten from the FoodPicker after the user selects items he or she consumed:

func updateJournal () -> () {
var now: NSDate = NSDate()
let calendar : NSCalendar = NSCalendar.currentCalendar()       
var components: NSDateComponents = calendar.components(.CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay, fromDate: now)
var startDate: NSDate = calendar.dateFromComponents(components)!
var endDate: NSDate = calendar.dateByAddingUnit(.CalendarUnitDay, value:1, toDate:startDate, options:nil)!

var sampleType: HKSampleType = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDietaryEnergyConsumed)
var predicate: NSPredicate = HKQuery.predicateForSamplesWithStartDate(startDate, endDate:endDate, options:.None)

var query: HKSampleQuery = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: 0, sortDescriptors: nil) {

(query:HKSampleQuery?, results:[AnyObject]!, error:NSError!) -> Void in

if (error != nil) {
NSLog("An error occured fetching the user's tracked food. In your app, try to handle this gracefully. The error was: %@.", error)                 
abort()
}
if results != nil {
NSLog("Got something!")
}
dispatch_async(dispatch_get_main_queue(), {

// PARSE RESULTS INTO ARRAY, RELOAD DATA ALL ON MAIN QUEUE
self.foodItems?.removeAllObjects()
for sample in results as [HKQuantitySample] {                 
NSLog("sample is= \(sample.metadata)")
let foodMetadataDict = (sample.metadata as NSDictionary)
let foodThing: AnyObject? = foodMetadataDict.objectForKey("HKMetadataKeyFoodType")                 NSLog("\(foodThing)")

let foodName = foodThing as NSString
let joules: Double = sample.quantity.doubleValueForUnit(HKUnit.jouleUnit())             
let foodItem: FoodItem = FoodItem(name: foodName, joules:joules) as FoodItem                 self.foodItems?.addObject(foodItem)
}             
self.tableView.reloadData()
})
}
self.healthStore?.executeQuery(query)     // EXECUTE QUERY  }

Ok, its a bit long but simple.  The first few lines create a date for our query.  Then we create a sampleType for DietaryEnergyConsumed which is a data type health store can read and write.  We then create a predicate for it which basically limits the query to today.  Finally we create the query object and execute it (in the last line).

Remember, whats inside the query body is actually a completion block.  We pass the query the sampleType, dates and completion handler.  That completion handler takes a result and an error once again.  If there is an error, we log, if we get results from the query then we log that we got something!  Thats just being silly, but then the query code goes on to a dispatch queue.

This is where we will now update the UI by removing all objects in our foodItems array, and then for every HKQuantitySample in results array, we get the metadata key that we  will store in the object array as part of the FoodItem object and get it as a NSString.

Finally we get the value for that quantity and create a fully qualified FoodItem with that data.  In the end we add that FoodItem to the array and reload the table data. Well now that we mentioned FoodItem, let’s take a look at that class and it’ll help you better understand the next method which is the actual method for adding a FoodItem’s data to the health store.  So here is the FoodItem class:

import Foundation
import HealthKit

class FoodItem {
var name: NSString
var joules:  Double
init (name:NSString, joules:Double) {
self.name = name
self.joules = joules
}

func isEqual(object:AnyObject) -> (Bool) {
if object.isKindOfClass(FoodItem) {
return ((object as FoodItem).joules == self.joules) && (self.name == (object as FoodItem).name)
}
return false;
}

func description(NSString) -> (NSString){
var descriptionDict: NSDictionary = [ "name" : self.name, "joules" : self.joules ]
return descriptionDict.description as NSString
}
}

That was short and sweet!  First we declare the properties of our FoodItem class, name and joules.  Then we create the initializer for it, important to note!  We also added an isEqual function in there to test if an object is a FoodItem type object.  Remember that FoodItem is not subclassing NSObject so it doesn’t have access to NSObject’s isEqual function.  Finally we give FoodItem a description function which basically returns its name and joules values as an NSString. Sweet!  Ok so now let’s look at the Journal view controller method that actually adds a FoodItem:

func addFoodItem (foodItem:FoodItem) {         
// MUST DEFINE HKQUANTITY_TYPE         
var quantityType: HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDietaryEnergyConsumed)         //MUST DEFINE HKQUANTITY         
var quantity: HKQuantity = HKQuantity(unit: HKUnit.jouleUnit(), doubleValue:foodItem.joules)         //DATE & METADATA         
var now: NSDate = NSDate()         
var metadata: NSDictionary = ["HKMetadataKeyFoodType":foodItem.name]         
//This creates the object to SAVE         
var calorieSample: HKQuantitySample = HKQuantitySample(type: quantityType, quantity:quantity, startDate:now, endDate:now, metadata:metadata)          
NSLog("Before saving to health store in Journal...")         self.healthStore?.saveObject(calorieSample, withCompletion: { (success, error) in             NSLog("After saving to healthstore in Journal...")             dispatch_async(dispatch_get_main_queue(), {                 
NSLog("After dispatch to health store in Journal...")                 
if success {                     //MUST UPDATE TABLE in MAIN QUEUE                     NSLog("Energy Consumed Saved")                     
self.foodItems?.insertObject(foodItem, atIndex:0)                     
var indexPathForInsertedFoodItem: NSIndexPath = NSIndexPath(forRow: 0, inSection: 0)                     self.tableView.insertRowsAtIndexPaths([indexPathForInsertedFoodItem], withRowAnimation: UITableViewRowAnimation.Automatic)                 
} else {                     
NSLog("An error occured saving the food %@. In your app, try to handle this gracefully. The error was: %@.", foodItem.name, error)                     
abort()                 
}             
})         
})     
}

It seems long, but the pattern is pretty simple.  We create the right identifier for DietaryEnergyConsumed, we create a quantity from the FoodItem we passed into this method, specifically from its joules value.  We create a date and give it some metadata in order to complete the creation of our HKQuantitySample.  Finally we saveObject to the health store and in its completion block, if success we log Energy Consumed Saved, insert the newly selected item into the foodItems array and update the tableview.  Otherwise we log the error. Finally let’s look at our tableview methods and our segue method because remember that from this Journal view controller we will allow the user to tap the “Add” button to take us to the FoodPicker which we will analyze next.  So here are our tableview and segue methods:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {         return self.foodItems!.count     }

We set our numberOfRowsInSection to the items in our mutable foodItems array.

override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell {         
let cell: UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath:indexPath!) as UITableViewCell          
var foodItem: FoodItem = self.foodItems?[indexPath!.row] as FoodItem         cell.textLabel.text = foodItem.name          
cell.detailTextLabel!.text = energyFormatter.stringFromJoules(foodItem.joules)         
return cell;     
}

Here we dequeue a cell as always and create a FoodItem according to the indexPath!.row and get its name and joules into the cell.

func performUnwindSegue(segue:UIStoryboardSegue) { //was typed as ibaction...         
var foodPickerViewController: FoodPickerViewController = segue.sourceViewController as FoodPickerViewController         
var selectedFoodItem: FoodItem = foodPickerViewController.selectedFoodItem! as FoodItem       self.addFoodItem(selectedFoodItem)     
}

As for the segue, this is the initiating view controller, so we need an unwind because when the user finishes selecting an item in the FoodPicker, we must do some stuff.  What stuff?  Well first we get our sourceViewController for the “unwind” segue.  Don’t get confused because normally you get the destination segue of a forward segue in order to set that destination view controller’s properties.  In this case, we are in the calling view controller and we are unwinding from a segue.  Our source view controller is FoodPicker and our destination is Journal.  We then take the source’s selectedFoodItem property and set it as a local variable called selectedFoodItem here, locally, in Journal.  Then we call our addFoodItem() method passing it in that local variable selectedFoodItem.  That saves that food item’s name in metadata and its joules into the health store as DietaryConsumedEnergy. Well, that was quite simple.  The FoodPicker is actually quite simple.  See you there!

HealthKit for iOS8: Part 2

So you got past the intro and learned about the health store and how we need authorization from the user in order to have read and write access to certain properties.  Now that the user trusts your app, let’s learn how to interact with the health store.

1. HKObjects and the health store

Before we get into reading and writing, let’s get to know HKObjects.  Here is the class tree:

HealthKit HKObject Hierarchy Santiapps.com
HealthKit HKObject Hierarchy

As you can see, HKObjectType inherits directly from NSObject.  You can basically have 2 types of HKObjects; HKCharacteristic and HKSample -Type.  HKCharacteristicType doesn’t change over time whereas HKSampleType does change, such as calories consumed, calories burnt, Glucose levels etc.  Let’s see some examples:

HKCharactersiticType has 3 main objects:

Biological Sex

Blood Type

Birthdate.

HKSampleType has objects like:

-HKCategoryType: discrete, finite values that can be enumerated such as sleep analysis.

-HKCorrelationType: for creating correlated objects grouped as one.

-HKQuantityType: used for creating objects that store numerical values.

-HKWorkoutType: used for creating workout objects.

-FURTHERMORE: HKSample has HKCategorySample & HKQuantitySample

For the most part you will simply fetch HKCharacteristic types from the health store of the user.  The user will have input those values through his/her Health app.  HKSample types on the other hand you will read and write constantly.  Let’s think about how we need to write data:

A. Decide on a Type Identifier

B. Create a matching HKObjectType

C. Create an HKSample

D. Call saveObject on the health store

Let’s take a look at saving data:

self.healthStore?.saveObject(calorieSample, withCompletion: { (success, error) in
// do a bunch of UI stuff probably in the main queue
})

Ok so you might be thinking, what kinds of objects are involved here.  What is dateOfBirth or calorieSample?  Well the best way of understanding is to see an example.  Here are images representing the logic behind writing data to the store.  As mentioned earlier, you find a matching type, set it, create a sample for it and saveObject!  This is the process for a Category type:

HealthKit iOS8 HKCategory by Santiapps.com
HealthKit iOS8 HKCategory

We need to find the matching identifier for the category type in order to save a Category Sample.  Likewise for the Quantity type:

HealthKit iOS8 HKQuantity by Santiapps.com
HealthKit iOS8 HKQuantity

What this means is that before you call saveObject and pass it in that calorieSample, you need to have properly defined calorieSample:

// 1.  MUST FIND THE RIGHT TYPE FOR ENERGY BEING CONSUMED

var quantityType: HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDietaryEnergyConsumed)

// 2. MUST CREATE AND HKQUANTITY FOR THAT TYPE

var quantity: HKQuantity = HKQuantity(unit: HKUnit.jouleUnit(), doubleValue:300)

// 3. MUST DEFINE A DATE FOR THE SAMPLE AND OPTIONALLY SOME METADATA

var now: NSDate = NSDate()

var metadata: NSDictionary = ["HKMetadataKeyFoodType":"Ham Sandwich"]

//4. CREATE HKOBJECT TO BE SAVED

var calorieSample: HKQuantitySample = HKQuantitySample(type: quantityType, quantity:quantity, startDate:now, endDate:now, metadata:metadata)

Finally you can call the saveObject method.  That’s not so bad.  So you would do the same for energy burnt in a workout, calcium intake, etc.

Now let’s take a look at how we would read data from the store:

func fetchUsersAge () -> () {

var error: NSError?

var dateOfBirth = self.healthStore?.dateOfBirthWithError(&error)

if error != nil {

NSLog("An error occured fetching the user's age information. In your app, try to handle this gracefully. The error was: \(error)")

//Present VC

abort()

}

}

As you can see, this is quite simple, we basically say self.healthStore?.dateOfBirthWithError.
Now dateOfBirth is not only a finite, or discrete measure but its also a Characteristic type, it doesn’t change with time.  How would you read other data, HKSampleTypes such as height and weight or Dietary Calories which are not discrete, in other words not whole numbers but infinitesimally scalar and change constantly?  That’s what HKQuery is for!

2. Queries & Stats

Sometimes you want to be able to query the health store for data, that’s what HKQuery is for.  If it’s a quantity sample type then we probably want to save the start and end date with it as before.  So here is an example of how to query our health store:

// 1. Create a date

var now: NSDate = NSDate()

let calendar : NSCalendar = NSCalendar.currentCalendar()

var components: NSDateComponents = calendar.components(.CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay, fromDate: now)

var startDate: NSDate = calendar.dateFromComponents(components)!

var endDate: NSDate = calendar.dateByAddingUnit(.CalendarUnitDay, value:1, toDate:startDate, options:nil)!

// 2. Create the identifier

var sampleType: HKSampleType = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDietaryEnergyConsumed)

// 3. Create the predicate to search with

var predicate: NSPredicate = HKQuery.predicateForSamplesWithStartDate(startDate, endDate:endDate, options:.None)

// 4. Create the HKSampleQuery because we are querying sample types

var query: HKSampleQuery = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: 0, sortDescriptors: nil) { (query:HKSampleQuery?, results:[AnyObject]!, error:NSError!) -> Void in

if (error != nil) {

NSLog("An error occured fetching the user's tracked food. In your app, try to handle this gracefully. The error was: %@.", error)

abort()

}

if results != nil {

NSLog("Got something!")

}

// do UI stuff probably in the main queue

self.healthStore?.executeQuery(query)     // EXECUTE QUERY

}

3. User input UI

So let’s start creating an app.  As you can see, this is all based on the sample app from Apple called Fit.  But it has been modified to add some workout data.  What we want is to be able to read data from our health store (which will provide us with a user’s profile view), collect some user info on how much energy was consumed (which will give us our Energy Consumption view) and have the user input their workout info which in our case is going to be a swimming app (this will give us a Workout view).  For the last 2 data, we will write to the health store how much energy we consumed and how much we burnt.

Ok so we have our AppDelegate and Storyboard.  To recap, you’ve activated the HealthKit capabilities in your project which if you go to the Dev Center, Xcode has created a Team provisioning profile for this app you created and its enabled for HealthKit.  This also gave you an Entitlements file in your navigator which looks like this:

HealthKit app iOS8 by Santiapps.com
HealthKit app iOS8

The Journal file is something I create to keep track of my work, ignore that.  The FoodItem is a file we will quickly throw up on the screen but is a simple Custom Class file to be used in the UI part of this app.  For the first part which deals with the Profile view controller we won’t concern ourselves with it.

Ok so let’s take a quick look at our Profile view controller class.  As always we start out with our properties:

class ProfileViewController: UITableViewController, UITextFieldDelegate {
@IBOutlet var ageValueLabel: UITextField!
@IBOutlet var ageUnitLabel: UILabel?
@IBOutlet var heightValueTextField: UITextField!
@IBOutlet var heightUnitLabel: UILabel?
@IBOutlet var weightValueTextField: UITextField!
@IBOutlet var weightUnitLabel: UILabel?
var healthStore: HKHealthStore?
}

We have created 6 IBOutlet’s.  Each UILabel will hold unit values and each corresponding UITextField will hold the respective data we fetch from the health store.

Then we create a variable for our HealthStore which, if you recall, is set for us from our AppDelegate once it receives authorization.

Now our methods:

Each time our view will appear, we want to update our data, since it may have changed:

override func viewWillAppear(animated: Bool) {
// Update the user interface based on the current user's health information.
self.updateUsersAge()
self.updateUsersHeight()
self.updateUsersWeight()
}

Ok so with each viewWillAppear, we call methods to update our UI with the latest data from our store, which of course means we will fetch from the store and as you recall, it was different fetching a CharacteristicType such as DOB vs a Sample Type such as weight.  So let’s take a look at the simplest one first:

func updateUsersAge () -> () {

var error: NSError?

var dateOfBirth = self.healthStore?.dateOfBirthWithError(&error)

if error != nil {

NSLog("An error occured fetching the user's age information. In your app, try to handle this gracefully. The error was: \(error)")

abort()

}

if dateOfBirth != nil {

NSLog("Found a DOB \(dateOfBirth)")

}

if dateOfBirth == nil {

var dateFormatter = NSDateFormatter()

dateFormatter.dateFormat = "yyyy-MM-dd"

dateOfBirth = dateFormatter.dateFromString("1974-10-18")!

}

// Compute the age of the user.

let now: NSDate = NSDate()

let calendar : NSCalendar = NSCalendar.currentCalendar()

var ageComponents = calendar.components(.CalendarUnitYear,

fromDate: dateOfBirth!,

toDate: now,

options:nil)

var usersAge: Int = ageComponents.year

var ageValueString: NSString = NSNumberFormatter.localizedStringFromNumber(usersAge, numberStyle:NSNumberFormatterStyle.NoStyle)

if let something = self.ageValueLabel!.text {

self.ageValueLabel!.text = ageValueString

}

}

Ok so first we simply fetch the value for dateOfBirth from our store and if we get an error back, we log it.  If dateOfBirth is not nil, then we log success.  You will see a strange bit now, where it checks whether if DOB is nil.  I know, we already checked if error was nil, so DOB should not be nil, but sometimes its empty.  The user may not have set DOB on his or her device yet.  This means you will get nil and your app will crash.  So in case DOB IS nil, we hard code one.  Finally we compute the user’s age based on their DOB by using NSDateComponents and we assign the computed Int to our label using NSNumberFormatter.localizedStringFromNumber method.

Now let’s see how to get height and weight, which are Sample types:

func updateUsersHeight () -> () {

// Fetch user's default height unit in inches.

var lengthFormatter: NSLengthFormatter = NSLengthFormatter()

lengthFormatter.unitStyle = NSFormattingUnitStyle.Long

var heightFormatterUnit: NSLengthFormatterUnit = NSLengthFormatterUnit.Inch

self.heightUnitLabel!.text = lengthFormatter.unitStringFromValue(10, unit:heightFormatterUnit)

var heightType: HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight)

self.fetchMostRecentDataOfQuantityType(heightType, withCompletion: { (mostRecentQuantity:HKQuantity?, error:NSError?) -> () in

//some code

if let something = error {

NSLog("An error occured fetching the user's height information. In your app, try to handle this gracefully. The error was: %@.", something)

abort()

}

//Determine the height in the required unit.

var usersHeight: Double = 0.0

if let somethingElse = mostRecentQuantity {

var heightUnit: HKUnit = HKUnit.inchUnit()

usersHeight = mostRecentQuantity!.doubleValueForUnit(heightUnit)

// Update the user interface.

dispatch_async(dispatch_get_main_queue(), {

self.heightValueTextField.text = NSNumberFormatter.localizedStringFromNumber(usersHeight, numberStyle:.NoStyle)

})

}

})

}

Ok so we use NSLengthFormatter to determine the length style to use.  Then we use its NSLengthFormatterUnit in order to specify the unit in order to set the label for the height value.  Now we get into fetching requirements, so we must create the matching type first and then pass it into our fetch method which is called fetchMostRecentDataOfQuantityType.  So we are passing our quantity type of height, to this method.  We will study that method in a bit, but for the time being, notice we pass in a completion handler.  If that completion handler receives an error, it will log the error.  Otherwise, we create a variable for height, use the received mostRecentQuantity variable and test it since its an optional, create a heightUnit from HKUnit and unwrap the mostRecentQuantity value into our usersHeight variable using our heightUnit.

What?  Double take!  Ok, here we go, this time with weight:

func updateUsersWeight () -> (){

// Fetch the user's default weight unit in pounds.

var massFormatter: NSMassFormatter = NSMassFormatter()

massFormatter.unitStyle = NSFormattingUnitStyle.Long

var weightFormatterUnit: NSMassFormatterUnit = NSMassFormatterUnit.Pound

self.weightUnitLabel!.text = massFormatter.unitStringFromValue(10, unit:weightFormatterUnit)

// Query to get the user's latest weight, if it exists.

var weightType: HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)

self.fetchMostRecentDataOfQuantityType(weightType, withCompletion: { (mostRecentQuantity: HKQuantity?, error: NSError?) -> () in

if ((error) != nil) {

NSLog("An error occured fetching the user's weight information. In your app, try to handle this gracefully. The error was: %@.", error!);

abort()

}

// Determine the weight in the required unit.

var usersWeight:Double = 0.0

if let somethingArse = mostRecentQuantity {

var weightUnit: HKUnit = HKUnit.poundUnit()

usersWeight = mostRecentQuantity!.doubleValueForUnit(weightUnit)

// Update the user interface.

dispatch_async(dispatch_get_main_queue(), {

self.weightValueTextField.text = NSNumberFormatter.localizedStringFromNumber(usersWeight, numberStyle:.NoStyle)

})

}

})

}

Again, we use NSMassFormatter for our style, its NSMassFormatterUnit for our unit, create the weight type identifier and pass it in to our fetch method along with a completion handler.  If the completion handler returns error, we log it.  Otherwise, we create a local variable to store the usersWeight, if let check mostRecentQuantity because its an optional and use it to assign its doubleValue using weightUnit to our local usersWeight variable.  Finally we update the UI in the main queue as always.

Ok so let’s take a look at this magical method, fetch something or other:

func fetchMostRecentDataOfQuantityType(quantityType: HKQuantityType, withCompletion completion: ((mostRecentQuantity:HKQuantity?, error:NSError?) -> ())? ) {

let timeSortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)

let query = HKSampleQuery(sampleType: quantityType, predicate: nil, limit: 1, sortDescriptors: [timeSortDescriptor]) { query, results, error in

if completion != nil && error != nil {

completion!(mostRecentQuantity: nil, error: error)

return;

}

let resultsArray = results as NSArray?

var quantitySample: HKQuantitySample? = resultsArray?.firstObject as HKQuantitySample?

var quantity: HKQuantity? = quantitySample?.quantity

if completion != nil {

completion!(mostRecentQuantity: quantity, error: error)

}

}

self.healthStore?.executeQuery(query)

}

Ok, not that complicated really.  We create a NSSortDescriptor in order to arrange our results and be able to get the latest one.  We create the HKQuery and pass it in the sampleType (height or weight, depending on what was passed into the fetch method), limit results to 1 and no predicate.  We pass in the sort descriptor and another completion handler.  Here are the cases tested next:

– If the HKQuery completion handler is NOT nil && the error is also NOT nil, then there is an error, so return the other completion handler with nil

– Otherwise, take the results AnyObject from the HKQuery and cast it as NSArray, get its firstObject as HKQuantitySample, get its quantity and set it to some local variable called quantity.  Then again check if the original completion is NOT nil, then return the original completion handler with the quantity results.  Finally execute the query!

Finally, you may have noticed the user can update their height and weight.  And we adopted the UITextFieldDelegate protocol for that.  So let’s:

func textFieldShouldReturn (textField: UITextField) -> (ObjCBool) {

textField.resignFirstResponder()

if textField == self.heightValueTextField {

self.saveHeightIntoHealthStore()

} else if textField == self.weightValueTextField {

self.saveWeightIntoHealthStore()

}

return true;

}

call save each time the user modifies their height or weight.  And those methods are simply:

func saveHeightIntoHealthStore () -> () {

var formatter: NSNumberFormatter = self.numberFormatter()

var height: NSNumber?

if let somethingA = self.heightValueTextField!.text {

height = formatter.numberFromString(self.heightValueTextField!.text)

}

let cosa:NSNumber = 8

var otra = cosa.doubleValue

if height == height {

// Save the user's height into HealthKit.

var heightType: HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight)

var heightQuantity: HKQuantity = HKQuantity(unit:HKUnit.inchUnit(), doubleValue: height!.doubleValue)

var heightSample: HKQuantitySample = HKQuantitySample(type: heightType, quantity:heightQuantity, startDate:NSDate(), endDate:NSDate())

self.healthStore?.saveObject(heightSample, withCompletion: { (success, error) in

if (!success) {

NSLog("An error occured saving the height sample %@. In your app, try to handle this gracefully. The error was: %@.", heightSample, error)

abort()

}

})

}

}

Format the value, carefully unwrap the textfield value, create a new height type identifier, height quantity and height sample and save it to the health store.  Likewise for the weight:

func saveWeightIntoHealthStore () -> () {

var formatter: NSNumberFormatter = self.numberFormatter()

var weight: NSNumber?

if let somethingIsInsideOf = self.weightValueTextField!.text {

weight = formatter.numberFromString(self.weightValueTextField!.text)!

NSLog("The weight entered is not numeric. In your app, try to handle this gracefully.");

abort()

}

if weight == weight {

// Save the user's weight into HealthKit.

var weightType: HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)

var weightQuantity: HKQuantity = HKQuantity(unit: HKUnit.inchUnit(), doubleValue: weight!.doubleValue)

var weightSample: HKQuantitySample  = HKQuantitySample(type:weightType, quantity:weightQuantity, startDate:NSDate(), endDate:NSDate())

self.healthStore?.saveObject(weightSample, withCompletion: { (success, error) in

if (!success) {

NSLog("An error occured saving the weight sample %@. In your app, try to handle this gracefully. The error was: %@.", weightSample, error)

abort()

}

})

}

}

And we need the number formatter method:

func numberFormatter ()->(NSNumberFormatter) {

var numberFormatter: NSNumberFormatter?

dispatch_once(&onceToken, {

numberFormatter = NSNumberFormatter()

})

return numberFormatter!

}

Since we use this onceToken, we need to define it globally at the top of the class by adding this line below the following imports:

import UIKit
import HealthKit
var onceToken: dispatch_once_t = 0

See you in Part 3!

HealthKit for iOS8: Part 1

HealthKit iOS8 by Santiapps.com
HealthKit iOS8

Intro: What it’s for?  

HealthKit is a framework that allows you to store health data to a persistent store on the users device.  We will begin with an app from Part 1 but we will take some detours that are important to understand so bare with me.

The first steps are:

– Create a tab bar application in Swift for iPhone Only

– Rename First and Second view controllers to Profile and Journal and make them UITableViewControllers

– In Capabilities turn on HealthKit

HealthKit, (HK), requires permissions to access the health store since most of this data is considered confidential.  So to do this, move over to the AppDelegate.swift.  Add this to appDidFinishLaunching method in AppDelegate:

if (HKHealthStore.isHealthDataAvailable() == true) {

self.healthStore = HKHealthStore() //needs to be var for it to work?

var writeDataTypes = self.dataTypesToWrite()

var readDataTypes = self.dataTypesToRead()

self.healthStore!.requestAuthorizationToShareTypes(writeDataTypes, readTypes: readDataTypes, completion: { (success, error) -> Void in

NSLog(“success \(error)”)

if (!success) {

NSLog(“You didn’t allow HealthKit to access these read/write data types. In your app, try to handle this error gracefully when a user decides not to provide access. The error was: %@. If you’re using a simulator, try it on a device.”, error)

//Present VC

return

} else {

NSLog(“success authorizing the healthstore!”)

NSLog(“writeDataTypes is%@”,writeDataTypes)

NSLog(“readDataTypes is%@”,readDataTypes)

}

// Handle success in your app here.

self.setupHealthStoreForTabBarControllers()

})

}

This requests permission from the user’s health store (on their device) in order to read and write personal data.  The user will be prompted to authorize whatever data he or she feels comfortable with.

Those NSSets containing the user data to read and write are created like so:

func dataTypesToWrite () -> (NSSet) {

var dietaryCalorieEnergyType: HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDietaryEnergyConsumed)

var activeEnergyBurnType: HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned)

var heightType: HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight)

var weightType: HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)

var aSet: NSSet = NSSet(objects: dietaryCalorieEnergyType, activeEnergyBurnType, heightType, weightType)

return aSet

}

We do something a little different in the read types:

func dataTypesToRead () -> NSSet {

let values = [HKQuantityTypeIdentifierDietaryEnergyConsumed,

HKQuantityTypeIdentifierActiveEnergyBurned,

HKQuantityTypeIdentifierHeight,

HKQuantityTypeIdentifierBodyMass,

HKCharacteristicTypeIdentifierDateOfBirth,

HKCharacteristicTypeIdentifierBiologicalSex]

return values.reduce(NSMutableSet()) { (var theSet, let identifier) in

if identifier != nil {

if let something = HKCharacteristicType.characteristicTypeForIdentifier(identifier) {

theSet.addObject(something)

} else if let quantity = HKQuantityType.quantityTypeForIdentifier(identifier) {

theSet.addObject(quantity)

}

}

return theSet

}

}

And finally, since we are going to be using the health store to read and write from many view controllers so we need to propagate our central health store throughout those view controllers.  So we need to use this method:

func setupHealthStoreForTabBarControllers () -> () {

var tabBarController = self.window!.rootViewController as UITabBarController

for navigationController: UINavigationController in tabBarController.viewControllers as Array {

var viewController = navigationController.topViewController

if viewController.isKindOfClass(ProfileViewController) {

var profileViewController: ProfileViewController = viewController as ProfileViewController

profileViewController.healthStore = self.healthStore!

} else if viewController.isKindOfClass(JournalViewController) {

var journalViewController: JournalViewController = viewController as JournalViewController

journalViewController.healthStore = self.healthStore!

} else if viewController.isKindOfClass(EnergyViewController) {

var energyViewController: EnergyViewController = viewController as EnergyViewController

energyViewController.healthStore = self.healthStore!

}

}

}

Now our app has access to our health store.  So recapping what we’ve done in this intro part, we basically requested permission from the health store, specified the data types to read and write and finally propagated our fully authorized health store to our child view controllers.

This is what our storyboard will look like:

Storyboard for HealtKit App Santiapps.com
Storyboard for HealthKit App

 

Here we start out with a tab bar controller with 3 UITableViewControllers as children.  The best way to do this is to drag UITableViewControllers from the Object Library in case you removed the original First and Second placeholder view controllers supplied by the template.  But you could also just drag a tableview and a cell into each of those 2 supplied view controllers and drag an additional one into the storyboard, its up to you.  Then select each of the 3, one by one, and in Editor -> Embed in Navigation Controller.

Here is the ProfileViewController:

HealthKit iOS8 App by Santiapps.com
HealthKit iOS8 App – Profile

As you can see, these are not prototype-dynamic cells but rather custom static cells.  In this case we have given the UINavBar a title and we have divided up the tableview into 3 sections: one for the users age, another for the users height and the last one for the users weight.  You can do this by setting the Sections property of the tableview in the Attributes Inspector, setting the style to Grouped and giving each section a name.

The Journal view controller is the only one that has prototype cells actually.  It looks like this:

HealthKit App iOS8 by Santiapps.com
HealthKit App iOS8 – Journal

 

Here we will simply create a tableview with dynamically populated data from a hardcoded array.  The type of cell is Detail Style and we add a button on the top right.  The idea here is that the user can select items from a list that provide a certain number of calories or joules as input.  Remember, this is the Energy Consumption tab where the user can specify his daily intake.  So of course the “Add” button calls a view controller where the user can select from a list of items:

HealthKit app iOS8 by Santiapps.com
HealthKit app iOS8 – FoodPicker

 

Ok so this one also has dynamic cells, this is the tableview that will actually be filled with the hardcoded items.  Once the user selects an item, it will be passed back to the Journal view controller.  This is the FoodPicker view controller.

Finally here is the EnergyViewController which will basically unify all data:

HealthKit app iOS8 by Santiapps.com
HealthKit app iOS8

Wait!  What do you mean all the data, so far we only have the profile data read from the health store and consumed data, what about the workout?  Well that comes in the next view controller but for now, suffice it to say that this is a static table view cell with data fed in by:
Active Energy Burned = what you burn exercising

Resting Basal Burn = what you burn just breathing

Consumed Energy = what you ate

Net Energy = your final tally for the day

 

You input the Active Energy from the Workout view controller called when you tap the “Add” button in the Energy view controller which looks like this:

HealthKit app iOS8 by Santiapps.com
HealthKit app iOS8

 

Notice something important here, we are capturing workout specific data such as laps, time, meters and pace.  This is not health data per se.  And that is precisely what HealthStore was NOT made for.  Its important to make this distinction because if you open your Health app you will not find laps, meters, km biked, tennis sets played.  That is workout-specific data and the only specific data Health app records, is Steps, because its something the iPhone can record by itself.  The Health app and the health store was conceived to record health data and it can’t very well cover all sport-specific needs.  However all that workout-specific data, laps swam, km biked or sets played, translates, somehow, into calories burned.  Now THAT is a point of convergence for all workouts and that is the kind of thing stored by the health store.  Take a look at your Health app and you will see a Health Data icon which lets you see the kinds of data the app stores.

So why is this important, well, as you will see, we will be storing energy in and energy out.  But we are still capturing laps and meters and time from the user and of course that IS important to the user.  He or she will still want to see their progress in terms of laps and time.  So we will need to incorporate CoreData in the end, in order to store that kind of data.

Ok in the next part we will look at the core of health data in iOS, HKObjects and how to use them!

 

Swift Closures Quick Reference: Part 3

Now let’s write our own closure!

func fetchMostRecentDataOfQuantityType(quantityType: HKQuantityType, withCompletion completion: ((mostRecentQuantity:HKQuantity?, error:NSError?) -> ())? ) {

let timeSortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)

[timeSortDescriptor], resultsHandler: { (query:HKSampleQuery!, results:[AnyObject]!, error:NSError?) -> Void in

let query = HKSampleQuery(sampleType: quantityType, predicate: nil, limit: 1, sortDescriptors: [timeSortDescriptor]) { query, results, error in

if completion != nil && error != nil {

completion!(mostRecentQuantity: nil, error: error)

return;

}

let resultsArray = results as NSArray?

var quantitySample: HKQuantitySample? = resultsArray?.firstObject as HKQuantitySample?

var quantity: HKQuantity? = quantitySample?.quantity

if completion != nil {

completion!(mostRecentQuantity: quantity, error: error)

}

}

self.healthStore?.executeQuery(query)

}

Notice we declare a function that takes a quantityType parameter and a completion parameter.  The completion parameter is a closure (it also has an internal and external name).  The closure is defined as:

{ (mostRecentQuantity, error) -> () } // which takes 2 parameters itself and returns void *dont worry about the ?

Internally, the function uses the completion block.  Whenever we wish to call the completion block, we test for it and say completion (mostRecentQuantity, error) which sets the completion block.  Again don’t worry about the ?

Now let’s actually use that function in code.  We call it with self and pass it in heightType as the quantityType parameter and pass it the values for mostRecentQuantity and any error and the code to be executed.  That code takes the error and checks if there is any in order to log it.  Otherwise, it takes mostRecentQuantity and uses it to set a local variable.

self.fetchMostRecentDataOfQuantityType(heightType, withCompletion: { (mostRecentQuantity:HKQuantity?, error:NSError?) -> () in

if let something = error {

NSLog(“An error occured fetching the user’s height information. In your app, try to handle this gracefully. The error was: %@.”, something)

abort()

}

//Determine the height in the required unit.

var usersHeight: Double = 0.0

if let somethingElse = mostRecentQuantity {

var heightUnit: HKUnit = HKUnit.inchUnit()

usersHeight = mostRecentQuantity!.doubleValueForUnit(heightUnit)

// Update the user interface.

dispatch_async(dispatch_get_main_queue(), {

self.heightValueTextField.text = NSNumberFormatter.localizedStringFromNumber(usersHeight, numberStyle:.NoStyle)

})

}

})

Here is another example of one we can write.  I actually borrowed this from a web tutorial and a GitHub post:

func searchUsersWithSearchTerm(searchTerm: String, completionClosure: (users :User[]) ->()) {
      var request = NSMutableURLRequest(URL: NSURL(string: "https://api.github.com/search/users?q=\(searchTerm)"))
      request.HTTPMethod = "GET"
      let postDataTask = self.urlSession.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
           if error {
                println(error.localizedDescription)
           }
           println(response)
           var repoJSON : NSMutableDictionary = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers, error: nil) as NSMutableDictionary
           var jsonArray = repoJSON["items"] as NSMutableArray
           var users : User[] = User().parseJSONIntoUsers(jsonArray)
           println(users.count)
           completionClosure(users: users)
      })
      postDataTask.resume()
 }

The function takes a searchTerm parameter and a completionClosure much like the previous one.  That closure is:

{ (user:[User]) -> ( )  } // takes a User array named user and returns void.

The function performs some heavy-slow work and then ends up populating an array of Users.  Once we have our data, we want to call our completion block.  So normally you would use a parameter inside a function to do something.  Here we are using our closure parameter to do something, call it!  We call it by saying closure(parameters) as if we were calling a function().  To call a function you pass in the parameters that function requires.  To call a closure you call the parameters that closure requires.  In this case, our closure only requires one parameter, the users array.  So basically the code inside the function will be executed serially and only after the data is returned from the fetch, will the completion closure be called.  Once its called, then it will do whatever was passed into it inside { }.

Now how would we call this function:

self.someController.searchUsers("Woz") { (users: User[]) in
    self.searchUsers = users
    NSOperationQueue.mainQueue().addOperationWithBlock() { () in
        self.collectionView.reloadData()
    }
}

We call the function by passing it the two parameters it requires (searchTerm and completion closure).  Normally we would say (param1,param2), but when the last parameter of a function is a closure, we are allowed to tag it as:

function(param1) { //closure code }

In short, when you call a function that has a completion closure as a parameter you pass it the parameters it needs to execute it’s own code and the closure to execute afterwards.  It first executes it’s own code (which in our case got data from the web) and then that same code is in charge of calling a completion closure only after it’s done with it’s own code.  By calling completion(), the function is calling the completion closure which contains the code we passed it.

How to properly use pre-made closures:

1. var = method(param1, param2, param3) handler4{ params in //if code } //handler4 is optional
        OR
2. var = method(param1, param2, param3, handler:{() -> Void in //if code })

Hope you enjoyed this.