Skip to content

Commit 1d9847c

Browse files
authored
Merge pull request #104 from rossmartin/master
Separate the implementation for clinical records
2 parents c02ad39 + ccad28a commit 1d9847c

12 files changed

+730
-99
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
.idea
22
.DS_Store
3+
node_modules

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@
3434
* `queryCorrelationType`
3535
* `deleteSamples`
3636

37+
Read below about `CLINICAL_READ_PERMISSION` to use these
38+
* `queryClinicalSampleType`
39+
* `queryForClinicalRecordsFromSource`
40+
* `queryForClinicalRecordsWithFHIRResourceType`
41+
3742
### Resources
3843

3944
* The official Apple documentation for [HealthKit can be found here](https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Framework/index.html#//apple_ref/doc/uid/TP40014707).
@@ -53,6 +58,16 @@ cordova plugin add com.telerik.plugins.healthkit --variable HEALTH_READ_PERMISSI
5358
```
5459
`HEALTH_READ_PERMISSION` and `HEALTH_WRITE_PERMISSION` are shown when your app asks for access to data in HealthKit.
5560

61+
If you would like to read clinical record data from the HealthKit store you will need to provide an extra variable during the plugin install. The `CLINICAL_READ_PERMISSION` can be set to include the ability to read FHIR resources. The value that is set here will be used in the `NSHealthClinicalHealthRecordsShareUsageDescription` key of your app's `info.plist` file. It will be shown when your app asks for clinical record data from HealthKit. Do not include the `CLINICAL_READ_PERMISSION` variable unless you really need access to the clinical record data otherwise Apple may reject your app.
62+
63+
The `Health Records` capability will be enabled if the `CLINICAL_READ_PERMISSION` is provided.
64+
65+
Here is an install example with `CLINICAL_READ_PERMISSION` -
66+
```bash
67+
cordova plugin add com.telerik.plugins.healthkit --variable HEALTH_READ_PERMISSION='App needs read access' --variable HEALTH_WRITE_PERMISSION='App needs write access' --variable CLINICAL_READ_PERMISSION='App needs read access' --save
68+
```
69+
70+
5671
#### Using PhoneGap Build?
5772

5873
```xml
@@ -76,7 +91,7 @@ PhoneGap Build has [recently migrated](https://blog.phonegap.com/phonegap-7-0-1-
7691
<plugin name="com.telerik.plugins.healthkit" spec="^0.5.5" >
7792
<variable name="HEALTH_READ_PERMISSION" value="App needs read access" />
7893
<variable name="HEALTH_WRITE_PERMISSION" value="App needs write access" />
79-
</plugin>
94+
</plugin>
8095
</platform>
8196
```
8297

demo/index.html

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,16 @@ <h1>Apache Cordova</h1>
124124
'HKQuantityTypeIdentifierDietaryFatTotal'
125125
];
126126

127+
// or any of these HKClinicalType for readTypes
128+
// use these to access health records (read only)
129+
// HKClinicalTypeIdentifierAllergyRecord
130+
// HKClinicalTypeIdentifierConditionRecord
131+
// HKClinicalTypeIdentifierImmunizationRecord
132+
// HKClinicalTypeIdentifierLabResultRecord
133+
// HKClinicalTypeIdentifierMedicationRecord
134+
// HKClinicalTypeIdentifierProcedureRecord
135+
// HKClinicalTypeIdentifierVitalSignRecord
136+
127137
window.plugins.healthkit.requestAuthorization(
128138
{
129139
readTypes: supportedTypes,
@@ -283,6 +293,77 @@ <h1>Apache Cordova</h1>
283293
);
284294
}
285295

296+
// used to query a specified clinical sample type
297+
function queryClinicalSampleType() {
298+
window.plugins.healthkit.queryClinicalSampleType(
299+
{
300+
'startDate': new Date(new Date().getTime() - 365 * 24 * 60 * 60 * 1000), // 365 days ago
301+
'endDate': new Date(), // now
302+
'sampleType': 'HKClinicalTypeIdentifierAllergyRecord',
303+
// or any of these other HKClinicalType
304+
// HKClinicalTypeIdentifierConditionRecord
305+
// HKClinicalTypeIdentifierImmunizationRecord
306+
// HKClinicalTypeIdentifierLabResultRecord
307+
// HKClinicalTypeIdentifierMedicationRecord
308+
// HKClinicalTypeIdentifierProcedureRecord
309+
// HKClinicalTypeIdentifierVitalSignRecord
310+
},
311+
callback,
312+
callback
313+
);
314+
}
315+
316+
// this is used to search for a specific FHIR resource type
317+
// it uses predicateForClinicalRecordsWithFHIRResourceType (https://developer.apple.com/documentation/healthkit/hkquery/2999414-predicateforclinicalrecordswithf?language=objc)
318+
// In most cases, there’s a one-to-one correspondance between the clinical record types and the FHIR resource types;
319+
// therefore, most queries already return samples from a single FHIR resource type.
320+
// However, queries for the HKClinicalTypeIdentifierMedicationRecord type can return records from the
321+
// HKFHIRResourceTypeMedicationOrder, HKFHIRResourceTypeMedicationDispense, and HKFHIRResourceTypeMedicationStatement FHIR resource types.
322+
// You can use this predicate to limit your query to one of these FHIR types.
323+
function queryForClinicalRecordsWithFHIRResourceType() {
324+
window.plugins.healthkit.queryForClinicalRecordsWithFHIRResourceType(
325+
{
326+
fhirResourceType: 'HKFHIRResourceTypeCondition',
327+
sampleType: 'HKClinicalTypeIdentifierConditionRecord'
328+
// or any of these other HKFHIRResourceType
329+
// HKFHIRResourceTypeAllergyIntolerance',
330+
// HKFHIRResourceTypeImmunization
331+
// HKFHIRResourceTypeMedicationDispense
332+
// HKFHIRResourceTypeMedicationOrder
333+
// HKFHIRResourceTypeMedicationStatement
334+
// HKFHIRResourceTypeObservation
335+
// HKFHIRResourceTypeProcedure
336+
337+
// or any of these other HKClinicalType
338+
// HKClinicalTypeIdentifierImmunizationRecord
339+
// HKClinicalTypeIdentifierLabResultRecord
340+
// HKClinicalTypeIdentifierMedicationRecord
341+
// HKClinicalTypeIdentifierProcedureRecord
342+
// HKClinicalTypeIdentifierVitalSignRecord
343+
},
344+
callback,
345+
callback
346+
);
347+
}
348+
349+
// this is used to find a particular FHIR record
350+
// it uses predicateForClinicalRecordsFromSource (https://developer.apple.com/documentation/healthkit/hkquery/2999413-predicateforclinicalrecordsfroms?language=objc)
351+
function queryForClinicalRecordsFromSource() {
352+
window.plugins.healthkit.queryForClinicalRecordsFromSource(
353+
{
354+
fhirResourceType: 'HKFHIRResourceTypeCondition',
355+
sampleType: 'HKClinicalTypeIdentifierConditionRecord',
356+
identifier: '42467', // the identifier of the FHIR resource
357+
source: { // the provenance of this FHIR record
358+
name: 'Sample Location C',
359+
bundleIdentifier: 'com.apple.public.health.clinical.7D9AED77-AD3F-7801-5A65-59F537187C7D'
360+
}
361+
},
362+
callback,
363+
callback
364+
);
365+
}
366+
286367

287368
// this is work in progress
288369
function monitorSampleType() {

package-lock.json

Lines changed: 89 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,9 @@
2626
"name": "cordova",
2727
"version": ">=3.0.0"
2828
}
29-
]
30-
}
29+
],
30+
"dependencies": {
31+
"elementtree": "0.1.7",
32+
"xcode": "^1.0.0"
33+
}
34+
}

plugin.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,18 @@
5757
<true/>
5858
</config-file>
5959

60+
<config-file target="*/Entitlements-Debug.plist" parent="com.apple.developer.healthkit.access">
61+
<array/>
62+
</config-file>
63+
6064
<config-file target="*/Entitlements-Release.plist" parent="com.apple.developer.healthkit">
6165
<true/>
6266
</config-file>
6367

68+
<config-file target="*/Entitlements-Release.plist" parent="com.apple.developer.healthkit.access">
69+
<array/>
70+
</config-file>
71+
6472
<header-file src="src/ios/WorkoutActivityConversion.h"/>
6573
<source-file src="src/ios/WorkoutActivityConversion.m"/>
6674
<header-file src="src/ios/HKHealthStore+AAPLExtensions.h"/>
@@ -69,6 +77,8 @@
6977
<source-file src="src/ios/HealthKit.m"/>
7078

7179
<framework src="HealthKit.framework" weak="true" />
80+
81+
<hook type="after_plugin_install" src="scripts/after-plugin-install.js" />
7282
</platform>
7383

7484
</plugin>

scripts/after-plugin-install.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
module.exports = function (ctx) {
2+
var isMeteorProject = ctx.opts.projectRoot.indexOf('/.meteor/') > -1;
3+
4+
if (ctx.cmdLine.indexOf('CLINICAL_READ_PERMISSION') < 0 && !isMeteorProject) {
5+
console.log('CLINICAL_READ_PERMISSION was not provided');
6+
return;
7+
}
8+
9+
try {
10+
var fs = require('fs'),
11+
path = require('path'),
12+
configXMLPath = path.join(ctx.opts.projectRoot, 'config.xml'),
13+
et = require('elementtree'),
14+
xcode = require('xcode'),
15+
usageDescription;
16+
17+
if (isMeteorProject) {
18+
var meteorProjectPath = ctx.opts.projectRoot.split('/.meteor/')[0];
19+
var mobileConfigPath = path.join(meteorProjectPath, 'mobile-config.js');
20+
var mobileConfigData = fs.readFileSync(mobileConfigPath, 'utf8');
21+
var re = /CLINICAL_READ_PERMISSION?:\s*["|'](.*)['|"]/g;
22+
var matches = re.exec(mobileConfigData);
23+
if (matches && matches.length > 1) {
24+
usageDescription = matches[1];
25+
} else {
26+
console.log('CLINICAL_READ_PERMISSION was not provided');
27+
return;
28+
}
29+
} else {
30+
usageDescription = ctx.cmdLine.split('CLINICAL_READ_PERMISSION=')[1].split('--')[0].trim();
31+
}
32+
33+
console.log('*** Installing HealthKitClinicalRecords ***');
34+
console.log('CLINICAL_READ_PERMISSION = ', usageDescription);
35+
36+
var configData = fs.readFileSync(configXMLPath).toString();
37+
var etree = et.parse(configData);
38+
var appName = etree.findtext('./name');
39+
var srcPath = path.join(ctx.opts.projectRoot, 'plugins/com.telerik.plugins.healthkit/src/ios');
40+
var projPath = path.join(ctx.opts.projectRoot, 'platforms/ios', appName + '.xcodeproj/project.pbxproj');
41+
var xcodeProj = xcode.project(projPath);
42+
xcodeProj.parseSync();
43+
44+
xcodeProj.addHeaderFile(path.join(srcPath, 'HealthKitClinicalRecords.h'));
45+
xcodeProj.addSourceFile(path.join(srcPath, 'HealthKitClinicalRecords.m'));
46+
47+
fs.writeFileSync(projPath, xcodeProj.writeSync());
48+
49+
// enable health records
50+
var tagPlatform = etree.findall('./platform[@name="ios"]');
51+
if (tagPlatform.length > 0) {
52+
// add CLINICAL_READ_PERMISSION text to config.xml
53+
var tagEditConfig = et.Element('config-file', { target: '*-Info.plist', parent: 'NSHealthClinicalHealthRecordsShareUsageDescription' });
54+
var tagString = et.Element('string');
55+
tagString.text = usageDescription;
56+
tagEditConfig.append(tagString);
57+
tagPlatform[0].append(tagEditConfig);
58+
59+
// add Health Records to entitlements
60+
['*Entitlements-Debug.plist', '*Entitlements-Release.plist'].forEach(function(fileName){
61+
var healthRecordCapabilityConfig = et.Element('config-file', { target: fileName, parent: 'com.apple.developer.healthkit.access'});
62+
var healthRecordArray = et.Element('array');
63+
var healthRecordString = et.Element('string');
64+
healthRecordString.text = 'health-records';
65+
healthRecordArray.append(healthRecordString);
66+
healthRecordCapabilityConfig.append(healthRecordArray);
67+
tagPlatform[0].append(healthRecordCapabilityConfig);
68+
});
69+
70+
configData = etree.write({ 'indent': 4 });
71+
fs.writeFileSync(configXMLPath, configData);
72+
}
73+
74+
console.log('*** DONE Installing HealthKitClinicalRecords ***');
75+
} catch(e) {
76+
console.log('healthkit after-plugin-install error, e: ', JSON.stringify(e, null, 2));
77+
}
78+
};

0 commit comments

Comments
 (0)