Fit/AAPLJournalViewController.m

/*
    Copyright (C) 2016 Apple Inc. All Rights Reserved.
    See LICENSE.txt for this sample’s licensing information
    
    Abstract:
    Displays information retrieved from HealthKit about the food items consumed today.
*/
 
#import "AAPLJournalViewController.h"
#import "AAPLFoodPickerViewController.h"
#import "AAPLFoodItem.h"
 
@interface AAPLJournalViewController()
 
@property (nonatomic) NSMutableArray *foodItems;
 
@end
 
 
NSString *const AAPLJournalViewControllerTableViewCellReuseIdentifier = @"cell";
 
 
@implementation AAPLJournalViewController
 
- (void)viewDidLoad {
    [super viewDidLoad];
 
    self.foodItems = [NSMutableArray array];
 
    [self updateJournal];
 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateJournal) name:UIApplicationDidBecomeActiveNotification object:nil];
}
 
- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
}
 
#pragma mark - Reading HealthKit Data
 
- (void)updateJournal {
    NSCalendar *calendar = [NSCalendar currentCalendar];
    
    NSDate *now = [NSDate date];
    
    NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:now];
    
    NSDate *startDate = [calendar dateFromComponents:components];
    
    NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0];
    
    HKCorrelationType *foodType = [HKObjectType correlationTypeForIdentifier:HKCorrelationTypeIdentifierFood];
    
    NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionNone];
 
    HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:foodType predicate:predicate limit:HKObjectQueryNoLimit sortDescriptors:nil resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
        if (!results) {
            NSLog(@"An error occured fetching the user's tracked food. In your app, try to handle this gracefully. The error was: %@.", error);
            abort();
        }
 
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.foodItems removeAllObjects];
            
            for (HKCorrelation *foodCorrelation in results) {
                // Create an AAPLFoodItem instance that contains the information we care about that's
                // stored in the food correlation.
                AAPLFoodItem *foodItem = [self foodItemFromFoodCorrelation:foodCorrelation];
 
                [self.foodItems addObject:foodItem];
            }
            
            [self.tableView reloadData];
        });
    }];
    
    [self.healthStore executeQuery:query];
}
 
- (AAPLFoodItem *)foodItemFromFoodCorrelation:(HKCorrelation *)foodCorrelation {
    // Fetch the name fo the food.
    NSString *foodName = foodCorrelation.metadata[HKMetadataKeyFoodType];
    
    // Fetch the total energy from the food.
    HKQuantityType *energyConsumedType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierDietaryEnergyConsumed];
    NSSet *energyConsumedSamples = [foodCorrelation objectsForType:energyConsumedType];
    
    // Note that we only have one energy consumed sample correlation (for Fit specifically).
    HKQuantitySample *energyConsumedSample = [energyConsumedSamples anyObject];
    
    HKQuantity *energyQuantityConsumed = [energyConsumedSample quantity];
    
    double joules = [energyQuantityConsumed doubleValueForUnit:[HKUnit jouleUnit]];
    
    return [AAPLFoodItem foodItemWithName:foodName joules:joules];
}
 
#pragma mark - Writing HealthKit Data
 
- (void)addFoodItem:(AAPLFoodItem *)foodItem {
    // Create a new food correlation for the given food item.
    HKCorrelation *foodCorrelationForFoodItem = [self foodCorrelationForFoodItem:foodItem];
    
    [self.healthStore saveObject:foodCorrelationForFoodItem withCompletion:^(BOOL success, NSError *error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (success) {
                [self.foodItems insertObject:foodItem atIndex:0];
                
                NSIndexPath *indexPathForInsertedFoodItem = [NSIndexPath indexPathForRow:0 inSection:0];
                
                [self.tableView insertRowsAtIndexPaths:@[indexPathForInsertedFoodItem] withRowAnimation:UITableViewRowAnimationAutomatic];
            }
            else {
                NSLog(@"An error occured saving the food %@. In your app, try to handle this gracefully. The error was: %@.", foodItem.name, error);
 
                abort();
            }
        });
    }];
}
 
- (HKCorrelation *)foodCorrelationForFoodItem:(AAPLFoodItem *)foodItem {
    NSDate *now = [NSDate date];
    
    HKQuantity *energyQuantityConsumed = [HKQuantity quantityWithUnit:[HKUnit jouleUnit] doubleValue:foodItem.joules];
    
    HKQuantityType *energyConsumedType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierDietaryEnergyConsumed];
    
    HKQuantitySample *energyConsumedSample = [HKQuantitySample quantitySampleWithType:energyConsumedType quantity:energyQuantityConsumed startDate:now endDate:now];
    NSSet *energyConsumedSamples = [NSSet setWithObject:energyConsumedSample];
    
    HKCorrelationType *foodType = [HKObjectType correlationTypeForIdentifier:HKCorrelationTypeIdentifierFood];
 
    NSDictionary *foodCorrelationMetadata = @{HKMetadataKeyFoodType: foodItem.name};
 
    HKCorrelation *foodCorrelation = [HKCorrelation correlationWithType:foodType startDate:now endDate:now objects:energyConsumedSamples metadata:foodCorrelationMetadata];
    
    return foodCorrelation;
}
 
#pragma mark - UITableViewDelegate
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.foodItems.count;
}
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    return [tableView dequeueReusableCellWithIdentifier:AAPLJournalViewControllerTableViewCellReuseIdentifier forIndexPath:indexPath];
}
 
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    AAPLFoodItem *foodItem = self.foodItems[indexPath.row];
    
    cell.textLabel.text = foodItem.name;
    
    NSEnergyFormatter *energyFormatter = [self energyFormatter];
    cell.detailTextLabel.text = [energyFormatter stringFromJoules:foodItem.joules];
}
 
#pragma mark - Segue Interaction
 
- (IBAction)performUnwindSegue:(UIStoryboardSegue *)segue {
    AAPLFoodPickerViewController *foodPickerViewController = [segue sourceViewController];
    
    AAPLFoodItem *selectedFoodItem = foodPickerViewController.selectedFoodItem;
    
    [self addFoodItem:selectedFoodItem];
}
 
#pragma mark - Convenience
 
- (NSEnergyFormatter *)energyFormatter {
    static NSEnergyFormatter *energyFormatter;
    static dispatch_once_t onceToken;
    
    dispatch_once(&onceToken, ^{
        energyFormatter = [[NSEnergyFormatter alloc] init];
        energyFormatter.unitStyle = NSFormattingUnitStyleLong;
        energyFormatter.forFoodEnergyUse = YES;
        energyFormatter.numberFormatter.maximumFractionDigits = 2;
    });
    
    return energyFormatter;
}
 
@end