Sunday, 5 February 2023

Apple Health Kit

How to access and share health and fitness data while maintaining the user’s privacy and control.


Overview

HealthKit provides a central repository for health and fitness data on iPhone and Apple Watch. With the user’s permission, apps communicate with the HealthKit store to access and share this data.

Creating a complete, personalised health and fitness experience includes a variety of tasks:

  • Collecting and storing health and fitness data

  • Analysing and visualising the data

  • Enabling social interactions


HealthKit apps take a collaborative approach to building this experience. Your app doesn’t need to provide all of these features. Instead, you can focus just on the subset of tasks that most interest you.

For example, users can select their favourite weight-tracking, step-counting, and health challenge app, each calibrated to their personal needs. Because HealthKit apps freely exchange data (with user permission), the combined suite provides a more customised experience than any single app on its own


Note

Because health data may contain sensitive, personal information, apps must receive permission from the user to read data from or write data to the HealthKit store. They must also take steps to protect that data at all times. For more information, see Protecting User Privacy.



About the HealthKit Framework


Overview

The HealthKit framework is designed to share data between apps in a meaningful way. The framework constrains the types of data and units to a predefined list, ensuring that all apps understand what the data means and how it can be used. Developers cannot create custom data types or units. Instead, HealthKit provides a wide variety of data types and units.

HealthKit Data

HealthKit saves a variety of data types in the HealthKit Store:

  • Characteristic data. These records represent items that typically do not change, such as the user’s birthdate, blood type, biological sex, and skin type. You can read this data directly from the HealthKit store. Your application cannot save characteristic data. The user must enter or modify this data using the Health app.

  • Sample data. Most of the user’s health data is stored in samples that represent data at a particular point in time. 

  • Workout data. Information about fitness and exercise activities are stored as HKWorkout

  • Source data. Each sample stores information about its source. The HKSourceRevisionobject contains information about the app or device that saved the sample. The HKDeviceobject contains information about the hardware device that generated the data.

  • Deleted objects. An HKDeletedObject instance is used to temporarily store the UUID of an item that has been deleted from the HealthKit store. You can use deleted objects to respond when an object is deleted by the user or another app. For more information, see HKAnchoredObjectQuery and HKDeletedObject.


Properties of Objects and Samples

The HKObject class is the superclass of all HealthKit sample types. All HKObject subclasses are immutable. Each object has the following properties:

  • UUID. A unique identifier for that particular entry.

  • Metadata. A dictionary containing additional information about the entry. The metadata can contain both predefined and custom keys. The predefined keys facilitate the sharing of data between apps. Custom keys help extend a given HealthKit object type, adding app-specific data to the entry.

  • Source Revision. The source of the sample. The source can be a device that directly saves data into HealthKit, or an app. HealthKit automatically records each object’s source and version when the object is saved to the HealthKit store. This property is available only on objects retrieved from the store.

  • Device. The hardware device that generated the data stored in this sample.

  • Type. The sample type, such as a sleep analysis sample, a height sample, or a step count sample.

  • Start date. The sample’s start time.

  • End date. The sample’s end time. If the sample represents a single point in time, the end time should equal the start time. If the sample represents data collected over a time interval, the end time should occur after the start time.

Threading

The HealthKit store is thread-safe, and most HealthKit objects are immutable. In general, you can use HealthKit safely in a multithreaded environment.

All the HealthKit API’s completion handlers execute on private background queues. You will typically want to dispatch this data back to the main queue before updating your user interface or modifying any other resources that should be touched only by the main thread

Syncing Data Between Devices

iPhone and Apple Watch each have their own HealthKit store, and data is automatically synced between the phone and watch. To save space, old data is periodically purged from the Apple Watch. Use earliestPermittedSampleDate() to determine the oldest samples available on Apple Watch.



Setting Up HealthKit

Before using HealthKit, you must perform the following steps:

  1. Enable HealthKit in your app.

  2. Ensure that HealthKit is available on the current device.

  3. Create your app’s HealthKit store.

  4. Request permission to read and share data.


More Info Authorizing Access to Health Data

Enable HealthKit


Before you can use HealthKit, you must enable the HealthKit capabilities for your app. In Xcode, select the project and add the HealthKit capability.



Only select the Clinical Health Records checkbox if your app needs to access the user’s clinical records. App Review may reject apps that enable the Clinical Health Records capability if the app doesn’t actually use the health record data



For a detailed discussion about enabling capabilities, see Configure HealthKit in Xcode Help.



When you enable the HealthKit capabilities on an iOS app, Xcode adds HealthKit to the list of required device capabilities, which prevents users from purchasing or installing the app on devices that don’t support HealthKit.

Ensure HealthKit’s Availability


if HKHealthStore.isHealthDataAvailable() {

   // Add code to use HealthKit here.

}

Create the HealthKit Store


let healthStore = HKHealthStore()


We need only a single HealthKit store per app. These are long-lived objects; you create the store once and keep a reference for later use.


HealthKit Entitlement


A Boolean value that indicates whether the app may request user authorization to access health and activity data that appears in the Health app.


Details

Key 

com.apple.developer.healthkit

Type 

Boolean



HealthKit Capabilities Entitlement


Details

Key 

com.apple.developer.healthkit.access 

Type 

Array of strings



The HealthKit Entitlement provides access to most HealthKit data types. However, because of their highly sensitive nature, some data types require additional entitlements. The HealthKit Capabilities Entitlement provides access to these data types.


To add this entitlement to your app, first enable the HealthKit capability in Xcode, and then check any values that you want to add to the HealthKit Capabilities Entitlement.


Protecting User Privacy

Because health data can be sensitive, HealthKit provides users with fine-grained control over the information that apps can share. The user must explicitly grant each app permission to read and write data to the HealthKit store. Users can grant or deny permission separately for each type of data. 

For example, a user could let your app read step count data, but prevent it from reading blood glucose levels. To prevent possible information leaks, an app isn’t aware when the user denies permission to read data. From the app’s point of view, no data of that type exists.

Important :

Apps must include usage descriptions, or they will crash when you request authorization to access HealthKit data. Include the NSHealthShareUsageDescription key to read, and NSHealthUpdateUsageDescription key to writing data to Healthkit. For projects created using Xcode 13 or later, set these keys in the Target Properties list on the app’s Info tab. For projects created with Xcode 12 or earlier, set these keys in the app's Info.plist file. For more information, see Information Property List.



HKHealthStore


class HKHealthStore : NSObject


Use a HKHealthStore object to request permission to share or read HealthKit data. Once permission is granted, you can use the HealthKit store to save new samples to the store, or to manage the samples that your app has saved.


Coding part:


Authorise HealthKit Access


@IBAction func authoriseHealthKitAccess(_ sender: Any) {

        let healthKitTypes: Set = [

            // access step count

            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)!

        ]

        healthStore.requestAuthorization(toShare: healthKitTypes, read: healthKitTypes) { (_, _) in

            print("authrised???")

        }

        healthStore.requestAuthorization(toShare: healthKitTypes, read: healthKitTypes) { (bool, error) in

            if let e = error {

                print("oops something went wrong during authorisation \(e.localizedDescription)")

            } else {

                print("User has completed the authorization flow")

            }

        }

    }


Get Today's Steps


func getTodaysSteps(completion: @escaping (Double) -> Void) {

        let stepsQuantityType = HKQuantityType.quantityType(forIdentifier: .stepCount)!

        let now = Date()

        let startOfDay = Calendar.current.startOfDay(for: now)

        let predicate = HKQuery.predicateForSamples(withStart: startOfDay, end: now, options: .strictStartDate)

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

            var resultCount = 0.0

            guard let result = result else {

                print("Failed to fetch steps rate")

                completion(resultCount)

                return

            }

            if let sum = result.sumQuantity() {

                resultCount = sum.doubleValue(for: HKUnit.count())

            }

            DispatchQueue.main.async {

                completion(resultCount)

            }

        }

        healthStore.execute(query)

    }



  • In the getTodaysSteps we are creating a HKquery by which we will be able to access the HealthKit Data.

  • HKStatisticsQuery will perform statistical calculations for the given data and return matching samples, it can calculate minimum, maximum and average values for the quantities. This is perfect for our need as it will return the sumQuantity for the total number of steps.

    • It also requires a time between which it should run its calculations, we've set it so that we get stats for the number of steps from the start of the day.


Steps sum 

func getTotalSteps(_ sender: Any) {

        getTodaysSteps { (result) in

            print("\(result)")

            DispatchQueue.main.async {

                self.totalSteps.text = "\(result)"

            }

        }

    }


We are running a function on click on the getTotalSteps button, which in turn runs the function that will return the total number of steps.


This is done on a background thread so in order for us to view the total number of steps we will have to push it onto the main thread.



Friday, 17 May 2013

iPhone PassCode Screen

  


An Example for Pass code screen simple create single view app in 

ViewController.h



#import <UIKit/UIKit.h>
typedef enum {
    CPLockControllerTypeAuth,
    CPLockControllerTypeSet
} CPLockControllerStyle;

@protocol CPLockControllerDelegate<UITextFieldDelegate>

@required
- (void)lockControllerDidFinish:(NSString*)passcode;
- (void)lockControllerDidCancel;

@end

@interface ViewController : UIViewController
{
    //Public vars
    CPLockControllerStyle style;
    NSString *passcode;
    NSString *prompt;
    NSString *title;
    id <CPLockControllerDelegate> delegate;
    BOOL hideCode;
   
    //Private vars
    BOOL retry;
    NSMutableString *tempString;
    //UI Elements
    UITextField *hiddenField;
    IBOutlet UILabel *promptLabel;
     IBOutlet UILabel *subPromptLabel;
//    UILabel *subPromptLabel;
    IBOutlet UITextField *field1;
    IBOutlet UITextField *field2;
    IBOutlet UITextField *field3;
    IBOutlet UITextField *field4;
}

@property (nonatomic, assign) id delegate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic) CPLockControllerStyle style;
@property (nonatomic, retain) NSString *passcode;
@property (nonatomic, retain) NSString *prompt;
@property (nonatomic) BOOL hideCode;

- (void)setTitle:(NSString *)title;

@end





ViewCotroller.m


#import "ViewController.h"


#define kCPLCDefaultSetPrompt            @"Enter your new passcode"
#define kCPLCDefaultAuthPrompt            @"Enter your passcode"

#define kCPLCDefaultSetTitle            @"Set Passcode"
#define kCPLCDefaultConfirmTitle        @"Confirm Passcode"
#define kCPLCDefaultAuthTitle            @"Enter Passcode"

#define kCPLCDefaultSetError            @"Passcodes did not match. Try again."
#define kCPLCDefaultAuthError            @"Passcode incorrect. Try again."


@interface ViewController ()
- (void)setupSubviews;
//


//- (void)setupNavigationBar;
- (void)setupTextFields;
- (void)resetFields;
- (void)passcodeDidNotMatch;
- (void)dissmissView;

@property (nonatomic, retain) NSMutableString *tempString;
@property (nonatomic) BOOL retry;
@property (nonatomic, retain) UILabel *promptLabel;
@property (nonatomic, retain) UILabel *subPromptLabel;
@property (nonatomic, retain) UITextField *hiddenField;
//@property (nonatomic, retain) UINavigationItem *navigationItem;

@end

@implementation ViewController
@synthesize delegate,style,passcode,prompt,hiddenField,promptLabel,subPromptLabel,tempString,retry,title,hideCode;
- (id)initWithStyle:(CPLockControllerStyle)theStyle {
    if(self = [super init]){
        self.style = theStyle;
        self.retry = NO;
        self.tempString = [NSMutableString string];
            }
   
    return self;
}

- (void)viewDidLoad
{//passed default string you can use yours by setting page
    passcode = @"1234";
       
    //check if passcode is set for CPLockControllerTypeAuth
    if(style == CPLockControllerTypeAuth){
        assert(passcode != nil);
    }
   
    [self setupSubviews];

  //  [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}
- (void)setupSubviews {
   
       
    //prompt

    if(prompt == nil){
        if(self.style == CPLockControllerTypeSet){
            prompt = kCPLCDefaultSetPrompt;
        } else if(self.style == CPLockControllerTypeAuth){
            prompt = kCPLCDefaultAuthPrompt;
        }
    }
   
    //main prompt
    promptLabel.text = prompt;
   
    promptLabel.backgroundColor = [UIColor clearColor];
    promptLabel.shadowColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:.50];
    promptLabel.font = [UIFont systemFontOfSize:[UIFont labelFontSize]];
    promptLabel.shadowOffset = CGSizeMake(0, -0.75);
    promptLabel.textColor = [UIColor colorWithRed:0.318 green:0.345 blue:0.416 alpha:1.000];
    [self.view addSubview:promptLabel];
    [self setupTextFields];
}
- (void)setupTextFields {
   
    //create four textfields
   
    field1.backgroundColor = [UIColor whiteColor];
    field1.borderStyle = UITextBorderStyleBezel;
    //field1.enabled = NO;
    field1.secureTextEntry =YES;
        field1.tag = 0;
   
   
    field2.backgroundColor = [UIColor whiteColor];
    field2.borderStyle = UITextBorderStyleBezel;
    field2.enabled = NO;
    field2.secureTextEntry = YES;
   
    field2.tag = 2;
    field3.backgroundColor = [UIColor whiteColor];
    field3.borderStyle = UITextBorderStyleBezel;
    field3.enabled = NO;
    field3.secureTextEntry =YES;
   
    field3.tag = 3;
    field4.backgroundColor = [UIColor whiteColor];
    field4.borderStyle = UITextBorderStyleBezel;
    field4.enabled = NO;
    field4.secureTextEntry = YES;
    field4.tag = 4;
       
    //this is the field the passcode is put into
    hiddenField = [[UITextField alloc]initWithFrame:CGRectMake(-3000,-3000,0,0)];
    hiddenField.text = @"";
    hiddenField.keyboardType = UIKeyboardTypeNumberPad;
    [hiddenField becomeFirstResponder];
    hiddenField.delegate = self;
    [self.view addSubview:hiddenField];
   
   
   
   
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    int charcount = [textField.text length];
   
    if(self.retry == YES){
        charcount-=1;
    }
   
    if(charcount == 0){
        field1.text = string;
    } else if(charcount == 1){
        field2.text = string;
    } else if(charcount == 2){
        field3.text = string;
    } else if(charcount == 3){
        field4.text = string;
    }
    [self.tempString appendString:string];
   
    //we've reached 4 chars
    if(charcount == 3){
       
        if(self.style == CPLockControllerTypeSet){
           
            if(passcode == nil){
                //empty tempstring to passcode string
                passcode = [self.tempString copy];
               
                self.tempString = [NSMutableString string];
               
                //reset visible/hidden fields
                [self resetFields];
               
                promptLabel.text = kCPLCDefaultConfirmTitle;
                self.retry = YES;
            } else {
                //check if confirm matches first
                if([passcode isEqualToString:self.tempString]){
                    [delegate lockControllerDidFinish:passcode];
                    [self dissmissView];
                   
                    //confirm passcode doesn't match
                } else {
                    [self passcodeDidNotMatch];
                }
               
            }
           
        } else if(self.style == CPLockControllerTypeAuth){
            if([passcode isEqualToString:self.tempString])

{
NSLog(@"Correct Password Show your next window here");
               

               
            } else {
                [self passcodeDidNotMatch];
               
            }
           
           
        }
    }   
   
    return YES;
}

- (void)passcodeDidNotMatch {
    self.tempString = [NSMutableString string];
    if(self.style == CPLockControllerTypeSet){
       
        UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"Alert" message:@"Encorrect PIN" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
        [alert show];
        //subPromptLabel.text = kCPLCDefaultSetError;
    } else if(self.style == CPLockControllerTypeAuth){
        //subPromptLabel.text = kCPLCDefaultAuthError;
        UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"Alert" message:@"Encorrect PIN" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
        [alert show];
    
       
       
    }
    self.retry = YES;
    [self resetFields];
}

- (void)resetFields {
    field1.text = @"";
    field2.text = @"";
    field3.text = @"";
    field4.text = @"";
    hiddenField.text = @"";
}

- (void)dissmissView {
   
    [self dismissViewControllerAnimated:YES completion:nil];
   
}

- (void)userDidCancel:(id)sender {
    //[delegate lockControllerDidCancel];
    [self dissmissView];
}

////////////////////////////////////////////

Xib Should be designed with 4 text fields

Thank You.........:)

Thursday, 26 July 2012

iPhone Bluetooth Programming


Creating the Project
Using Xcode, create a new View-based Application project and name it as Bluetooth.
All the various APIs for accessing the Bluetooth is located in the GameKit framework. Hence, you need to add this framework to your project. Add a new Framework to the project by right-clicking on the Frameworks group in Xcode and selecting Add, Existing Frameworks. Select GameKit.framework




In the BluetoothViewController.h file, declare the following object, outlets, and actions:

#import
#import
@interface BluetoothViewController : UIViewController {
GKSession *currentSession;
IBOutlet UITextField *txtMessage;
IBOutlet UIButton *connect;
IBOutlet UIButton *disconnect;
}
@property (nonatomic, retain) GKSession *currentSession;
@property (nonatomic, retain) UITextField *txtMessage;
@property (nonatomic, retain) UIButton *connect;
@property (nonatomic, retain) UIButton *disconnect;
-(IBAction) btnSend:(id) sender;
-(IBAction) btnConnect:(id) sender;
-(IBAction) btnDisconnect:(id) sender;
@end


The GKSession object is used to represent a session between two connected Bluetooth devices. You will make use of it to send and receive data between the two devices.
In the BluetoothViewController.m file, add in the following statements in bold:

#import "BluetoothViewController.h"
@implementation BluetoothViewController
@synthesize currentSession;
@synthesize txtMessage;
@synthesize connect;
@synthesize disconnect;


Double-click on BluetoothViewController.xib to edit it in Interface Builder. Add the following views to the View window
  • Text Field
  • Round Rect Button




Perform the following actions:
  • Control-click on the File’s Owner item and drag and drop it over the Text Field view. Select txtMessage.
  • Control-click on the File’s Owner item and drag and drop it over the Connect button. Select connect.
  • Control-click on the File’s Owner item and drag and drop it over the Disconnect button. Select disconnect.
  • Control-click on the Send button and drag and drop it over the File’s Owner item. Select btnSend:.
  • Control-click on the Connect button and drag and drop it over the File’s Owner item. Select btnConnect:.
  • Control-click on the Disconnect button and drag and drop it over the File’s Owner item. Select btnDisconnect:.
Right-click on the File’s Owner item to verify that all the connections are made correctly.




Back in Xcode, in the BluetoothViewController.m file, add in the following statements in bold:

- (void)viewDidLoad {
[connect setHidden:NO];
[disconnect setHidden:YES];
[super viewDidLoad];
}
- (void)dealloc {
[txtMessage release];
[currentSession release];
[super dealloc];
}


Searching for Peer Devices
Now that all the plumbings for the project have been done, you can now focus on the APIs for accessing other Bluetooth devices.
In the BluetoothViewController.h file, declare a GKPeerPickerController object:

#import "BluetoothViewController.h"
#import
@implementation BluetoothViewController
@synthesize currentSession;
@synthesize txtMessage;
@synthesize connect;
@synthesize disconnect;
GKPeerPickerController *picker;


The GKPeerPickerController class provides a standard UI to let your application discover and connect to another Bluetooth device. This is the easiest way to connect to another Bluetooth device.
To discover and connect to another Bluetooth device, implement the btnConnect: method as follows:

-(IBAction) btnConnect:(id) sender {
picker = [[GKPeerPickerController alloc] init];
picker.delegate = self;
picker.connectionTypesMask = GKPeerPickerConnectionTypeNearby;
[connect setHidden:YES];
[disconnect setHidden:NO];
[picker show];
}

When remote Bluetooth devices are detected and the user has selected and connected to one of them, the peerPickerController:didConnectPeer:toSession: method will be called. Hence, implement this method as follows:
 
- (void)peerPickerController:(GKPeerPickerController *)picker
didConnectPeer:(NSString *)peerID
toSession:(GKSession *) session {
self.currentSession = session;
session.delegate = self;
[session setDataReceiveHandler:self withContext:nil];
picker.delegate = nil;
[picker dismiss];
[picker autorelease];
}
When the user has connected to the peer Bluetooth device, you save the GKSession object to the currentSession property. This will allow you to use the GKSession object to communicate with the remote device.
If the user cancels the Bluetooth Picker, the peerPickerControllerDidCancel: method will be called. Define this method as follows:
 
- (void)peerPickerControllerDidCancel:(GKPeerPickerController *)picker
{
picker.delegate = nil;
[picker autorelease];
[connect setHidden:NO];
[disconnect setHidden:YES];
}


To disconnect from a connected device, use the disconnectFromAllPeers method from the GKSession object. Define the btnDisconnect: method as follows:
 
-(IBAction) btnDisconnect:(id) sender {
[self.currentSession disconnectFromAllPeers];
[self.currentSession release];
currentSession = nil;
[connect setHidden:NO];
[disconnect setHidden:YES];
}
When a device is connected or disconnected, the session:peer:didChangeState: method will be called. Implement the method as follows:
 
- (void)session:(GKSession *)session
peer:(NSString *)peerID
didChangeState:(GKPeerConnectionState)state {
switch (state)
{
case GKPeerStateConnected:
NSLog(@"connected");
break;
case GKPeerStateDisconnected:
NSLog(@"disconnected");
[self.currentSession release];
currentSession = nil;
[connect setHidden:NO];
[disconnect setHidden:YES];
break;
}
}
Handling this event will allow you to know when a connection is established, or ended. For example, when the connection is established, you might want to immediately start sending data over to the other device.




Sending Data

To send data to the connected Bluetooth device, use the sendDataToAllPeers: method of the GKSession object. The data that you send is transmitted via an NSData object; hence you are free to define your own application protocol to send any types of data (e.g. binary data such as images). Define the mySendDataToPeers: method as follows:
 
- (void) mySendDataToPeers:(NSData *) data
{
if (currentSession)
[self.currentSession sendDataToAllPeers:data
withDataMode:GKSendDataReliable
error:nil];
}
Define the btnSend: method as follows so that the text entered by the user will be sent to the remote device:
 
-(IBAction) btnSend:(id) sender
{
//---convert an NSString object to NSData---
NSData* data;
NSString *str = [NSString stringWithString:txtMessage.text];
data = [str dataUsingEncoding: NSASCIIStringEncoding];
[self mySendDataToPeers:data];
}

Receiving Data





When data is received from the other device, the receiveData:fromPeer:inSession:context: method will be called. Implement this method as follows:
 
- (void) receiveData:(NSData *)data
fromPeer:(NSString *)peer
inSession:(GKSession *)session
context:(void *)context {
//---convert the NSData to NSString---
NSString* str;
str = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Data received"
message:str
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}
Here, the received data is in the NSData format. To display it using the UIAlertView class, you need to convert it to an NSString object.

Testing the Application



That’s it!
You are now ready to test the application. Press Command-R in Xcode to deploy the application onto two iPhones / iPod Touches. For this article, I assume you have two devices -- either iPhones or iPod Touches. In order to run this application,  they both need to run at least iPhone OS 3.0.
Once the application is deployed to the two devices, launch the application on both devices. On each device, tap the Connect button. The GKPeerPickerController will display the standard UI to discover other devices .
After a while, both application should be able to find each other  When you tap on the name of the found device, the application will attempt to connect to it.