Extending the interactive welcome BMI app with HealthKit

by Monte Goulding on June 2, 2016 2 comments

By now many of you will have completed the new interactive tutorial for LiveCode 8. If you haven’t done that yet, you might like to go and complete it before continuing on here.

By the end of the tutorial you should have an app that looks something like the image below. If you’re like me and get excited by pancake printers it’s probably telling you something different to the image below but let’s not worry about that just now… the point is you should have an app that uses a web service to calculate BMI and charts it.

Today I’m introducing a new external for HealthKit called mergHK. With mergHK you can read and write to the HealthStore and therefore improve the integration of your health app with the array of other apps that users use to manage their health data. You’ll find that included in the latest DP release of 8.1.

img1

Reconfiguring the calculator for saving BMI values

What we are going to do is make some changes to the app and store our BMI values in the iOS HealthKit HealthStore, using the new mergHK external. This will make the values accessible to other applications and any existing BMI values entered in other apps will be accessible in ours.

The first thing I’d like to do is change the getBMI function to calculate BMI locally. The tutorial provides a great example of how simple it is to use a web service in LiveCode, but considering BMI is easy to calculate we might as well do it locally. Copy this new getBMI function to the stack script:

function getBMI pHeight, pWeight
   return round(pWeight/pHeight ^ 2)
end getBMI

Now we are going to dynamically calculate as the user changes the height and weight values. To do that select height and weight fields and click on the Group button on the toolbar. The new group containing the fields should now be selected. Click on the Code button on the toolbar and add the following code to the new group:

on textChanged
   if field "height" is a number and field "weight" is a number then
      calculateBMI
   end if
end textChanged

Now our calculated BMI should update as we change the height or weight. This means we don’t need the Calculate BMI button. Instead I would like to add a new button to select the date the BMI should be recorded against. Change the name of the Calculate BMI button to Date and clear the label property. It’s more appropriate the date be above the
height and weight entry, so let’s move it there and move the height and weight down. It should look something like this:

img2

Set the script of the Date button to the following to allow the user to select a date:

on mouseUp
   local tSelected, tRangeEnd, tDateResult
   
   // the date and time selected by default
   put the cDate of this stack into tSelected
   // the maximum date and time that can be selected
   put the seconds into tRangeEnd 
   
   // launch the date and time picker
   mobilePickDate "date", tSelected,, tRangeEnd
   
   // get the result
   put the result into tDateResult
   
   // check and display the result
   if tDateResult is not 0 then
      set the cDate of this stack to tDateResult
      convert tDateResult from seconds to abbreviated date
      set the label of me to the result
   end if
end mouseUp

The next thing we will want to do is make some changes to the preOpenCard hander of the card script. First we are going to clear our calculator when the app opens in the stack script (we’ll do that in a minute), so we are going to replace the Clear header action with Save. Edit the card script so the preOpenCard handler reads as follows:

on preOpenCard
   set the itemNames of widget "header" to "Save"
   set the itemLabels of widget "header" to "Save"
   set the label of widget "header" to "BMI Calculator"
end preOpenCard

And the header widget script to:

on mouseUp
   if the mouseAction of widget "header" is "save" then
      SaveBMI
   end if
end mouseUp

Now if we go to a different card by clicking on Services in the navigation widget and come back to the calculator we should see something like this:

img3

Next up we will implement HealthKit and a new History card in our app.

Implementing HealthKit

The first thing we need to do to get started with HealthKit is ensure we have a development provisioning profile that has HealthKit enabled. Login to your Apple Developer Account and create a new App ID with the HealthKit service checked:

img4

Next create a new development provisioning profile using your newly created App ID. We will select that provisioning profile in the standalone settings dialog before building our app for iOS.

Before we can read or write to the HealthStore we need to get the user’s permission. Requesting access will present a dialog to the user, the first time your app requests access to that particular type of data. If they decline access they must grant access via the Settings or Health apps if they change their mind.

Add the following code to the stack script.

on preOpenStack
   local tDate
   put the seconds into tDate
   set the cDate of this stack to tDate
   convert tDate from seconds to abbreviated date
   set the label of button "Date" to tDate
   
   set the cBMI of this stack to empty
   set the cHeight of this stack to empty
   set the cWidth of this stack to empty
   
   put empty into field "height"
   put empty into field "weight"
   put empty into field "results"
   put empty into field "advice"
   set the backgroundColor of graphic "resultsBG" to "white"
   
   if the platform is not "iphone" then
      exit preOpenStack
   end if
   
   send "AuthorizeHealthKit" to me in 1 second
end preOpenStack

command AuthorizeHealthKit
   if mergHKIsHealthDataAvailable() then
      mergHKRequestAuthorization "HKQuantityTypeIdentifierBodyMassIndex", "HKQuantityTypeIdentifierBodyMassIndex"
   end if
end AuthorizeHealthKit

on mergHKAuthorizationComplete pError
   if pError is not empty then
      answer pError
      exit mergHKAuthorizationComplete
   end if
end mergHKAuthorizationComplete

command QueryBMIFromHealthStore
   if not mergHKIsHealthDataAvailable() then
      answer "Health data is not available on this device"
      exit QueryBMIFromHealthStore
   end if
   
   if not mergHKCheckAuthorization("HKQuantityTypeIdentifierBodyMass") is "sharing authorized" then
      answer "Please enable this app to read and write body mass index data via the Settings or Health apps."
      exit QueryBMIFromHealthStore
   end if
   
   local tStartDate, tEndDate
   put the seconds+86400 into tEndDate
   put the seconds - 31536000 into tStartDate # last year
   
   # get a year's BMI history and chart
   mergHKSampleQuery "HKQuantityTypeIdentifierBodyMassIndex", tStartDate, tEndDate
end QueryBMIFromHealthStore

on mergHKQueryResults pQueryID, pResultA, pError
   mergHKStopQuery pQueryID
   
   if pError is not empty then
      answer pError
      exit mergHKQueryResults
   end if
   
   local tUUID
   
   local tResultA
   
   # create an array of results by date
   repeat for each key tUUID in pResultA
      if pResultA[tUUID]["identifier"] is "HKQuantityTypeIdentifierBodyMassIndex" then
         put pResultA[tUUID]["quantity"] into tResultA[pResultA[tUUID]["start date"]]
      end if
   end repeat
   
   dispatch "BMIDataFromHealthStore" to card "history" with tResultA
end mergHKQueryResults

command SaveBMI
   if the cBMI of this stack is empty then
      answer "Enter a height and weight to calculate your BMI"
   end if
   
   mergHKAddQuantitySample "HKQuantityTypeIdentifierBodyMassIndex", the cDate of this stack, the cDate of this stack, the cBMI of this stack, "count"
   
   answer "BMI Saved!"
end SaveBMI

function getBMI pHeight, pWeight
   return round(pWeight/pHeight ^ 2)
end getBMI

on resizeStack pWidth, pHeight
   set the rectangle of widget "header" to 0,0,pWidth,the height of widget "header"
   set the rectangle of widget "Navigation Bar" to 0,pHeight-the height of widget "Navigation Bar",pWidth,pHeight
   get 0,the height of widget "header",pWidth,pHeight-the height of widget "Navigation Bar"
   set the rectangle of widget "chart" of card "chart" to it
   set the rectangle of widget "browser" of card "Services" to it
   pass resizeStack
end resizeStack

Now create a new card named History. This card will allow the user to pick a date they recorded BMI on and view the results. We need some of the same objects that we have on the Calculator card, so select the Date button and background graphic along with the results fields and background graphic. Once copied over to the card you can resize the objects to look something like the following:

img5

The Date button script just needs to be the following:

on mouseUp
  pickDate
end mouseUp

And the History card script handles the rest:

local sResultA
local sTestDates
local sTestConvertedDates
local sCurrentIndex

on preOpenCard
   set the label of widget "header" to "History"
   set the itemArray of widget "header" to empty
   
   set the label of button "Date" to space
   
   if the platform is "iphone" then
       QueryBMIFromHealthStore
   end if
end preOpenCard

on BMIDataFromHealthStore pResultA
   put pResultA into sResultA
   -- present the most recent results
   
   put the keys of pResultA into sTestDates
   sort lines of sTestDates numeric descending
   
   local tDate
   
   put empty into sTestConvertedDates
   repeat for each line tDate in sTestDates
      convert tDate from seconds to abbreviated date
      put tDate & return after sTestConvertedDates
   end repeat
   delete the last char of sTestConvertedDates
   
   setData 1
end BMIDataFromHealthStore

command setData pIndex
   put pIndex into sCurrentIndex
   set the label of button "Date" to line pIndex of sTestConvertedDates
   
   local tBMI
   put sResultA[line pIndex of sTestDates] into tBMI
   
   put "BMI:" && tBMI into field "results"
   
   if tBMI <= 18 then
      put "A BMI less than 19 is considered underweight." into field "advice"
      set the backgroundColor of graphic "resultsBG" to "blue"
   else if tBMI <= 24 then
      put "A BMI between 19 and 24 is considered healthy." into field "advice"
      set the backgroundColor of graphic "resultsBG" to "green"
   else if tBMI <= 29 then
      put "A BMI between 25 and 29 is considered overweight." into field "advice"
      set the backgroundColor of graphic "resultsBG" to "orange"
   else
      put "A BMI over 29 is considered obese." into field "advice"
      set the backgroundColor of graphic "resultsBG" to "red"
   end if
end setData

command pickDate
   if sTestConvertedDates is empty then
      exit pickDate
   end if
   
   mobilePick sTestConvertedDates, sCurrentIndex
   
   if the result is 0 then
      exit pickDate
   end if
   
   setData the result
end pickDate

Deploying the app

The first thing you need to know is that we are going to need to deploy to an iPhone, as HealthKit is not available on iPads. One of the goals of LiveCode 8.1 was to make including resources in your standalone much simpler, so we are going to use that version to build our application. Open the standalone settings dialog and choose Select inclusions for the standalone application. This will enable the new inclusions pane, which will list all the available inclusions. Scroll down the list and make sure Header Bar, Navigation Bar, Browser and mergHK are all checked.

Screen Shot 2016-05-26 at 3.11.14 PM

Now we need to go to the iOS pane to change some settings. Enter the app ID and choose the provisioning profile with HealthKit enabled that you setup earlier. Another important setting is in the Requirements and Restrictions area – you need to check the Disable ATS box. You will get a warning dialog because it’s not normally recommended, but the page we view on the Services card via the browser widget does not support https.

Screen Shot 2016-05-26 at 8.03.36 PM

Once you have done that you can build the app and copy it to your device. A helpful tutorial that you may like to reference which will explain how to put the app on your phone is, How do I build an iOS application? When it’s on your device and you run it for the first time you should see a dialog like this asking you to give permission for the app to read and write body mass index data:

IMG_0470

If you go ahead and allow access, then enter a height and weight and Save, the BMI will be saved to the HealthStore. With data in the HealthStore our History card and the Health App will start displaying BMI values.

You can download my version of the app from here.

IMG_0471IMG_0472

 

The HealthKit External is now included in the LiveCode 8.1.0 DP1 release as part of the LiveCode Extension Pack, available with Indy and Business editions.

Monte GouldingExtending the interactive welcome BMI app with HealthKit

Related Posts

Take a look at these posts

2 comments

Join the conversation
  • Rian Langley - June 3, 2016 reply

    Great, but what about Android?
    When will the MergExt suite of externals be available/adapted for Android and other operating systems?
    Seems that Livecode somehow neglects Android / Windows…

    Monte Goulding - June 6, 2016 reply

    Hi Rian, one of the great things about Infinite LiveCode is it will make delivering cross platform extensions much easier. I’m certainly looking forward to delivering some great Android features. HealthKit however is an iOS only framework and its development was funded via LiveCode Business Services. If there’s something you need in order to deliver your Windows or Android I’m sure the team would be happy to help. https://livecode.com/services/

Join the conversation

*