├── Pod
├── Assets
│ └── .gitkeep
└── Classes
│ ├── .gitkeep
│ ├── OMHError.m
│ ├── OMHError.h
│ ├── NSDate+RFC3339.m
│ ├── OMHSerializer.h
│ ├── NSDate+RFC3339.h
│ ├── OMHHealthKitConstantsMapper.h
│ ├── OMHHealthKitConstantsMapper.m
│ └── OMHSerializer.m
├── Example
├── Tests
│ ├── en.lproj
│ │ └── InfoPlist.strings
│ ├── Tests-Prefix.pch
│ ├── Tests-Info.plist
│ ├── OMHSampleFactory.h
│ ├── OMHSchemaStore.h
│ ├── HKObject+Private.h
│ ├── NSDate+RFC3339Tests.m
│ ├── OMHSchemaStore.m
│ └── OMHSampleFactory.m
├── Podfile
├── Granola.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── Podfile.lock
└── Granola.xcodeproj
│ ├── xcshareddata
│ └── xcschemes
│ │ └── Granola-Example.xcscheme
│ └── project.pbxproj
├── .gitmodules
├── .travis.yml
├── .gitignore
├── Granola.podspec
├── README.md
├── LICENSE
└── Docs
└── hkobject_type_coverage.md
/Pod/Assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Pod/Classes/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Example/Tests/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "Example/Tests/omh-schemas"]
2 | path = Example/Tests/omh-schemas
3 | url = https://github.com/openmhealth/schemas.git
4 | branch = master
--------------------------------------------------------------------------------
/Example/Podfile:
--------------------------------------------------------------------------------
1 | source 'https://github.com/CocoaPods/Specs.git'
2 |
3 | target 'Granola' do
4 | pod "Granola", :path => "../"
5 |
6 | pod 'Specta', '~> 1.0'
7 | pod 'Expecta', '~> 1.0'
8 | pod 'VVJSONSchemaValidation', '~> 1.3'
9 | end
10 |
11 |
--------------------------------------------------------------------------------
/Example/Granola.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Example/Granola.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Example/Tests/Tests-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header
3 | //
4 | // The contents of this file are implicitly included at the beginning of every test case source file.
5 | //
6 |
7 | #ifdef __OBJC__
8 |
9 | #define EXP_SHORTHAND
10 | #import
11 | #import
12 |
13 | #endif
14 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # references:
2 | # * http://www.objc.io/issue-6/travis-ci.html
3 | # * https://github.com/supermarin/xcpretty#usage
4 |
5 | language: objective-c
6 | # cache: cocoapods
7 | # podfile: Example/Podfile
8 | before_install:
9 | - gem install cocoapods # Since Travis is not always on latest version
10 | - pod install --project-directory=Example
11 | install:
12 | - gem install xcpretty --no-rdoc --no-ri --no-document --quiet
13 | script:
14 | - set -o pipefail && xcodebuild test -workspace Example/HealthKitIO.xcworkspace -scheme HealthKitIO-Example -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty -c
15 | - pod lib lint --quick
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OS X
2 | .DS_Store
3 |
4 | # Xcode
5 | build/
6 | *.pbxuser
7 | !default.pbxuser
8 | *.mode1v3
9 | !default.mode1v3
10 | *.mode2v3
11 | !default.mode2v3
12 | *.perspectivev3
13 | !default.perspectivev3
14 | xcuserdata
15 | *.xccheckout
16 | profile
17 | *.moved-aside
18 | DerivedData
19 | *.hmap
20 | *.ipa
21 |
22 | # Bundler
23 | .bundle
24 |
25 | # We recommend against adding the Pods directory to your .gitignore. However
26 | # you should judge for yourself, the pros and cons are mentioned at:
27 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
28 | #
29 | # Note: if you ignore the Pods directory, make sure to uncomment
30 | # `pod install` in .travis.yml
31 | #
32 | Pods/
33 |
--------------------------------------------------------------------------------
/Pod/Classes/OMHError.m:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Open mHealth
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #import "OMHError.h"
18 |
19 | NSString *const OMHErrorDomain = @"org.openmhealth.Granola";
20 |
21 |
--------------------------------------------------------------------------------
/Example/Tests/Tests-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundlePackageType
14 | BNDL
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Example/Tests/OMHSampleFactory.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Open mHealth
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #import
18 | @import HealthKit;
19 |
20 | @interface OMHSampleFactory : NSObject
21 | + (HKSample*)typeIdentifier:(NSString*)typeIdentifier attrs:(NSDictionary*)attrs;
22 | + (HKSample*)typeIdentifier:(NSString*)typeIdentifier;
23 | @end
24 |
25 |
--------------------------------------------------------------------------------
/Example/Tests/OMHSchemaStore.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Open mHealth
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #import
18 |
19 | @interface OMHSchemaStore : NSObject
20 | + (BOOL)validateObject:(id)object
21 | againstSchemaAtPath:(NSString*)path
22 | withError:(NSError**)validationError;
23 | + (NSArray*) schemaPartialPaths;
24 |
25 | @end
26 |
27 |
--------------------------------------------------------------------------------
/Example/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Expecta (1.0.5)
3 | - Granola (0.6.0):
4 | - ObjectiveSugar (~> 1.1)
5 | - ObjectiveSugar (1.1.0)
6 | - Specta (1.0.5)
7 | - VVJSONSchemaValidation (1.4.0)
8 |
9 | DEPENDENCIES:
10 | - Expecta (~> 1.0)
11 | - Granola (from `../`)
12 | - Specta (~> 1.0)
13 | - VVJSONSchemaValidation (~> 1.3)
14 |
15 | SPEC REPOS:
16 | https://github.com/CocoaPods/Specs.git:
17 | - Expecta
18 | - ObjectiveSugar
19 | - Specta
20 | - VVJSONSchemaValidation
21 |
22 | EXTERNAL SOURCES:
23 | Granola:
24 | :path: "../"
25 |
26 | SPEC CHECKSUMS:
27 | Expecta: e1c022fcd33910b6be89c291d2775b3fe27a89fe
28 | Granola: 45b768c2c454eb3dc91c6b402b291437736892c3
29 | ObjectiveSugar: a6a25f23d657c19df0a0b972466d5b5ca9f5295c
30 | Specta: ac94d110b865115fe60ff2c6d7281053c6f8e8a2
31 | VVJSONSchemaValidation: 11f5ba886e80baccae5d606f3b10f20c1303200d
32 |
33 | PODFILE CHECKSUM: abd6914286eceb417a0fbf867e2c1826546083a7
34 |
35 | COCOAPODS: 1.8.4
36 |
--------------------------------------------------------------------------------
/Example/Tests/HKObject+Private.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Open mHealth
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #import
18 | @import HealthKit;
19 |
20 | @interface HKObject (Private)
21 |
22 | // source:
23 | // https://github.com/LeoNatan/Apple-Runtime-Headers/blob/master/iOS/Frameworks/HealthKit.framework/HKObject.h
24 | - (BOOL)_validateForSavingWithClientEntitlements:(id)arg1 applicationSDKVersion:(unsigned int)arg2 error:(id *)arg3;
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/Granola.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "Granola"
3 | s.version = "0.6.0"
4 | s.summary = "A healthful serializer for your HealthKit data."
5 | s.homepage = "https://github.com/openmhealth/Granola"
6 | s.license = { :type => 'Apache 2.0',
7 | :file => 'LICENSE' }
8 | s.authors = { "Brent Hargrave" => "brent@brent.is",
9 | "Chris Schaefbauer" => "chris.schaefbauer@openmhealth.org",
10 | "Emerson Farrugia" => "emerson@openmhealth.org",
11 | "Simona Carini" => "simona@openmhealth.org" }
12 | s.source = { :git => "https://github.com/openmhealth/Granola.git",
13 | :tag => s.version.to_s }
14 | s.social_media_url = 'https://twitter.com/openmhealth'
15 | s.platform = :ios, '11.0'
16 | s.requires_arc = true
17 | s.source_files = 'Pod/Classes/**/*'
18 | s.frameworks = 'HealthKit'
19 | s.dependency 'ObjectiveSugar', '~> 1.1'
20 | end
21 |
22 |
--------------------------------------------------------------------------------
/Pod/Classes/OMHError.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Open mHealth
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #import
18 |
19 | FOUNDATION_EXPORT NSString *const OMHErrorDomain;
20 |
21 | /**
22 | Error types for Granola used to identify different problematic conditions within the library.
23 | */
24 | typedef NS_ENUM(NSInteger, OMHErrorCode) {
25 |
26 | /** Indicates that the sample type is a valid type, but not currently supported by Granola.*/
27 | OMHErrorCodeUnsupportedType = 1000,
28 |
29 | /** Indicates that the input value is not supported by the specific method in Granola.*/
30 | OMHErrorCodeUnsupportedValues = 1001,
31 |
32 | /** Indicates that the sample type is the incorrect type for the specific method or serializer.*/
33 | OMHErrorCodeIncorrectType = 1002
34 | };
35 |
36 |
--------------------------------------------------------------------------------
/Pod/Classes/NSDate+RFC3339.m:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Open mHealth
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #import "NSDate+RFC3339.h"
18 |
19 | @implementation NSDate (RFC3339)
20 |
21 | + (NSDateFormatter*)RFC3339Formatter:(NSTimeZone*)timeZone {
22 |
23 | NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
24 |
25 | NSLocale* locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
26 |
27 | formatter.timeZone = timeZone;
28 | formatter.locale = locale;
29 | formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
30 |
31 | return formatter;
32 | }
33 |
34 | - (NSString *)RFC3339String {
35 | return [[[self class] RFC3339Formatter:[NSTimeZone defaultTimeZone]] stringFromDate:self];
36 | }
37 |
38 | - (NSString *)RFC3339StringAtTimeZone:(NSTimeZone*)timeZone {
39 | return [[[self class] RFC3339Formatter:timeZone] stringFromDate:self];
40 | }
41 |
42 | + (NSDate*)fromRFC3339String:(NSString*)dateString {
43 | return [[self RFC3339Formatter:[NSTimeZone defaultTimeZone]] dateFromString:dateString];
44 | }
45 |
46 | @end
47 |
48 |
--------------------------------------------------------------------------------
/Pod/Classes/OMHSerializer.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Open mHealth
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #import
18 | @import HealthKit;
19 | #import "OMHError.h"
20 |
21 | /**
22 | Translates HealthKit samples of varying types into JSON representations that conform with Open mHealth schemas.
23 | */
24 | @interface OMHSerializer : NSObject
25 |
26 | /**
27 | Returns a list of the HealthKit type identifiers that can be serialized to Open mHealth curated schemas. These are schemas that are not specific to Granola and are consistent with data points generated across the Open mHealth ecosystem.
28 |
29 | @return A list of the HealthKit type identifiers serializable to Open mHealth curated schemas.
30 | */
31 | + (NSArray*)supportedTypeIdentifiersWithOMHSchema;
32 |
33 | /**
34 | Lists all of the HealthKit type identifiers that are supported by Granola, regardless of whether they use Open mHealth curated schemas or Granola-based generic schemas.
35 |
36 | @return A list of all HealthKit type identifiers that are supported by Granola.
37 | */
38 | + (NSArray*)supportedTypeIdentifiers;
39 |
40 | + (NSString*)getCategoryValueForTypeWithValue: (HKCategoryType*) categoryType categoryValue:(NSInteger)categoryValue;
41 |
42 | /**
43 | Serializes HealthKit samples into Open mHealth compliant JSON data points.
44 | @param sample The HealthKit sample to be serialized.
45 | @param error An NSError that is passed by reference and can be checked to identify specific errors.
46 | @return A formatted JSON string containing the sample's data in a format that adheres to the appropriate Open mHealth schema.
47 | */
48 | - (NSString*)jsonForSample:(HKSample*)sample error:(NSError**)error;
49 |
50 | @end
51 |
52 |
--------------------------------------------------------------------------------
/Pod/Classes/NSDate+RFC3339.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Open mHealth
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #import
18 |
19 | /**
20 | Extension for the `NSDate` class to provide support for RFC3339 formatting.
21 |
22 | @warning Due to the limited precision of the `NSDate` class, we can only support millsecond-level precision for RFC3339 timestamps. In translating an `NSDate` to an RFC3339 string or vice versa, values with greater than millisecond-level precision may lose fidelity and have values that deviate from the expected value by a number of microseconds or nanoseconds.
23 | */
24 | @interface NSDate (RFC3339)
25 |
26 | /**
27 | Generates an RFC3339 formatted string representation of the object. This method uses the default timezone in generating the RFC3339 timestamp.
28 |
29 | @return An RFC3339 formatted string representation of the `NSDate` object.
30 | */
31 | - (NSString *)RFC3339String;
32 |
33 | /**
34 | Generates an RFC3339 formatted string representation of the object. This method uses the `timeZone` parameter as the offset for the RFC3339 timestamp.
35 |
36 | @param timeZone The timezone to use in rendering the RFC3339 timestamp.
37 |
38 | @return An RFC3339 formatted string representation of the `NSDate` object.
39 | */
40 | - (NSString *)RFC3339StringAtTimeZone:(NSTimeZone*)timeZone;
41 |
42 | /**
43 | Static method to create an `NSDate` object from an RFC3339 formatted timestamp.
44 |
45 | @param dateString The RFC3339 formatted string that you want to represent as an `NSDate` object.
46 |
47 | @return An `NSDate` that refers to the same _moment in time_ that the input string represented.
48 | */
49 | + (NSDate*)fromRFC3339String:(NSString*)dateString;
50 |
51 | @end
52 |
53 |
--------------------------------------------------------------------------------
/Example/Granola.xcodeproj/xcshareddata/xcschemes/Granola-Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
66 |
72 |
73 |
74 |
75 |
76 |
77 |
83 |
85 |
91 |
92 |
93 |
94 |
96 |
97 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/Example/Tests/NSDate+RFC3339Tests.m:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Open mHealth
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 |
18 | #import
19 | #import "NSDate+RFC3339.h"
20 |
21 | SpecBegin(NSDate)
22 |
23 | __block NSInteger offsetHours;
24 | __block NSCalendar* calendar;
25 | __block NSDateComponents* dateBuilder;
26 | __block float offsetNumber;
27 | __block float hour;
28 |
29 | beforeAll(^{
30 | offsetNumber = [[NSTimeZone defaultTimeZone] secondsFromGMT] / 3600;
31 |
32 | calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
33 |
34 | hour = 60*60;
35 | offsetHours = 6*hour; // +06:00 offset
36 | dateBuilder = [NSDateComponents new];
37 |
38 | dateBuilder.year = 2015;
39 | dateBuilder.day = 28;
40 | dateBuilder.month = 6;
41 | dateBuilder.hour = 8;
42 | dateBuilder.minute = 6;
43 | dateBuilder.second = 9;
44 | dateBuilder.nanosecond = 100000000;
45 |
46 | });
47 |
48 | describe(@"NSDate RFC3339 Formatter RFC3339String", ^{
49 |
50 | it(@"should create timestamps with default time zone when time zone is not provided",^{
51 |
52 | NSString* expectedDateString = @"2015-06-28T09:06:09.100+06:00";
53 |
54 | dateBuilder.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:(offsetHours-(1*hour))];
55 |
56 | NSDate* date = [calendar dateFromComponents:dateBuilder];
57 |
58 | [NSTimeZone setDefaultTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:offsetHours]];
59 |
60 | NSString* testDateString = [date RFC3339String];
61 | expect(testDateString).to.equal(expectedDateString);
62 | });
63 |
64 | it(@"should create timestamps with correct offset when date is created in UTC and time zone is provided",^{
65 |
66 | NSString* expectedDateString = @"2015-06-28T14:06:09.100+06:00";
67 |
68 | dateBuilder.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
69 |
70 | NSDate* date = [calendar dateFromComponents:dateBuilder];
71 | NSString* testDateString = [date RFC3339StringAtTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:offsetHours]];
72 | expect(testDateString).to.equal(expectedDateString);
73 | });
74 |
75 | it(@"should create timestamps with correct offset when date is created in offset from UTC and time zone provided",^{
76 |
77 | NSString* expectedDateString = @"2015-06-28T08:06:09.100+06:00";
78 |
79 | NSTimeZone* timezone = [NSTimeZone timeZoneForSecondsFromGMT:offsetHours];
80 |
81 | dateBuilder.timeZone = timezone;
82 |
83 | NSDate* date = [calendar dateFromComponents:dateBuilder];
84 | NSString* testDateString = [date RFC3339StringAtTimeZone:timezone];
85 | expect(testDateString).to.equal(expectedDateString);
86 | });
87 |
88 | it(@"should create timestamps with correct fractional offset when date is created in offset from UTC and fractional time zone provided",
89 | ^{
90 | NSString* expectedDateString = @"2015-06-28T13:36:09.100+05:30";
91 |
92 | dateBuilder.timeZone = [NSTimeZone timeZoneWithAbbreviation: @"UTC"];
93 |
94 | // +05:30 with not DST
95 | NSTimeZone* timezone = [NSTimeZone timeZoneWithName:@"Asia/Kolkata"];
96 |
97 | NSDate* date = [calendar dateFromComponents:dateBuilder];
98 | NSString* testDateString = [date RFC3339StringAtTimeZone:timezone];
99 | expect(testDateString).to.equal(expectedDateString);
100 | }
101 | );
102 |
103 | });
104 |
105 | describe(@"NSDate RFC3339 Formatter fromRFC3339String", ^{
106 |
107 | it(@"should create correct date when at second level precision", ^{
108 |
109 | NSString* testDateString = @"2016-02-19T05:35:10.000Z";
110 |
111 | NSDate* expectedDate = [NSDate dateWithTimeIntervalSince1970:[@1455860110 doubleValue]];
112 |
113 | NSDate* testDate = [NSDate fromRFC3339String:testDateString];
114 |
115 | expect(testDate).to.equal(expectedDate);
116 | });
117 |
118 | it(@"should create correct date at millisecond level precision", ^{
119 |
120 | NSString* testDateString = @"2016-02-19T05:35:10.282Z";
121 |
122 | NSDate* expectedDate = [NSDate dateWithTimeIntervalSince1970:[@1455860110.282 doubleValue]];
123 |
124 | NSDate* testDate = [NSDate fromRFC3339String:testDateString];
125 |
126 | expect(testDate).to.equal(expectedDate);
127 | });
128 |
129 | it(@"should create correct date when string contains non-UTC offset", ^{
130 |
131 | NSString* testDateString = @"2016-02-19T12:35:10.282+07:00";
132 |
133 | NSDate* expectedDate = [NSDate dateWithTimeIntervalSince1970:[@1455860110.282 doubleValue]];
134 |
135 | NSDate* testDate = [NSDate fromRFC3339String:testDateString];
136 |
137 | expect(testDate).to.equal(expectedDate);
138 | });
139 | });
140 |
141 | SpecEnd
142 |
143 |
--------------------------------------------------------------------------------
/Example/Tests/OMHSchemaStore.m:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Open mHealth
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #import "OMHSchemaStore.h"
18 | #import
19 | #import
20 |
21 | @interface OMHSchemaStore()
22 | @property (nonatomic, retain) VVMutableJSONSchemaStorage* storage;
23 | @end
24 |
25 | @implementation OMHSchemaStore
26 |
27 | + (BOOL)validateObject:(id)object
28 | againstSchemaAtPath:(NSString*)path
29 | withError:(NSError**)validationError {
30 | VVJSONSchema* schema = [[self sharedStore] schemaForPartialPath:path];
31 | return [schema validateObject:object withError:validationError];
32 | }
33 |
34 | #pragma mark - Private
35 |
36 | + (id)sharedStore {
37 | static OMHSchemaStore *shared = nil;
38 | static dispatch_once_t onceToken;
39 | dispatch_once(&onceToken, ^{
40 | shared = [[self alloc] init];
41 | if (shared) [shared loadSchemas];
42 | });
43 | return shared;
44 | }
45 |
46 | - (void)loadSchemas {
47 | _storage = [VVMutableJSONSchemaStorage storage];
48 | [[OMHSchemaStore schemaPartialPaths]
49 | each:^(NSString* partialPath) {
50 | NSURL* schemaURI = [self schemaURIForPartialPath:partialPath];
51 | // add schema at URI
52 | NSError *error = nil;
53 | VVJSONSchema* schema =
54 | [VVJSONSchema schemaWithData:[NSData dataWithContentsOfURL:schemaURI]
55 | baseURI:schemaURI
56 | referenceStorage:_storage
57 | error:&error];
58 | NSAssert(schema,
59 | @"Failed to create schema: %@, error: %@", schemaURI, error);
60 | NSAssert([_storage addSchema:schema],
61 | @"Failed to add schema to storage: %@", schemaURI);
62 | }];
63 | }
64 |
65 | - (VVJSONSchema*)schemaForPartialPath:(NSString*)path {
66 | NSURL* schemaURI = [self schemaURIForPartialPath:path];
67 | return [_storage schemaForURI:schemaURI];
68 | }
69 |
70 | - (NSURL*)schemaURIForPartialPath:(NSString*)fname {
71 | NSArray* components = [fname componentsSeparatedByString:@"/"];
72 | NSString* dirname =
73 | [NSString stringWithFormat:@"schema/%@", components.firstObject];
74 | NSBundle* bundle = [NSBundle bundleForClass:[self class]];
75 | NSURL* schemaURI =
76 | [bundle URLForResource:components.lastObject
77 | withExtension:@"json"
78 | subdirectory:dirname];
79 | NSAssert(schemaURI, @"No schema %@ in %@", components.lastObject, dirname);
80 | return schemaURI;
81 | }
82 |
83 | + (NSArray*)schemaPartialPaths{
84 | static NSArray* schemaPartialPaths = nil;
85 |
86 | if (schemaPartialPaths == nil)
87 | {
88 | schemaPartialPaths = @[
89 | @"omh/schema-id-1.x",
90 | @"omh/date-time-1.x",
91 | @"omh/unit-value-1.x",
92 | @"omh/duration-unit-value-1.x",
93 | @"omh/part-of-day-1.x",
94 | @"omh/time-interval-1.x",
95 | @"omh/time-frame-1.x",
96 | @"omh/header-1.x",
97 | @"omh/data-point-1.x",
98 | @"omh/step-count-1.x",
99 | @"omh/length-unit-value-1.x",
100 | @"omh/mass-unit-value-1.x",
101 | @"omh/temperature-unit-value-1.x",
102 | @"omh/descriptive-statistic-1.x",
103 | @"omh/body-height-1.x",
104 | @"omh/body-weight-1.x",
105 | @"omh/activity-name-1.x",
106 | @"omh/area-unit-value-1.x",
107 | @"omh/temporal-relationship-to-sleep-1.x",
108 | @"omh/blood-specimen-type-1.x",
109 | @"omh/temporal-relationship-to-meal-1.x",
110 | @"omh/blood-glucose-1.x",
111 | @"omh/position-during-measurement-1.x",
112 | @"omh/systolic-blood-pressure-1.x",
113 | @"omh/diastolic-blood-pressure-1.x",
114 | @"omh/blood-pressure-1.x",
115 | @"omh/temporal-relationship-to-physical-activity-1.x",
116 | @"omh/heart-rate-1.x",
117 | @"omh/body-mass-index-1.x",
118 | @"omh/sleep-duration-1.x",
119 | @"omh/physical-activity-1.x",
120 | @"omh/kcal-unit-value-1.x",
121 | @"omh/calories-burned-1.x",
122 | @"omh/body-fat-percentage-1.x",
123 | @"omh/oxygen-saturation-1.x",
124 | @"omh/respiratory-rate-1.x",
125 | @"omh/body-temperature-2.x",
126 | @"granola/hk-metadata-1.x",
127 | @"granola/hk-quantity-type-1.x",
128 | @"granola/hk-quantity-sample-1.x",
129 | @"granola/hk-category-type-1.x",
130 | @"granola/hk-category-sample-1.x",
131 | @"granola/hk-correlation-type-1.x",
132 | @"granola/hk-correlation-1.x",
133 | @"granola/hk-workout-1.x"];
134 | }
135 |
136 | return schemaPartialPaths;
137 | }
138 |
139 |
140 |
141 | @end
142 |
143 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Granola
2 |
3 | _*A healthful serializer for your HealthKit data.*_
4 |
5 | ## Overview
6 |
7 | So you want to store your app's [HealthKit](https://developer.apple.com/healthkit/)
8 | data somewhere *outside* of HealthKit, perhaps a remote server for analysis or
9 | backup? Use Granola to serialize your data.
10 |
11 | Granola spares you the effort of mapping HealthKit's API to JSON yourself,
12 | and emits JSON that validates against
13 | [schemas developed by Open mHealth](http://www.openmhealth.org/developers/schemas/)
14 | to ensure the data is intuitive and clinically meaningful.
15 |
16 |
17 | ***
18 |
19 | ## Installation
20 |
21 | ### CocoaPods
22 |
23 | Granola is available through [CocoaPods](http://cocoapods.org), a dependency manager for Objective-C. If you don't already have CocoaPods installed, you can install it with the following command:
24 | ```ruby
25 | $ gem install cocoapods
26 | ```
27 |
28 | To integrate Granola with your Xcode project, simply add the following line to your `Podfile`:
29 | ```ruby
30 | pod "Granola"
31 | ```
32 |
33 | and then run the following command:
34 | ```ruby
35 | $ pod install
36 | ```
37 |
38 | ***
39 |
40 | ## Usage
41 |
42 | ### Quick start
43 |
44 | First, be sure to study Apple's
45 | [HealthKit Framework Reference](https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Framework),
46 | which includes code samples illustrating how to ask your app's user for
47 | permission to access their HealthKit data, and how to query that data after
48 | you've gained permission.
49 |
50 | Now, let's say you want to see what a "steps" sample data point looks like
51 | serialized to JSON.
52 |
53 | ```objective-c
54 | // Granola includes OMHSerializer for serializing HealthKit data
55 | #import "OMHSerializer.h"
56 |
57 | // (initialize your HKHealthStore instance, request permissions with it)
58 | // ...
59 |
60 | // create a query for steps data
61 | HKSampleType *stepsSampleType =
62 | [HKSampleType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
63 | HKSampleQuery* query =
64 | [[HKSampleQuery alloc] initWithSampleType:stepsSampleType
65 | predicate:nil
66 | limit:HKObjectQueryNoLimit
67 | sortDescriptors:nil
68 | resultsHandler:^(HKSampleQuery *query,
69 | NSArray *results,
70 | NSError *error) {
71 | if (!results) abort();
72 | // pick a sample to serialize
73 | HKQuantitySample *sample = [results first];
74 | // create and use a serializer instance
75 | OMHSerializer *serializer = [OMHSerializer new];
76 | NSString* jsonString = [serializer jsonForSample:sample error:nil];
77 | NSLog(@"sample json: %@", jsonString);
78 | }];
79 | // run the query with your authorized HKHealthStore instance
80 | [self.healthStore executeQuery:query];
81 | ```
82 |
83 | Upon running your code, the console would render the data sample as Open mHealth compliant JSON:
84 |
85 | ```json
86 | {
87 | "body" : {
88 | "step_count" : 45,
89 | "effective_time_frame" : {
90 | "time_interval" : {
91 | "start_date_time" : "2015-05-12T18:58:06.969Z",
92 | "end_date_time" : "2015-05-12T18:58:32.524Z"
93 | }
94 | }
95 | },
96 | "header" : {
97 | "id" : "4A00E553-B01D-4757-ADD0-A4283BABAC6F",
98 | "creation_date_time" : "2015-05-12T18:58:06.969Z",
99 | "schema_id" : {
100 | "namespace" : "omh",
101 | "name" : "step-count",
102 | "version" : "1.0"
103 | }
104 | }
105 | }
106 | ```
107 |
108 | ### HKObjectType support
109 |
110 | The serializer has support for all HealthKit samples (`HKSample`), either through curated Open mHealth schemas or through generic HealthKit schemas. You can take a look at the [mapping table of supported types and their associated schemas](Docs/hkobject_type_coverage.md) to understand how data gets mapped. The `HKObjectType` identifiers are pulled from the
111 | [HealthKit Constant Reference](https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Constants/#//apple_ref/doc/uid/TP40014710-CH2-DontLinkElementID_3).
112 |
113 | You can retrieve a map (`NSDictionary`) of the supported types in Granola and the class name of the specific serializer they use by calling the static method:
114 | ```objective-c
115 | [OMHHealthKitConstantsMapper allSupportedTypeIdentifiersToClasses]
116 | ```
117 |
118 | You can also retrieve a list of those types, without their associated serializers, using the method:
119 | ```objective-c
120 | [OMHSerializer supportedTypeIdentifiers]
121 | ```
122 |
123 | And retrieve a list of types that serialize with Open mHealth curated schemas (instead of the generic type schemas) by using the method:
124 | ```objective-c
125 | [OMHSerializer supportedTypeIdentifiersWithOMHSchema]
126 | ```
127 |
128 | Over time, as curated schemas are developed that correspond to the HealthKit data represented by the generic schemas, the generic mappings will be replaced by mappings to the curated schemas.
129 |
130 | [Contact us](#contact) to request support for a particular type or
131 | [contribute](#contributing) support with a pull request.
132 |
133 | ### Time zones
134 |
135 | Granola uses the time zone specified for the `HKMetadataKeyTimeZone` key to serialize timestamps when it is present. If time zone metadata is not provided, Granola uses the default time zone of the application for the UTC offset in timestamps. Although these timestamps are correct, some data, especially older data, that is being serialized may be offset incorrectly if it was originally created by HealthKit in a different time zone than the device is in when Granola serializes it. For example, if a data point was originally created by HealthKit in San Francisco on June 1st, 2015 at 8:00am (-07:00), but then serialized three months later in New York, the timestamp would read 2015-06-01T11:00-04:00. These are technically the same point in time, however they are offset differently.
136 |
137 | In a future update, we plan to allow developers to specify the prefered time zone for serializing data points to give them control over how timestamps are serialized.
138 |
139 | ## Contact
140 |
141 | Have a question? Please [open an issue](https://github.com/openmhealth/Granola/issues/new)!
142 |
143 | Also, feel free to tweet at Open mHealth ([@openmhealth](http://twitter.com/openmhealth)).
144 |
145 |
146 | ## Contributing
147 |
148 | 1. Fork it
149 | 2. Create your feature branch (`git checkout -b my-new-feature`)
150 | 3. Commit your changes (`git commit -am 'Add some feature'`)
151 | 4. Push to the branch (`git push origin my-new-feature`)
152 | 5. Create new Pull Request
153 |
154 | To setup Granola for development, first pull the code. Then install CocoaPods:
155 | ```ruby
156 | $ gem install cocoapods
157 | ```
158 |
159 | After that, change into the Example directory:
160 | ```ruby
161 | $ cd Example
162 | ```
163 |
164 | and type:
165 | ```ruby
166 | pod install
167 | ```
168 |
169 | Then open the .xcworkspace file instead of the .xcodeproj file. This will allow you to make changes to the code and run and add tests for any changes you make.
170 |
171 |
172 | ## License
173 |
174 | Granola is available under the Apache 2 license. See the [LICENSE](/LICENSE) file for more info.
175 |
176 |
177 | ## Authors
178 |
179 | Brent Hargrave ([@brenthargrave](http://twitter.com/brenthargrave))
180 | Chris Schaefbauer (chris.schaefbauer@openmhealth.org)
181 | Emerson Farrugia (emerson@openmhealth.org)
182 |
183 |
--------------------------------------------------------------------------------
/Pod/Classes/OMHHealthKitConstantsMapper.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Open mHealth
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | Provides mappings between HealthKit domain constants and Open mHealth representations. This includes mappings from sample values that are represented by constants in HealthKit (e.g., workout activities and category values) and mappings from HealthKit sample types to the appropriate JSON serializer.
19 | */
20 | @interface OMHHealthKitConstantsMapper : NSObject
21 |
22 | /**
23 | Translates `HKWorkoutActivityType` constants into more semantically meaningful string representations.
24 |
25 | Used in generating values for workout types in serializing `HKWorkout` objects to JSON.
26 |
27 | @param enumValue The numeric value from the `HKWorkoutActivityType` enum.
28 |
29 | @return A string representation of the constant.
30 |
31 | @see [HKWorkoutActivityType](https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Constants/index.html#//apple_ref/c/tdef/HKWorkoutActivityType)
32 | @see [HKWorkout](https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HKWorkout_Class/index.html)
33 | */
34 | + (NSString*) stringForHKWorkoutActivityType:(int) enumValue;
35 |
36 | /**
37 | Translates `HKCategoryValueSleepAnalysis` constants into more semantically meaningful string representations.
38 |
39 | Used in generating values for sleep analysis in serializing samples of `HKCategoryValueSleepAnalysis` type, with the value _inBed_, into JSON.
40 |
41 | @param enumValue The numeric value from the `HKCategoryValueSleepAnalysis` enum.
42 |
43 | @return A string representation of the constant.
44 |
45 | @see [HKCategoryValueSleepAnalysis](https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Constants/index.html#//apple_ref/c/tdef/HKCategoryValueSleepAnalysis)
46 | @see [HKCategorySample](https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HKCategorySample_Class/index.html)
47 | */
48 | + (NSString*) stringForHKSleepAnalysisValue:(int) enumValue;
49 |
50 | /**
51 | Translates `HKCategoryValueAppleStandHour` constants into more semantically meaningful string representations.
52 |
53 | @param enumValue The numeric value from the `HKCategoryValueAppleStandHour` enum.
54 |
55 | @return A string representation of the constant.
56 |
57 | @see [HKCategoryValueAppleStandHour](https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Constants/index.html#//apple_ref/c/tdef/HKCategoryValueAppleStandHour)
58 | @see [HKCategorySample](https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HKCategorySample_Class/index.html)
59 | */
60 | + (NSString*) stringForHKAppleStandHourValue:(int) enumValue;
61 |
62 | /**
63 | Translates `HKCategoryValueCervicalMucusQuality` constants into more semantically meaningful string representations.
64 |
65 | @param enumValue The numeric value from the `HKCategoryValueCervicalMucusQuality` enum.
66 |
67 | @return A string representation of the constant.
68 |
69 | @see [HKCategoryValueCervicalMucusQuality](https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Constants/index.html#//apple_ref/c/tdef/HKCategoryValueCervicalMucusQuality)
70 | @see [HKCategorySample](https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HKCategorySample_Class/index.html)
71 | */
72 | + (NSString*) stringForHKCervicalMucusQualityValue:(int) enumValue;
73 |
74 |
75 | /**
76 | Translates `HKCategoryValueMenstrualFlow` constants into more semantically meaningful string representations.
77 |
78 | @param enumValue The numeric value from the `HKCategoryValueMenstrualFlow` enum.
79 |
80 | @return A string representation of the constant.
81 |
82 | @see [HKCategoryValueMenstrualFlow](https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Constants/index.html#//apple_ref/c/tdef/HKCategoryValueMenstrualFlow)
83 | @see [HKCategorySample](https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HKCategorySample_Class/index.html)
84 | */
85 | + (NSString*) stringForHKMenstrualFlowValue:(int) enumValue;
86 |
87 | /**
88 | Translates `HKCategoryValueOvulationTestResult` constant values into more semantically meaningful string representations.
89 |
90 | @param enumValue The constant value from the `HKCategoryValueOvulationTestResult` enum.
91 |
92 | @return A string representation of the constant.
93 |
94 | @see [HKCategoryValueOvulationTestResult](https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Constants/index.html#//apple_ref/c/tdef/HKCategoryValueOvulationTestResult)
95 | @see [HKCategorySample](https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HKCategorySample_Class/index.html)
96 | */
97 | + (NSString*) stringForHKOvulationTestResultValue:(int) enumValue;
98 |
99 | /**
100 | Describes the mappings between all HealthKit type identifiers suppported by Granola and the specific serializer class used by Granola to serialize samples of that type into JSON. The type identifiers are keys in the dictionary with their corresponding serializer class name as the value.
101 |
102 | This dictionary is an aggregation of all the type-specific dictionaries returned by allSupportedCategoryTypeIdentifiersToClasses, allSupportedCorrelationTypeIdentifiersToClass, and allSupportedQuantityTypeIdentifiersToClass, along with the HKWorkout type identifier and its associated serializer class.
103 |
104 | @return A dictionary containing tuples of the HealthKit types supported by Granola with the name of the serializer class they use.
105 |
106 | @see allSupportedCategoryTypeIdentifiersToClasses
107 | @see allSupportedCorrelationTypeIdentifiersToClass
108 | @see allSupportedQuantityTypeIdentifiersToClass
109 | */
110 | + (NSDictionary*) allSupportedTypeIdentifiersToClasses;
111 |
112 | /**
113 | Describes the mappings between HealthKit category type identifiers suppported by Granola and the specific serializer class used by Granola to serialize samples of that type into JSON. The type identifiers are keys in the dictionary with their corresponding serializer class name as the value.
114 |
115 | @return A dictionary containing tuples of the HealthKit category types supported by Granola with the name of the serializer class they use.
116 | */
117 | + (NSDictionary*) allSupportedCategoryTypeIdentifiersToClasses;
118 |
119 | /**
120 | Describes the mappings between HealthKit correlation type identifiers suppported by Granola and the specific serializer class used by Granola to serialize samples of that type into JSON. The type identifiers are keys in the dictionary with their corresponding serializer class name as the value.
121 |
122 | @return A dictionary containing tuples of the HealthKit correlation types supported by Granola with the name of the serializer class they use.
123 | */
124 | + (NSDictionary*) allSupportedCorrelationTypeIdentifiersToClass;
125 |
126 | /**
127 | Describes the mappings between HealthKit quantity type identifiers suppported by Granola and the specific serializer class used by Granola to serialize samples of that type into JSON. The type identifiers are keys in the dictionary with their corresponding serializer class name as the value.
128 |
129 | @return A dictionary containing tuples of the HealthKit quantity types supported by Granola with the name of the serializer class they use.
130 | */
131 | + (NSDictionary*) allSupportedQuantityTypeIdentifiersToClass;
132 |
133 | @end
134 |
135 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Example/Tests/OMHSampleFactory.m:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Open mHealth
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #import "OMHSampleFactory.h"
18 | #import "HKObject+Private.h"
19 | #import
20 | #include
21 |
22 | @implementation OMHSampleFactory
23 |
24 | + (HKSample*)typeIdentifier:(NSString*)sampleTypeIdentifier
25 | attrs:(NSDictionary*)attrs {
26 | id (^or)(id this, id that) = ^(id this, id that) {
27 | return (this) ? this : that;
28 | };
29 | NSCalendar* calendar = [NSCalendar currentCalendar];
30 | NSDate* defaultStart = [NSDate date];
31 | NSDate* defaultEnd = [calendar dateByAddingUnit:NSCalendarUnitSecond
32 | value:30
33 | toDate:defaultStart
34 | options:kNilOptions];
35 | NSDate* start = or(attrs[@"start"], defaultStart);
36 | NSDate* end = or(attrs[@"end"], defaultEnd);
37 | NSDictionary *metadata = nil;
38 | if(attrs[@"metadata"]){
39 | metadata = attrs[@"metadata"];
40 | }
41 | HKSample* sample = nil;
42 | if ([sampleTypeIdentifier hasPrefix:@"HKCategoryTypeIdentifier"]) {
43 | HKCategoryType* type =
44 | [HKObjectType categoryTypeForIdentifier:sampleTypeIdentifier];
45 | NSNumber *defaultValue = [NSNumber numberWithInt:0];
46 |
47 | // Apple starts their category enums at different values, for some reproductive health enums, it starts at 1 instead of 0
48 | if ([type.description isEqualToString:HKCategoryTypeIdentifierCervicalMucusQuality] || [type.description isEqualToString:HKCategoryTypeIdentifierOvulationTestResult] || [type.description isEqualToString:HKCategoryTypeIdentifierMenstrualFlow]){
49 |
50 | defaultValue = [NSNumber numberWithInt:1];
51 | }
52 |
53 | NSNumber *value = or(attrs[@"value"], defaultValue);
54 |
55 | if ([type.description isEqualToString:HKCategoryTypeIdentifierMenstrualFlow]) {
56 |
57 | // Menstrual flow category samples require an HKMetadataKeyMenstrualCycleStart entry. Without it, HealthKit will throw an exception.
58 | NSMutableDictionary *menstrualStartMetadata = [NSMutableDictionary dictionaryWithDictionary: @{ HKMetadataKeyMenstrualCycleStart : @true}];
59 |
60 | if (metadata !=nil){
61 | [menstrualStartMetadata addEntriesFromDictionary:metadata];
62 | }
63 |
64 | metadata = menstrualStartMetadata;
65 | }
66 |
67 | sample =
68 | [HKCategorySample categorySampleWithType:type
69 | value:[value integerValue]
70 | startDate:start
71 | endDate:end
72 | metadata:metadata];
73 | }
74 | else if (sampleTypeIdentifier == HKCorrelationTypeIdentifierBloodPressure) {
75 | NSSet* defaultSamples = [NSSet setWithArray:[@[
76 | HKQuantityTypeIdentifierBloodPressureSystolic,
77 | HKQuantityTypeIdentifierBloodPressureDiastolic
78 | ] map:^(NSString* identifier) {
79 | NSDate* sampledAt = [NSDate date];
80 | return [OMHSampleFactory typeIdentifier:identifier
81 | attrs:@{ @"start": sampledAt,
82 | @"end": sampledAt }];
83 | }]];
84 | NSSet* objects = or(attrs[@"objects"], defaultSamples);
85 | HKCorrelationType *bloodPressureType =
86 | [HKObjectType correlationTypeForIdentifier:HKCorrelationTypeIdentifierBloodPressure];
87 | NSDictionary *metadata = nil;
88 |
89 | sample =
90 | (HKSample*)[HKCorrelation correlationWithType:bloodPressureType
91 | startDate:start
92 | endDate:end
93 | objects:objects
94 | metadata:metadata];
95 | }
96 | else if (sampleTypeIdentifier == HKCorrelationTypeIdentifierFood){
97 | NSSet *nutritionContentSamples = nil;
98 | if(attrs[@"objects"]){
99 | nutritionContentSamples = attrs[@"objects"];
100 | }
101 | else{
102 | HKQuantitySample *defaultCarbQuantitySample = [HKQuantitySample quantitySampleWithType:[HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierDietaryCarbohydrates] quantity:[HKQuantity quantityWithUnit:[HKUnit unitFromString:@"g"] doubleValue:29.3] startDate:start endDate:end metadata:nil];
103 | HKQuantitySample *defaultCalorieQuantitySample = [HKQuantitySample quantitySampleWithType:[HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierDietaryEnergyConsumed] quantity:[HKQuantity quantityWithUnit:[HKUnit unitFromString:@"kcal"] doubleValue:105] startDate:start endDate:end metadata:nil];
104 | nutritionContentSamples = [NSSet setWithArray:@[defaultCarbQuantitySample,defaultCalorieQuantitySample]];
105 | }
106 | NSDictionary *metadata = nil;
107 |
108 | sample = (HKSample*)[HKCorrelation correlationWithType:[HKCorrelationType correlationTypeForIdentifier:HKCorrelationTypeIdentifierFood]
109 | startDate:start
110 | endDate:end
111 | objects:nutritionContentSamples
112 | metadata:metadata];
113 | }
114 | else if (sampleTypeIdentifier == HKWorkoutTypeIdentifier){
115 | //If we pass attributes into the sample factory, then they should be used in creating the sample
116 | if([attrs count]>0){
117 |
118 | NSTimeInterval duration = 100;
119 | int activityType = HKWorkoutActivityTypeCycling;
120 | if(attrs[@"activity_type"]){
121 | NSString *activityTypeStr = (NSString*)attrs[@"activity_type"];
122 | activityType = [activityTypeStr intValue];
123 | }
124 |
125 | //If one of these three keys has been set in the attributes that were passed to the factory, then we want to create a more complex workout sample
126 | if([attrs hasKey:@"duration"] || [attrs hasKey:@"energy_burned"] || [attrs hasKey:@"distance"]){
127 | if(attrs[@"duration"]){
128 | NSString *timeIntervalStr = (NSString*)attrs[@"duration"];
129 | duration = [timeIntervalStr doubleValue];
130 | }
131 |
132 | NSDictionary *metadata = nil;
133 |
134 | HKQuantity *defaultEnergyBurned = [HKQuantity quantityWithUnit:[HKUnit unitFromString:@"kcal"] doubleValue:120.1];
135 | HKQuantity *defaultDistance = [HKQuantity quantityWithUnit:[HKUnit unitFromString:@"km"] doubleValue:5.2];
136 | sample = (HKSample*)[HKWorkout workoutWithActivityType:activityType startDate:or(attrs[@"start"],defaultStart) endDate:or(attrs[@"end"],defaultEnd) duration:duration totalEnergyBurned:or(attrs[@"energy_burned"],defaultEnergyBurned) totalDistance:or(attrs[@"distance"],defaultDistance) metadata:metadata];
137 | }
138 | //If we do not have keys in the attribute dictionary for the more complex pieces of workout data, then we can create a simple workout sample
139 | else{
140 | sample = (HKSample*)[HKWorkout workoutWithActivityType:activityType startDate:or(attrs[@"start"],defaultStart) endDate:or(attrs[@"end"],defaultEnd)];
141 | }
142 |
143 | }
144 | //If no attributes are passed into the sample factory, then we can create a simple workout with start and end date set to now
145 | else{
146 | sample = (HKSample*)[HKWorkout workoutWithActivityType:HKWorkoutActivityTypeCycling startDate:[NSDate date] endDate:[NSDate date]];
147 | }
148 |
149 | }
150 | else if ([@[ HKQuantityTypeIdentifierHeight,
151 | HKQuantityTypeIdentifierBodyMass,
152 | HKQuantityTypeIdentifierHeartRate,
153 | HKQuantityTypeIdentifierStepCount,
154 | HKQuantityTypeIdentifierNikeFuel,
155 | HKQuantityTypeIdentifierBloodGlucose,
156 | HKQuantityTypeIdentifierBasalEnergyBurned,
157 | HKQuantityTypeIdentifierActiveEnergyBurned,
158 | HKQuantityTypeIdentifierBloodPressureSystolic,
159 | HKQuantityTypeIdentifierBloodPressureDiastolic,
160 | HKQuantityTypeIdentifierBodyMassIndex,
161 | HKQuantityTypeIdentifierDietaryBiotin,
162 | HKQuantityTypeIdentifierDietaryWater,
163 | HKQuantityTypeIdentifierInhalerUsage,
164 | HKQuantityTypeIdentifierDietaryCarbohydrates,
165 | HKQuantityTypeIdentifierDietaryEnergyConsumed,
166 | HKQuantityTypeIdentifierUVExposure,
167 | HKQuantityTypeIdentifierOxygenSaturation,
168 | HKQuantityTypeIdentifierBodyFatPercentage,
169 | HKQuantityTypeIdentifierRespiratoryRate,
170 | HKQuantityTypeIdentifierBodyTemperature,
171 | HKQuantityTypeIdentifierBasalBodyTemperature,
172 | HKQuantityTypeIdentifierBloodAlcoholContent
173 | ] includes:sampleTypeIdentifier]) {
174 |
175 | NSString* defaultUnitString = nil;
176 | if (sampleTypeIdentifier == HKQuantityTypeIdentifierHeight) {
177 | defaultUnitString = @"cm";
178 | } else if (sampleTypeIdentifier == HKQuantityTypeIdentifierBodyMass) {
179 | defaultUnitString = @"lb";
180 | } else if (sampleTypeIdentifier == HKQuantityTypeIdentifierHeartRate || sampleTypeIdentifier == HKQuantityTypeIdentifierRespiratoryRate) {
181 | defaultUnitString = @"count/min";
182 | } else if (sampleTypeIdentifier == HKQuantityTypeIdentifierBloodGlucose) {
183 | defaultUnitString = @"mg/dL";
184 | } else if ([sampleTypeIdentifier containsString:@"EnergyBurned"]) {
185 | defaultUnitString = @"kcal";
186 | } else if ([sampleTypeIdentifier containsString:@"HKQuantityTypeIdentifierBloodPressure"]) {
187 | defaultUnitString = @"mmHg";
188 | } else if (sampleTypeIdentifier == HKQuantityTypeIdentifierDietaryBiotin) {
189 | defaultUnitString = @"mcg";
190 | } else if (sampleTypeIdentifier == HKQuantityTypeIdentifierDietaryWater) {
191 | defaultUnitString = @"L";
192 | } else if (sampleTypeIdentifier == HKQuantityTypeIdentifierOxygenSaturation || sampleTypeIdentifier == HKQuantityTypeIdentifierBodyFatPercentage
193 | || sampleTypeIdentifier == HKQuantityTypeIdentifierBloodAlcoholContent) {
194 | defaultUnitString = @"%";
195 | } else if (sampleTypeIdentifier == HKQuantityTypeIdentifierBodyTemperature || sampleTypeIdentifier == HKQuantityTypeIdentifierBasalBodyTemperature) {
196 | defaultUnitString = @"degC";
197 | } else {
198 | defaultUnitString = @"count";
199 | };
200 | NSString* unitString = or(attrs[@"unitString"], defaultUnitString);
201 |
202 | NSNumber* defaultValue =
203 | [NSNumber numberWithInteger:arc4random_uniform(74)];
204 | NSNumber* value = or(attrs[@"value"], defaultValue);
205 |
206 | HKUnit* unit = [HKUnit unitFromString:unitString];
207 | HKQuantityType* quantityType =
208 | [HKObjectType quantityTypeForIdentifier:sampleTypeIdentifier];
209 | HKQuantity* quantity = [HKQuantity quantityWithUnit:unit
210 | doubleValue:value.doubleValue];
211 | NSDictionary *metadata = nil;
212 | if(attrs[@"metadata"]){
213 | metadata = attrs[@"metadata"];
214 | }
215 |
216 | sample =
217 | [HKQuantitySample quantitySampleWithType:quantityType
218 | quantity:quantity
219 | startDate:start
220 | endDate:end
221 | metadata:metadata];
222 | }
223 | // validate sample object using HK private API, exposed via category,
224 | // see HKObject+Private.h
225 | // NOTE: throws _HKObjectValidationFailureException if invalid
226 |
227 | [sample _validateForSavingWithClientEntitlements:nil applicationSDKVersion:11 error:nil];
228 | return sample;
229 | }
230 |
231 | + (HKSample*)typeIdentifier:(NSString*)typeIdentifier {
232 | return [self typeIdentifier:typeIdentifier attrs:@{}];
233 | }
234 |
235 | @end
236 |
237 |
--------------------------------------------------------------------------------
/Docs/hkobject_type_coverage.md:
--------------------------------------------------------------------------------
1 | ## HKObjectType Coverage
2 |
3 | |HKObject type|Granola|Open mHealth schema|
4 | |-------------|:---------:|-------------|
5 | |*__Nutrition Identifiers__*|||
6 | |HKQuantityTypeIdentifierDietaryBiotin|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
7 | |HKQuantityTypeIdentifierDietaryCaffeine|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
8 | |HKQuantityTypeIdentifierDietaryCalcium|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
9 | |HKQuantityTypeIdentifierDietaryCarbohydrates|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
10 | |HKQuantityTypeIdentifierDietaryChloride|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
11 | |HKQuantityTypeIdentifierDietaryCholesterol|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
12 | |HKQuantityTypeIdentifierDietaryChromium|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
13 | |HKQuantityTypeIdentifierDietaryCopper|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
14 | |HKQuantityTypeIdentifierDietaryEnergyConsumed|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
15 | |HKQuantityTypeIdentifierDietaryFatMonounsaturated|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
16 | |HKQuantityTypeIdentifierDietaryFatPolyunsaturated|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
17 | |HKQuantityTypeIdentifierDietaryFatSaturated|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
18 | |HKQuantityTypeIdentifierDietaryFatTotal|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
19 | |HKQuantityTypeIdentifierDietaryFiber|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
20 | |HKQuantityTypeIdentifierDietaryFolate|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
21 | |HKQuantityTypeIdentifierDietaryIodine|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
22 | |HKQuantityTypeIdentifierDietaryIron|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
23 | |HKQuantityTypeIdentifierDietaryMagnesium|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
24 | |HKQuantityTypeIdentifierDietaryManganese|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
25 | |HKQuantityTypeIdentifierDietaryMolybdenum|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
26 | |HKQuantityTypeIdentifierDietaryNiacin|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
27 | |HKQuantityTypeIdentifierDietaryPantothenicAcid|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
28 | |HKQuantityTypeIdentifierDietaryPhosphorus|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
29 | |HKQuantityTypeIdentifierDietaryPotassium|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
30 | |HKQuantityTypeIdentifierDietaryProtein|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
31 | |HKQuantityTypeIdentifierDietaryRiboflavin|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
32 | |HKQuantityTypeIdentifierDietarySelenium|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
33 | |HKQuantityTypeIdentifierDietarySodium|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
34 | |HKQuantityTypeIdentifierDietarySugar|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
35 | |HKQuantityTypeIdentifierDietaryThiamin|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
36 | |HKQuantityTypeIdentifierDietaryVitaminA|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
37 | |HKQuantityTypeIdentifierDietaryVitaminB12|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
38 | |HKQuantityTypeIdentifierDietaryVitaminB6|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
39 | |HKQuantityTypeIdentifierDietaryVitaminC|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
40 | |HKQuantityTypeIdentifierDietaryVitaminD|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
41 | |HKQuantityTypeIdentifierDietaryVitaminE|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
42 | |HKQuantityTypeIdentifierDietaryVitaminK|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
43 | |HKQuantityTypeIdentifierDietaryWater|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
44 | |HKQuantityTypeIdentifierDietaryZinc|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
45 | |*__Body Measurements__*|||
46 | |HKQuantityTypeIdentifierBodyMassIndex|:white_check_mark:|[omh:body-mass-index:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-mass-index)|
47 | |HKQuantityTypeIdentifierBodyFatPercentage|:white_check_mark:|[omh:body-fat-percentage:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-fat-percentage)|
48 | |HKQuantityTypeIdentifierHeight|:white_check_mark:|[omh:body-height:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-height)|
49 | |HKQuantityTypeIdentifierBodyMass|:white_check_mark:|[omh:body-weight:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-weight)|
50 | |HKQuantityTypeIdentifierLeanBodyMass|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
51 | |*__Results Identifiers__*|||
52 | |HKQuantityTypeIdentifierOxygenSaturation|:white_check_mark:|[omh:oxygen-saturation:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_oxygen-saturation)|
53 | |HKQuantityTypeIdentifierPeripheralPerfusionIndex|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
54 | |HKQuantityTypeIdentifierBloodGlucose|:white_check_mark:|[omh:blood-glucose:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_blood-glucose)|
55 | |HKQuantityTypeIdentifierNumberOfTimesFallen|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
56 | |HKQuantityTypeIdentifierElectrodermalActivity|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
57 | |HKQuantityTypeIdentifierInhalerUsage|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
58 | |HKQuantityTypeIdentifierBloodAlcoholContent|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
59 | |HKQuantityTypeIdentifierForcedVitalCapacity|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
60 | |HKQuantityTypeIdentifierForcedExpiratoryVolume1|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
61 | |HKQuantityTypeIdentifierPeakExpiratoryFlowRate|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
62 | |HKQuantityTypeIdentifierUVExposure|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
63 | |*__Correlation Identifiers__*|||
64 | |HKCorrelationTypeIdentifierBloodPressure|:white_check_mark:|[omh:blood-pressure:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_blood-pressure)|
65 | |HKCorrelationTypeIdentifierFood|:white_check_mark:|[granola:hk-correlation:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-correlation)|
66 | |*__Sleep Identifiers__*|||
67 | |HKCategoryTypeIdentifierSleepAnalysis - Asleep|:white_check_mark:|[omh:sleep-duration:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_sleep-duration)|
68 | |HKCategoryTypeIdentifierSleepAnalysis - InBed|:white_check_mark:|[granola:hk-category-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-category-sample)|
69 | |*__Workout Identifier__*|||
70 | |HKWorkoutTypeIdentifier|:white_check_mark:|[granola:hk-workout:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-workout)|
71 | |*__Fitness Identifiers__*|||
72 | |HKQuantityTypeIdentifierStepCount|:white_check_mark:|[omh:step-count:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_step-count)|
73 | |HKQuantityTypeIdentifierDistanceWalkingRunning|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
74 | |HKQuantityTypeIdentifierDistanceCycling|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
75 | |HKQuantityTypeIdentifierBasalEnergyBurned|:white_check_mark:|[omh:calories-burned:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_calories-burned)|
76 | |HKQuantityTypeIdentifierActiveEnergyBurned|:white_check_mark:|[omh:calories-burned:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_calories-burned)|
77 | |HKQuantityTypeIdentifierFlightsClimbed|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
78 | |HKQuantityTypeIdentifierNikeFuel|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
79 | |HKCategoryTypeIdentifierAppleStandHour|:white_check_mark:|[granola:hk-category-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-category-sample)|
80 | |*__Vital Signs Identifiers__*|||
81 | |HKQuantityTypeIdentifierHeartRate|:white_check_mark:|[omh:heart-rate:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_heart-rate)|
82 | |HKQuantityTypeIdentifierBodyTemperature|:white_check_mark:|[omh:body-temperature:2.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-temperature)|
83 | |HKQuantityTypeIdentifierBasalBodyTemperature|:white_check_mark:|[omh:body-temperature:2.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-temperature)|
84 | |HKQuantityTypeIdentifierBloodPressureSystolic|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
85 | |HKQuantityTypeIdentifierBloodPressureDiastolic|:white_check_mark:|[granola:hk-quantity-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample)|
86 | |HKQuantityTypeIdentifierRespiratoryRate|:white_check_mark:|[omh:respiratory-rate:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_respiratory-rate)|
87 | |*__Sexual and Reproductive Health Identifiers__*|||
88 | |HKCategoryTypeIdentifierCervicalMucusQuality|:white_check_mark:|[granola:hk-category-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-category-sample)|
89 | |HKCategoryTypeIdentifierOvulationTestResult|:white_check_mark:|[granola:hk-category-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-category-sample)|
90 | |HKCategoryTypeIdentifierMenstrualFlow|:white_check_mark:|[granola:hk-category-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-category-sample)|
91 | |HKCategoryTypeIdentifierIntermenstrualBleeding|:white_check_mark:|[granola:hk-category-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-category-sample)|
92 | |HKCategoryTypeIdentifierSexualActivity|:white_check_mark:|[granola:hk-category-sample:1.x](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-category-sample)|
93 | |*__Characteristics Identifiers__*|||
94 | |HKCharacteristicTypeIdentifierBiologicalSex|||
95 | |HKCharacteristicTypeIdentifierBloodType|||
96 | |HKCharacteristicTypeIdentifierDateOfBirth|||
97 | |HKCharacteristicTypeIdentifierFitzpatrickSkinType|||
98 |
99 |
--------------------------------------------------------------------------------
/Pod/Classes/OMHHealthKitConstantsMapper.m:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Open mHealth
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #import
18 | #import
19 | #import "OMHHealthKitConstantsMapper.h"
20 |
21 | @implementation OMHHealthKitConstantsMapper
22 | + (NSString*)stringForHKWorkoutActivityType:(int) enumValue{
23 | switch( enumValue ){
24 | case HKWorkoutActivityTypeAmericanFootball:
25 | return @"HKWorkoutActivityTypeAmericanFootball";
26 | case HKWorkoutActivityTypeArchery:
27 | return @"HKWorkoutActivityTypeArchery";
28 | case HKWorkoutActivityTypeAustralianFootball:
29 | return @"HKWorkoutActivityTypeAustralianFootball";
30 | case HKWorkoutActivityTypeBadminton:
31 | return @"HKWorkoutActivityTypeBadminton";
32 | case HKWorkoutActivityTypeBaseball:
33 | return @"HKWorkoutActivityTypeBaseball";
34 | case HKWorkoutActivityTypeBasketball:
35 | return @"HKWorkoutActivityTypeBasketball";
36 | case HKWorkoutActivityTypeBowling:
37 | return @"HKWorkoutActivityTypeBowling";
38 | case HKWorkoutActivityTypeBoxing:
39 | return @"HKWorkoutActivityTypeBoxing";
40 | case HKWorkoutActivityTypeClimbing:
41 | return @"HKWorkoutActivityTypeClimbing";
42 | case HKWorkoutActivityTypeCricket:
43 | return @"HKWorkoutActivityTypeCricket";
44 | case HKWorkoutActivityTypeCrossTraining:
45 | return @"HKWorkoutActivityTypeCrossTraining";
46 | case HKWorkoutActivityTypeCurling:
47 | return @"HKWorkoutActivityTypeCurling";
48 | case HKWorkoutActivityTypeCycling:
49 | return @"HKWorkoutActivityTypeCycling";
50 | case HKWorkoutActivityTypeDance:
51 | return @"HKWorkoutActivityTypeDance";
52 | case HKWorkoutActivityTypeDanceInspiredTraining:
53 | return @"HKWorkoutActivityTypeDanceInspiredTraining";
54 | case HKWorkoutActivityTypeElliptical:
55 | return @"HKWorkoutActivityTypeElliptical";
56 | case HKWorkoutActivityTypeEquestrianSports:
57 | return @"HKWorkoutActivityTypeEquestrianSports";
58 | case HKWorkoutActivityTypeFencing:
59 | return @"HKWorkoutActivityTypeFencing";
60 | case HKWorkoutActivityTypeFishing:
61 | return @"HKWorkoutActivityTypeFishing";
62 | case HKWorkoutActivityTypeFunctionalStrengthTraining:
63 | return @"HKWorkoutActivityTypeFunctionalStrengthTraining";
64 | case HKWorkoutActivityTypeGolf:
65 | return @"HKWorkoutActivityTypeGolf";
66 | case HKWorkoutActivityTypeGymnastics:
67 | return @"HKWorkoutActivityTypeGymnastics";
68 | case HKWorkoutActivityTypeHandball:
69 | return @"HKWorkoutActivityTypeHandball";
70 | case HKWorkoutActivityTypeHiking:
71 | return @"HKWorkoutActivityTypeHiking";
72 | case HKWorkoutActivityTypeHockey:
73 | return @"HKWorkoutActivityTypeHockey";
74 | case HKWorkoutActivityTypeHunting:
75 | return @"HKWorkoutActivityTypeHunting";
76 | case HKWorkoutActivityTypeLacrosse:
77 | return @"HKWorkoutActivityTypeLacrosse";
78 | case HKWorkoutActivityTypeMartialArts:
79 | return @"HKWorkoutActivityTypeMartialArts";
80 | case HKWorkoutActivityTypeMindAndBody:
81 | return @"HKWorkoutActivityTypeMindAndBody";
82 | case HKWorkoutActivityTypeMixedMetabolicCardioTraining:
83 | return @"HKWorkoutActivityTypeMixedMetabolicCardioTraining";
84 | case HKWorkoutActivityTypePaddleSports:
85 | return @"HKWorkoutActivityTypePaddleSports";
86 | case HKWorkoutActivityTypePlay:
87 | return @"HKWorkoutActivityTypePlay";
88 | case HKWorkoutActivityTypePreparationAndRecovery:
89 | return @"HKWorkoutActivityTypePreparationAndRecovery";
90 | case HKWorkoutActivityTypeRacquetball:
91 | return @"HKWorkoutActivityTypeRacquetball";
92 | case HKWorkoutActivityTypeRowing:
93 | return @"HKWorkoutActivityTypeRowing";
94 | case HKWorkoutActivityTypeRugby:
95 | return @"HKWorkoutActivityTypeRugby";
96 | case HKWorkoutActivityTypeRunning:
97 | return @"HKWorkoutActivityTypeRunning";
98 | case HKWorkoutActivityTypeSailing:
99 | return @"HKWorkoutActivityTypeSailing";
100 | case HKWorkoutActivityTypeSkatingSports:
101 | return @"HKWorkoutActivityTypeSkatingSports";
102 | case HKWorkoutActivityTypeSnowSports:
103 | return @"HKWorkoutActivityTypeSnowSports";
104 | case HKWorkoutActivityTypeSoccer:
105 | return @"HKWorkoutActivityTypeSoccer";
106 | case HKWorkoutActivityTypeSoftball:
107 | return @"HKWorkoutActivityTypeSoftball";
108 | case HKWorkoutActivityTypeSquash:
109 | return @"HKWorkoutActivityTypeSquash";
110 | case HKWorkoutActivityTypeStairClimbing:
111 | return @"HKWorkoutActivityTypeStairClimbing";
112 | case HKWorkoutActivityTypeSurfingSports:
113 | return @"HKWorkoutActivityTypeSurfingSports";
114 | case HKWorkoutActivityTypeSwimming:
115 | return @"HKWorkoutActivityTypeSwimming";
116 | case HKWorkoutActivityTypeTableTennis:
117 | return @"HKWorkoutActivityTypeTableTennis";
118 | case HKWorkoutActivityTypeTennis:
119 | return @"HKWorkoutActivityTypeTennis";
120 | case HKWorkoutActivityTypeTrackAndField:
121 | return @"HKWorkoutActivityTypeTrackAndField";
122 | case HKWorkoutActivityTypeTraditionalStrengthTraining:
123 | return @"HKWorkoutActivityTypeTraditionalStrengthTraining";
124 | case HKWorkoutActivityTypeVolleyball:
125 | return @"HKWorkoutActivityTypeVolleyball";
126 | case HKWorkoutActivityTypeWalking:
127 | return @"HKWorkoutActivityTypeWalking";
128 | case HKWorkoutActivityTypeWaterFitness:
129 | return @"HKWorkoutActivityTypeWaterFitness";
130 | case HKWorkoutActivityTypeWaterPolo:
131 | return @"HKWorkoutActivityTypeWaterPolo";
132 | case HKWorkoutActivityTypeWaterSports:
133 | return @"HKWorkoutActivityTypeWaterSports";
134 | case HKWorkoutActivityTypeWrestling:
135 | return @"HKWorkoutActivityTypeWrestling";
136 | case HKWorkoutActivityTypeYoga:
137 | return @"HKWorkoutActivityTypeYoga";
138 | case HKWorkoutActivityTypeOther:
139 | return @"HKWorkoutActivityTypeOther";
140 |
141 | // iOS 10+
142 | case HKWorkoutActivityTypeBarre:
143 | return @"HKWorkoutActivityTypeBarre";
144 | case HKWorkoutActivityTypeCoreTraining:
145 | return @"HKWorkoutActivityTypeCoreTraining";
146 | case HKWorkoutActivityTypeCrossCountrySkiing:
147 | return @"HKWorkoutActivityTypeCrossCountrySkiing";
148 | case HKWorkoutActivityTypeDownhillSkiing:
149 | return @"HKWorkoutActivityTypeDownhillSkiing";
150 | case HKWorkoutActivityTypeFlexibility:
151 | return @"HKWorkoutActivityTypeFlexibility";
152 | case HKWorkoutActivityTypeHighIntensityIntervalTraining:
153 | return @"HKWorkoutActivityTypeHighIntensityIntervalTraining";
154 | case HKWorkoutActivityTypeJumpRope:
155 | return @"HKWorkoutActivityTypeJumpRope";
156 | case HKWorkoutActivityTypeKickboxing:
157 | return @"HKWorkoutActivityTypeKickboxing";
158 | case HKWorkoutActivityTypePilates:
159 | return @"HKWorkoutActivityTypePilates";
160 | case HKWorkoutActivityTypeSnowboarding:
161 | return @"HKWorkoutActivityTypeSnowboarding";
162 | case HKWorkoutActivityTypeStairs:
163 | return @"HKWorkoutActivityTypeStairs";
164 | case HKWorkoutActivityTypeStepTraining:
165 | return @"HKWorkoutActivityTypeStepTraining";
166 | case HKWorkoutActivityTypeWheelchairWalkPace:
167 | return @"HKWorkoutActivityTypeWheelchairWalkPace";
168 | case HKWorkoutActivityTypeWheelchairRunPace:
169 | return @"HKWorkoutActivityTypeWheelchairRunPace";
170 | case HKWorkoutActivityTypeTaiChi:
171 | return @"HKWorkoutActivityTypeTaiChi";
172 | case HKWorkoutActivityTypeMixedCardio:
173 | return @"HKWorkoutActivityTypeMixedCardio";
174 | case HKWorkoutActivityTypeHandCycling:
175 | return @"HKWorkoutActivityTypeHandCycling";
176 | case HKWorkoutActivityTypeDiscSports:
177 | return @"HKWorkoutActivityTypeDiscSports";
178 | case HKWorkoutActivityTypeFitnessGaming:
179 | return @"HKWorkoutActivityTypeFitnessGaming";
180 |
181 | default:{
182 | return @"HKWorkoutActivityTypeOther";
183 | }
184 | }
185 | }
186 |
187 | + (NSString*) stringForHKSleepAnalysisValue:(int) enumValue {
188 | switch (enumValue) {
189 | case HKCategoryValueSleepAnalysisInBed:
190 | return @"InBed";
191 | break;
192 | case HKCategoryValueSleepAnalysisAsleep:
193 | return @"Asleep";
194 | break;
195 | case HKCategoryValueSleepAnalysisAwake:
196 | return @"Awake";
197 | break;
198 | default:{
199 | NSException *e = [NSException
200 | exceptionWithName:@"HKCategoryValueSleepAnalysisInvalidValue"
201 | reason:@"HKCategoryValueSleepAnalysis can only have a HKCategoryValueSleepAnalysisInBed or HKCategoryValueSleepAnalysisAsleep value"
202 | userInfo:nil];
203 | @throw e;
204 | }
205 | }
206 | }
207 |
208 | + (NSString*) stringForHKAppleStandHourValue:(int) enumValue {
209 | switch (enumValue) {
210 | case HKCategoryValueAppleStandHourIdle:
211 | return @"Idle";
212 | case HKCategoryValueAppleStandHourStood:
213 | return @"Standing";
214 | default:{
215 | NSException *e = [NSException
216 | exceptionWithName:@"HKCategoryValueAppleStandHourInvalidValue"
217 | reason:@"HKCategoryValueAppleStandHour can only have a HKCategoryValueAppleStandHourIdle or HKCategoryValueAppleStandHourStood value"
218 | userInfo:nil];
219 | @throw e;
220 | }
221 | }
222 | }
223 |
224 | + (NSString*) stringForHKCervicalMucusQualityValue:(int) enumValue {
225 | switch (enumValue) {
226 | case HKCategoryValueCervicalMucusQualityCreamy:
227 | return @"Creamy";
228 | case HKCategoryValueCervicalMucusQualityDry:
229 | return @"Dry";
230 | case HKCategoryValueCervicalMucusQualityEggWhite:
231 | return @"Egg white";
232 | case HKCategoryValueCervicalMucusQualitySticky:
233 | return @"Sticky";
234 | case HKCategoryValueCervicalMucusQualityWatery:
235 | return @"Watery";
236 | default:{
237 | NSException *e = [NSException
238 | exceptionWithName:@"HKCategoryValueCervicalMucusQualityInvalidValue"
239 | reason:@"HKCategoryValueCervicalMucusQuality can only have a value specified in the HKCategoryValueCervicalMucusQuality enum"
240 | userInfo:nil];
241 | @throw e;
242 | }
243 | }
244 | }
245 |
246 | + (NSString*) stringForHKMenstrualFlowValue:(int) enumValue {
247 | switch (enumValue) {
248 | case HKCategoryValueMenstrualFlowUnspecified:
249 | return @"Unspecified";
250 | case HKCategoryValueMenstrualFlowLight:
251 | return @"Light";
252 | case HKCategoryValueMenstrualFlowMedium:
253 | return @"Medium";
254 | case HKCategoryValueMenstrualFlowHeavy:
255 | return @"Heavy";
256 | default:{
257 | NSException *e = [NSException
258 | exceptionWithName:@"HKCategoryValueMenstrualFlowInvalidValue"
259 | reason:@"HKCategoryValueMenstrualFlow can only have a value specified in the HKCategoryValueMenstrualFlow enum"
260 | userInfo:nil];
261 | @throw e;
262 | }
263 | }
264 | }
265 |
266 | + (NSString*) stringForHKOvulationTestResultValue:(int) enumValue {
267 | switch (enumValue) {
268 | case HKCategoryValueOvulationTestResultNegative:
269 | return @"Negative";
270 | case HKCategoryValueOvulationTestResultPositive:
271 | return @"Positive";
272 | case HKCategoryValueOvulationTestResultIndeterminate:
273 | return @"Indeterminate";
274 | default:{
275 | NSException *e = [NSException
276 | exceptionWithName:@"HKCategoryValueOvulationTestResultInvalidValue"
277 | reason:@"HKCategoryValueOvulationTestResult can only have a value specified in the HKCategoryValueOvulationTestResult enum"
278 | userInfo:nil];
279 | @throw e;
280 | }
281 | }
282 | }
283 |
284 | + (NSDictionary*)allSupportedTypeIdentifiersToClasses {
285 | static NSDictionary* typeIdsToClasses = nil;
286 | if (typeIdsToClasses == nil) {
287 | typeIdsToClasses = @{ HKWorkoutTypeIdentifier: @"OMHSerializerGenericWorkout"};
288 | }
289 |
290 | NSMutableDictionary *allTypeIdsToClasses = [NSMutableDictionary dictionaryWithDictionary:typeIdsToClasses];
291 |
292 | [allTypeIdsToClasses addEntriesFromDictionary:[self allSupportedCategoryTypeIdentifiersToClasses]];
293 | [allTypeIdsToClasses addEntriesFromDictionary:[self allSupportedCorrelationTypeIdentifiersToClass]];
294 | [allTypeIdsToClasses addEntriesFromDictionary:[self allSupportedQuantityTypeIdentifiersToClass]];
295 |
296 | return allTypeIdsToClasses;
297 |
298 | }
299 |
300 | + (NSDictionary*)allSupportedCategoryTypeIdentifiersToClasses {
301 |
302 | static NSDictionary* allCategoryTypeIdsToClasses = nil;
303 | if (allCategoryTypeIdsToClasses == nil) {
304 | allCategoryTypeIdsToClasses = @{
305 | HKCategoryTypeIdentifierSleepAnalysis : @"OMHSerializerSleepAnalysis", //Samples with Asleep value use this serializer, samples with InBed value use generic category serializer
306 | HKCategoryTypeIdentifierAppleStandHour : @"OMHSerializerGenericCategorySample",
307 | HKCategoryTypeIdentifierCervicalMucusQuality : @"OMHSerializerGenericCategorySample",
308 | HKCategoryTypeIdentifierIntermenstrualBleeding: @"OMHSerializerGenericCategorySample",
309 | HKCategoryTypeIdentifierMenstrualFlow: @"OMHSerializerGenericCategorySample",
310 | HKCategoryTypeIdentifierOvulationTestResult: @"OMHSerializerGenericCategorySample",
311 | HKCategoryTypeIdentifierSexualActivity: @"OMHSerializerGenericCategorySample",
312 | HKCategoryTypeIdentifierMindfulSession: @"OMHSerializerGenericCategorySample"
313 | };
314 | }
315 | return allCategoryTypeIdsToClasses;
316 |
317 | }
318 |
319 | + (NSDictionary*)allSupportedCorrelationTypeIdentifiersToClass {
320 |
321 | static NSDictionary* allCorrelationTypeIdsToClasses = nil;
322 | if (allCorrelationTypeIdsToClasses == nil) {
323 | allCorrelationTypeIdsToClasses = @{
324 | HKCorrelationTypeIdentifierBloodPressure: @"OMHSerializerBloodPressure",
325 | HKCorrelationTypeIdentifierFood: @"OMHSerializerGenericCorrelation"
326 | };
327 | }
328 | return allCorrelationTypeIdsToClasses;
329 | }
330 |
331 | + (NSDictionary*)allSupportedQuantityTypeIdentifiersToClass {
332 |
333 | static NSDictionary* allQuantityTypeIdsToClasses = nil;
334 | if (allQuantityTypeIdsToClasses == nil) {
335 | allQuantityTypeIdsToClasses = @{
336 | HKQuantityTypeIdentifierActiveEnergyBurned: @"OMHSerializerEnergyBurned",
337 | HKQuantityTypeIdentifierBasalBodyTemperature: @"OMHSerializerBodyTemperature",
338 | HKQuantityTypeIdentifierBasalEnergyBurned: @"OMHSerializerEnergyBurned",
339 | HKQuantityTypeIdentifierBloodAlcoholContent: @"OMHSerializerGenericQuantitySample",
340 | HKQuantityTypeIdentifierBloodGlucose : @"OMHSerializerBloodGlucose",
341 | HKQuantityTypeIdentifierBloodPressureDiastolic: @"OMHSerializerGenericQuantitySample",
342 | HKQuantityTypeIdentifierBloodPressureSystolic: @"OMHSerializerGenericQuantitySample",
343 | HKQuantityTypeIdentifierBodyFatPercentage: @"OMHSerializerBodyFatPercentage",
344 | HKQuantityTypeIdentifierBodyMass : @"OMHSerializerWeight",
345 | HKQuantityTypeIdentifierBodyMassIndex: @"OMHSerializerBodyMassIndex",
346 | HKQuantityTypeIdentifierBodyTemperature: @"OMHSerializerBodyTemperature",
347 | HKQuantityTypeIdentifierDietaryBiotin: @"OMHSerializerGenericQuantitySample",
348 | HKQuantityTypeIdentifierDietaryCaffeine: @"OMHSerializerGenericQuantitySample",
349 | HKQuantityTypeIdentifierDietaryCalcium: @"OMHSerializerGenericQuantitySample",
350 | HKQuantityTypeIdentifierDietaryCarbohydrates: @"OMHSerializerGenericQuantitySample",
351 | HKQuantityTypeIdentifierDietaryChloride: @"OMHSerializerGenericQuantitySample",
352 | HKQuantityTypeIdentifierDietaryCholesterol: @"OMHSerializerGenericQuantitySample",
353 | HKQuantityTypeIdentifierDietaryChromium: @"OMHSerializerGenericQuantitySample",
354 | HKQuantityTypeIdentifierDietaryCopper: @"OMHSerializerGenericQuantitySample",
355 | HKQuantityTypeIdentifierDietaryEnergyConsumed: @"OMHSerializerGenericQuantitySample",
356 | HKQuantityTypeIdentifierDietaryFatMonounsaturated: @"OMHSerializerGenericQuantitySample",
357 | HKQuantityTypeIdentifierDietaryFatPolyunsaturated: @"OMHSerializerGenericQuantitySample",
358 | HKQuantityTypeIdentifierDietaryFatSaturated: @"OMHSerializerGenericQuantitySample",
359 | HKQuantityTypeIdentifierDietaryFatTotal: @"OMHSerializerGenericQuantitySample",
360 | HKQuantityTypeIdentifierDietaryFiber: @"OMHSerializerGenericQuantitySample",
361 | HKQuantityTypeIdentifierDietaryFolate: @"OMHSerializerGenericQuantitySample",
362 | HKQuantityTypeIdentifierDietaryIodine: @"OMHSerializerGenericQuantitySample",
363 | HKQuantityTypeIdentifierDietaryIron: @"OMHSerializerGenericQuantitySample",
364 | HKQuantityTypeIdentifierDietaryMagnesium: @"OMHSerializerGenericQuantitySample",
365 | HKQuantityTypeIdentifierDietaryManganese: @"OMHSerializerGenericQuantitySample",
366 | HKQuantityTypeIdentifierDietaryMolybdenum: @"OMHSerializerGenericQuantitySample",
367 | HKQuantityTypeIdentifierDietaryNiacin: @"OMHSerializerGenericQuantitySample",
368 | HKQuantityTypeIdentifierDietaryPantothenicAcid: @"OMHSerializerGenericQuantitySample",
369 | HKQuantityTypeIdentifierDietaryPhosphorus: @"OMHSerializerGenericQuantitySample",
370 | HKQuantityTypeIdentifierDietaryPotassium: @"OMHSerializerGenericQuantitySample",
371 | HKQuantityTypeIdentifierDietaryProtein: @"OMHSerializerGenericQuantitySample",
372 | HKQuantityTypeIdentifierDietaryRiboflavin: @"OMHSerializerGenericQuantitySample",
373 | HKQuantityTypeIdentifierDietarySelenium: @"OMHSerializerGenericQuantitySample",
374 | HKQuantityTypeIdentifierDietarySodium: @"OMHSerializerGenericQuantitySample",
375 | HKQuantityTypeIdentifierDietarySugar: @"OMHSerializerGenericQuantitySample",
376 | HKQuantityTypeIdentifierDietaryThiamin: @"OMHSerializerGenericQuantitySample",
377 | HKQuantityTypeIdentifierDietaryVitaminA: @"OMHSerializerGenericQuantitySample",
378 | HKQuantityTypeIdentifierDietaryVitaminB12: @"OMHSerializerGenericQuantitySample",
379 | HKQuantityTypeIdentifierDietaryVitaminB6: @"OMHSerializerGenericQuantitySample",
380 | HKQuantityTypeIdentifierDietaryVitaminC: @"OMHSerializerGenericQuantitySample",
381 | HKQuantityTypeIdentifierDietaryVitaminD: @"OMHSerializerGenericQuantitySample",
382 | HKQuantityTypeIdentifierDietaryVitaminE: @"OMHSerializerGenericQuantitySample",
383 | HKQuantityTypeIdentifierDietaryVitaminK: @"OMHSerializerGenericQuantitySample",
384 | HKQuantityTypeIdentifierDietaryWater: @"OMHSerializerGenericQuantitySample",
385 | HKQuantityTypeIdentifierDietaryZinc: @"OMHSerializerGenericQuantitySample",
386 | HKQuantityTypeIdentifierDistanceCycling: @"OMHSerializerGenericQuantitySample",
387 | HKQuantityTypeIdentifierDistanceWalkingRunning: @"OMHSerializerGenericQuantitySample",
388 | HKQuantityTypeIdentifierElectrodermalActivity: @"OMHSerializerGenericQuantitySample",
389 | HKQuantityTypeIdentifierFlightsClimbed: @"OMHSerializerGenericQuantitySample",
390 | HKQuantityTypeIdentifierForcedExpiratoryVolume1: @"OMHSerializerGenericQuantitySample",
391 | HKQuantityTypeIdentifierForcedVitalCapacity: @"OMHSerializerGenericQuantitySample",
392 | HKQuantityTypeIdentifierHeight : @"OMHSerializerHeight",
393 | HKQuantityTypeIdentifierHeartRate : @"OMHSerializerHeartRate",
394 | HKQuantityTypeIdentifierInhalerUsage: @"OMHSerializerGenericQuantitySample",
395 | HKQuantityTypeIdentifierLeanBodyMass: @"OMHSerializerGenericQuantitySample",
396 | HKQuantityTypeIdentifierNikeFuel: @"OMHSerializerGenericQuantitySample",
397 | HKQuantityTypeIdentifierNumberOfTimesFallen: @"OMHSerializerGenericQuantitySample",
398 | HKQuantityTypeIdentifierOxygenSaturation: @"OMHSerializerOxygenSaturation",
399 | HKQuantityTypeIdentifierPeakExpiratoryFlowRate: @"OMHSerializerGenericQuantitySample",
400 | HKQuantityTypeIdentifierPeripheralPerfusionIndex: @"OMHSerializerGenericQuantitySample",
401 | HKQuantityTypeIdentifierRespiratoryRate: @"OMHSerializerRespiratoryRate",
402 | HKQuantityTypeIdentifierStepCount : @"OMHSerializerStepCount",
403 | HKQuantityTypeIdentifierUVExposure: @"OMHSerializerGenericQuantitySample"
404 | };
405 | }
406 | return allQuantityTypeIdsToClasses;
407 | }
408 |
409 | @end
410 |
411 |
--------------------------------------------------------------------------------
/Example/Granola.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 2E945AD21B753D1400A1FEB0 /* NSDate+RFC3339Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2E945AD11B753D1400A1FEB0 /* NSDate+RFC3339Tests.m */; };
11 | 2EAAF0371C44CB1300897920 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EAAF0361C44CB1300897920 /* AppDelegate.swift */; };
12 | 2EAAF0391C44CB1300897920 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EAAF0381C44CB1300897920 /* ViewController.swift */; };
13 | 2EAAF03C1C44CB1300897920 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2EAAF03A1C44CB1300897920 /* Main.storyboard */; };
14 | 2EAAF03E1C44CB1300897920 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2EAAF03D1C44CB1300897920 /* Assets.xcassets */; };
15 | 2EAAF0411C44CB1300897920 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2EAAF03F1C44CB1300897920 /* LaunchScreen.storyboard */; };
16 | 469369ACEB2BC8FFF67D5A3D /* libPods-Granola.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6225ED718EED723BF396A7F1 /* libPods-Granola.a */; };
17 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; };
18 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; };
19 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; };
20 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; };
21 | 6003F5BC195388D20070C39A /* OMHSerializerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5BB195388D20070C39A /* OMHSerializerTests.m */; };
22 | 9E272AB11ADEFF1B007DE4ED /* OMHSampleFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E272AB01ADEFF1B007DE4ED /* OMHSampleFactory.m */; };
23 | 9E272AB41ADF523F007DE4ED /* OMHSchemaStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E272AB31ADF523F007DE4ED /* OMHSchemaStore.m */; };
24 | 9ECA505C1AD60A3000E367CA /* schema in Resources */ = {isa = PBXBuildFile; fileRef = 9ECA505B1AD60A3000E367CA /* schema */; };
25 | /* End PBXBuildFile section */
26 |
27 | /* Begin PBXFileReference section */
28 | 0713970833C99FFE3A26765F /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; };
29 | 1497BA18A434AE1495648DE8 /* Granola.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = Granola.podspec; path = ../Granola.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
30 | 2E945AD11B753D1400A1FEB0 /* NSDate+RFC3339Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDate+RFC3339Tests.m"; sourceTree = ""; };
31 | 2EAAF0341C44CB1300897920 /* Granola Test Swift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Granola Test Swift.app"; sourceTree = BUILT_PRODUCTS_DIR; };
32 | 2EAAF0361C44CB1300897920 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
33 | 2EAAF0381C44CB1300897920 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
34 | 2EAAF03B1C44CB1300897920 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
35 | 2EAAF03D1C44CB1300897920 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
36 | 2EAAF0401C44CB1300897920 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
37 | 2EAAF0421C44CB1300897920 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
38 | 5F5286D8580E554F428C1841 /* Pods-Granola.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Granola.debug.xcconfig"; path = "Target Support Files/Pods-Granola/Pods-Granola.debug.xcconfig"; sourceTree = ""; };
39 | 6003F58A195388D20070C39A /* Granola.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Granola.app; sourceTree = BUILT_PRODUCTS_DIR; };
40 | 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
41 | 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
42 | 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
43 | 6003F5AE195388D20070C39A /* Granola.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Granola.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
44 | 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
45 | 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; };
46 | 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
47 | 6003F5BB195388D20070C39A /* OMHSerializerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OMHSerializerTests.m; sourceTree = ""; };
48 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = ""; };
49 | 6225ED718EED723BF396A7F1 /* libPods-Granola.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Granola.a"; sourceTree = BUILT_PRODUCTS_DIR; };
50 | 647C6D368DFEFC0AB9546C4F /* Pods-Granola.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Granola.release.xcconfig"; path = "Target Support Files/Pods-Granola/Pods-Granola.release.xcconfig"; sourceTree = ""; };
51 | 9E272AAF1ADEFF1B007DE4ED /* OMHSampleFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OMHSampleFactory.h; sourceTree = ""; };
52 | 9E272AB01ADEFF1B007DE4ED /* OMHSampleFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OMHSampleFactory.m; sourceTree = ""; };
53 | 9E272AB21ADF523F007DE4ED /* OMHSchemaStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OMHSchemaStore.h; sourceTree = ""; };
54 | 9E272AB31ADF523F007DE4ED /* OMHSchemaStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OMHSchemaStore.m; sourceTree = ""; };
55 | 9ECA505B1AD60A3000E367CA /* schema */ = {isa = PBXFileReference; lastKnownFileType = folder; name = schema; path = "omh-schemas/schema"; sourceTree = ""; };
56 | 9EDEA5811ADB0B5200D3F174 /* HKObject+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "HKObject+Private.h"; sourceTree = ""; };
57 | EF857A4BFD07184BC663D020 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; };
58 | /* End PBXFileReference section */
59 |
60 | /* Begin PBXFrameworksBuildPhase section */
61 | 2EAAF0311C44CB1300897920 /* Frameworks */ = {
62 | isa = PBXFrameworksBuildPhase;
63 | buildActionMask = 2147483647;
64 | files = (
65 | );
66 | runOnlyForDeploymentPostprocessing = 0;
67 | };
68 | 6003F5AB195388D20070C39A /* Frameworks */ = {
69 | isa = PBXFrameworksBuildPhase;
70 | buildActionMask = 2147483647;
71 | files = (
72 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */,
73 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */,
74 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */,
75 | 469369ACEB2BC8FFF67D5A3D /* libPods-Granola.a in Frameworks */,
76 | );
77 | runOnlyForDeploymentPostprocessing = 0;
78 | };
79 | /* End PBXFrameworksBuildPhase section */
80 |
81 | /* Begin PBXGroup section */
82 | 2EAAF0351C44CB1300897920 /* Granola Test Swift */ = {
83 | isa = PBXGroup;
84 | children = (
85 | 2EAAF0361C44CB1300897920 /* AppDelegate.swift */,
86 | 2EAAF0381C44CB1300897920 /* ViewController.swift */,
87 | 2EAAF03A1C44CB1300897920 /* Main.storyboard */,
88 | 2EAAF03D1C44CB1300897920 /* Assets.xcassets */,
89 | 2EAAF03F1C44CB1300897920 /* LaunchScreen.storyboard */,
90 | 2EAAF0421C44CB1300897920 /* Info.plist */,
91 | );
92 | path = "Granola Test Swift";
93 | sourceTree = "";
94 | };
95 | 6003F581195388D10070C39A = {
96 | isa = PBXGroup;
97 | children = (
98 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */,
99 | 6003F5B5195388D20070C39A /* Tests */,
100 | 2EAAF0351C44CB1300897920 /* Granola Test Swift */,
101 | 6003F58C195388D20070C39A /* Frameworks */,
102 | 6003F58B195388D20070C39A /* Products */,
103 | 8B68C31EFCA7C1245D73EC64 /* Pods */,
104 | );
105 | sourceTree = "";
106 | };
107 | 6003F58B195388D20070C39A /* Products */ = {
108 | isa = PBXGroup;
109 | children = (
110 | 6003F58A195388D20070C39A /* Granola.app */,
111 | 6003F5AE195388D20070C39A /* Granola.xctest */,
112 | 2EAAF0341C44CB1300897920 /* Granola Test Swift.app */,
113 | );
114 | name = Products;
115 | sourceTree = "";
116 | };
117 | 6003F58C195388D20070C39A /* Frameworks */ = {
118 | isa = PBXGroup;
119 | children = (
120 | 6003F58D195388D20070C39A /* Foundation.framework */,
121 | 6003F58F195388D20070C39A /* CoreGraphics.framework */,
122 | 6003F591195388D20070C39A /* UIKit.framework */,
123 | 6003F5AF195388D20070C39A /* XCTest.framework */,
124 | 6225ED718EED723BF396A7F1 /* libPods-Granola.a */,
125 | );
126 | name = Frameworks;
127 | sourceTree = "";
128 | };
129 | 6003F5B5195388D20070C39A /* Tests */ = {
130 | isa = PBXGroup;
131 | children = (
132 | 6003F5BB195388D20070C39A /* OMHSerializerTests.m */,
133 | 9EDEA5811ADB0B5200D3F174 /* HKObject+Private.h */,
134 | 9E272AAF1ADEFF1B007DE4ED /* OMHSampleFactory.h */,
135 | 9E272AB01ADEFF1B007DE4ED /* OMHSampleFactory.m */,
136 | 9E272AB21ADF523F007DE4ED /* OMHSchemaStore.h */,
137 | 9E272AB31ADF523F007DE4ED /* OMHSchemaStore.m */,
138 | 2E945AD11B753D1400A1FEB0 /* NSDate+RFC3339Tests.m */,
139 | 6003F5B6195388D20070C39A /* Supporting Files */,
140 | );
141 | path = Tests;
142 | sourceTree = "";
143 | };
144 | 6003F5B6195388D20070C39A /* Supporting Files */ = {
145 | isa = PBXGroup;
146 | children = (
147 | 9ECA505B1AD60A3000E367CA /* schema */,
148 | 6003F5B7195388D20070C39A /* Tests-Info.plist */,
149 | 6003F5B8195388D20070C39A /* InfoPlist.strings */,
150 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */,
151 | );
152 | name = "Supporting Files";
153 | sourceTree = "";
154 | };
155 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = {
156 | isa = PBXGroup;
157 | children = (
158 | 1497BA18A434AE1495648DE8 /* Granola.podspec */,
159 | EF857A4BFD07184BC663D020 /* README.md */,
160 | 0713970833C99FFE3A26765F /* LICENSE */,
161 | );
162 | name = "Podspec Metadata";
163 | sourceTree = "";
164 | };
165 | 8B68C31EFCA7C1245D73EC64 /* Pods */ = {
166 | isa = PBXGroup;
167 | children = (
168 | 5F5286D8580E554F428C1841 /* Pods-Granola.debug.xcconfig */,
169 | 647C6D368DFEFC0AB9546C4F /* Pods-Granola.release.xcconfig */,
170 | );
171 | path = Pods;
172 | sourceTree = "";
173 | };
174 | /* End PBXGroup section */
175 |
176 | /* Begin PBXNativeTarget section */
177 | 2EAAF0331C44CB1300897920 /* Granola Test Swift */ = {
178 | isa = PBXNativeTarget;
179 | buildConfigurationList = 2EAAF0431C44CB1300897920 /* Build configuration list for PBXNativeTarget "Granola Test Swift" */;
180 | buildPhases = (
181 | 2EAAF0301C44CB1300897920 /* Sources */,
182 | 2EAAF0311C44CB1300897920 /* Frameworks */,
183 | 2EAAF0321C44CB1300897920 /* Resources */,
184 | );
185 | buildRules = (
186 | );
187 | dependencies = (
188 | );
189 | name = "Granola Test Swift";
190 | productName = "Granola Test Swift";
191 | productReference = 2EAAF0341C44CB1300897920 /* Granola Test Swift.app */;
192 | productType = "com.apple.product-type.application";
193 | };
194 | 6003F5AD195388D20070C39A /* Granola */ = {
195 | isa = PBXNativeTarget;
196 | buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "Granola" */;
197 | buildPhases = (
198 | 8511FFFB80DACB7BD3C256D2 /* [CP] Check Pods Manifest.lock */,
199 | 6003F5AA195388D20070C39A /* Sources */,
200 | 6003F5AB195388D20070C39A /* Frameworks */,
201 | 6003F5AC195388D20070C39A /* Resources */,
202 | );
203 | buildRules = (
204 | );
205 | dependencies = (
206 | );
207 | name = Granola;
208 | productName = GranolaTests;
209 | productReference = 6003F5AE195388D20070C39A /* Granola.xctest */;
210 | productType = "com.apple.product-type.bundle.unit-test";
211 | };
212 | /* End PBXNativeTarget section */
213 |
214 | /* Begin PBXProject section */
215 | 6003F582195388D10070C39A /* Project object */ = {
216 | isa = PBXProject;
217 | attributes = {
218 | CLASSPREFIX = OMH;
219 | LastSwiftUpdateCheck = 0710;
220 | LastUpgradeCheck = 0510;
221 | ORGANIZATIONNAME = "Open mHealth";
222 | TargetAttributes = {
223 | 2EAAF0331C44CB1300897920 = {
224 | CreatedOnToolsVersion = 7.1.1;
225 | };
226 | 6003F5AD195388D20070C39A = {
227 | TestTargetID = 6003F589195388D20070C39A;
228 | };
229 | };
230 | };
231 | buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "Granola" */;
232 | compatibilityVersion = "Xcode 3.2";
233 | developmentRegion = English;
234 | hasScannedForEncodings = 0;
235 | knownRegions = (
236 | English,
237 | en,
238 | Base,
239 | );
240 | mainGroup = 6003F581195388D10070C39A;
241 | productRefGroup = 6003F58B195388D20070C39A /* Products */;
242 | projectDirPath = "";
243 | projectRoot = "";
244 | targets = (
245 | 6003F5AD195388D20070C39A /* Granola */,
246 | 2EAAF0331C44CB1300897920 /* Granola Test Swift */,
247 | );
248 | };
249 | /* End PBXProject section */
250 |
251 | /* Begin PBXResourcesBuildPhase section */
252 | 2EAAF0321C44CB1300897920 /* Resources */ = {
253 | isa = PBXResourcesBuildPhase;
254 | buildActionMask = 2147483647;
255 | files = (
256 | 2EAAF0411C44CB1300897920 /* LaunchScreen.storyboard in Resources */,
257 | 2EAAF03E1C44CB1300897920 /* Assets.xcassets in Resources */,
258 | 2EAAF03C1C44CB1300897920 /* Main.storyboard in Resources */,
259 | );
260 | runOnlyForDeploymentPostprocessing = 0;
261 | };
262 | 6003F5AC195388D20070C39A /* Resources */ = {
263 | isa = PBXResourcesBuildPhase;
264 | buildActionMask = 2147483647;
265 | files = (
266 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */,
267 | 9ECA505C1AD60A3000E367CA /* schema in Resources */,
268 | );
269 | runOnlyForDeploymentPostprocessing = 0;
270 | };
271 | /* End PBXResourcesBuildPhase section */
272 |
273 | /* Begin PBXShellScriptBuildPhase section */
274 | 8511FFFB80DACB7BD3C256D2 /* [CP] Check Pods Manifest.lock */ = {
275 | isa = PBXShellScriptBuildPhase;
276 | buildActionMask = 2147483647;
277 | files = (
278 | );
279 | inputFileListPaths = (
280 | );
281 | inputPaths = (
282 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
283 | "${PODS_ROOT}/Manifest.lock",
284 | );
285 | name = "[CP] Check Pods Manifest.lock";
286 | outputFileListPaths = (
287 | );
288 | outputPaths = (
289 | "$(DERIVED_FILE_DIR)/Pods-Granola-checkManifestLockResult.txt",
290 | );
291 | runOnlyForDeploymentPostprocessing = 0;
292 | shellPath = /bin/sh;
293 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
294 | showEnvVarsInLog = 0;
295 | };
296 | /* End PBXShellScriptBuildPhase section */
297 |
298 | /* Begin PBXSourcesBuildPhase section */
299 | 2EAAF0301C44CB1300897920 /* Sources */ = {
300 | isa = PBXSourcesBuildPhase;
301 | buildActionMask = 2147483647;
302 | files = (
303 | 2EAAF0391C44CB1300897920 /* ViewController.swift in Sources */,
304 | 2EAAF0371C44CB1300897920 /* AppDelegate.swift in Sources */,
305 | );
306 | runOnlyForDeploymentPostprocessing = 0;
307 | };
308 | 6003F5AA195388D20070C39A /* Sources */ = {
309 | isa = PBXSourcesBuildPhase;
310 | buildActionMask = 2147483647;
311 | files = (
312 | 6003F5BC195388D20070C39A /* OMHSerializerTests.m in Sources */,
313 | 9E272AB11ADEFF1B007DE4ED /* OMHSampleFactory.m in Sources */,
314 | 2E945AD21B753D1400A1FEB0 /* NSDate+RFC3339Tests.m in Sources */,
315 | 9E272AB41ADF523F007DE4ED /* OMHSchemaStore.m in Sources */,
316 | );
317 | runOnlyForDeploymentPostprocessing = 0;
318 | };
319 | /* End PBXSourcesBuildPhase section */
320 |
321 | /* Begin PBXVariantGroup section */
322 | 2EAAF03A1C44CB1300897920 /* Main.storyboard */ = {
323 | isa = PBXVariantGroup;
324 | children = (
325 | 2EAAF03B1C44CB1300897920 /* Base */,
326 | );
327 | name = Main.storyboard;
328 | sourceTree = "";
329 | };
330 | 2EAAF03F1C44CB1300897920 /* LaunchScreen.storyboard */ = {
331 | isa = PBXVariantGroup;
332 | children = (
333 | 2EAAF0401C44CB1300897920 /* Base */,
334 | );
335 | name = LaunchScreen.storyboard;
336 | sourceTree = "";
337 | };
338 | 6003F5B8195388D20070C39A /* InfoPlist.strings */ = {
339 | isa = PBXVariantGroup;
340 | children = (
341 | 6003F5B9195388D20070C39A /* en */,
342 | );
343 | name = InfoPlist.strings;
344 | sourceTree = "";
345 | };
346 | /* End PBXVariantGroup section */
347 |
348 | /* Begin XCBuildConfiguration section */
349 | 2EAAF0441C44CB1300897920 /* Debug */ = {
350 | isa = XCBuildConfiguration;
351 | buildSettings = {
352 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
353 | CLANG_WARN_UNREACHABLE_CODE = YES;
354 | DEBUG_INFORMATION_FORMAT = dwarf;
355 | ENABLE_STRICT_OBJC_MSGSEND = YES;
356 | ENABLE_TESTABILITY = YES;
357 | GCC_NO_COMMON_BLOCKS = YES;
358 | INFOPLIST_FILE = "Granola Test Swift/Info.plist";
359 | IPHONEOS_DEPLOYMENT_TARGET = 9.1;
360 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
361 | MTL_ENABLE_DEBUG_INFO = YES;
362 | PRODUCT_BUNDLE_IDENTIFIER = "omh.Granola-Test-Swift";
363 | PRODUCT_NAME = "$(TARGET_NAME)";
364 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
365 | };
366 | name = Debug;
367 | };
368 | 2EAAF0451C44CB1300897920 /* Release */ = {
369 | isa = XCBuildConfiguration;
370 | buildSettings = {
371 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
372 | CLANG_WARN_UNREACHABLE_CODE = YES;
373 | COPY_PHASE_STRIP = NO;
374 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
375 | ENABLE_STRICT_OBJC_MSGSEND = YES;
376 | GCC_NO_COMMON_BLOCKS = YES;
377 | INFOPLIST_FILE = "Granola Test Swift/Info.plist";
378 | IPHONEOS_DEPLOYMENT_TARGET = 9.1;
379 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
380 | MTL_ENABLE_DEBUG_INFO = NO;
381 | PRODUCT_BUNDLE_IDENTIFIER = "omh.Granola-Test-Swift";
382 | PRODUCT_NAME = "$(TARGET_NAME)";
383 | };
384 | name = Release;
385 | };
386 | 6003F5BD195388D20070C39A /* Debug */ = {
387 | isa = XCBuildConfiguration;
388 | buildSettings = {
389 | ALWAYS_SEARCH_USER_PATHS = NO;
390 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
391 | CLANG_CXX_LIBRARY = "libc++";
392 | CLANG_ENABLE_MODULES = YES;
393 | CLANG_ENABLE_OBJC_ARC = YES;
394 | CLANG_WARN_BOOL_CONVERSION = YES;
395 | CLANG_WARN_CONSTANT_CONVERSION = YES;
396 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
397 | CLANG_WARN_EMPTY_BODY = YES;
398 | CLANG_WARN_ENUM_CONVERSION = YES;
399 | CLANG_WARN_INT_CONVERSION = YES;
400 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
401 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
402 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
403 | COPY_PHASE_STRIP = NO;
404 | GCC_C_LANGUAGE_STANDARD = gnu99;
405 | GCC_DYNAMIC_NO_PIC = NO;
406 | GCC_OPTIMIZATION_LEVEL = 0;
407 | GCC_PREPROCESSOR_DEFINITIONS = (
408 | "DEBUG=1",
409 | "$(inherited)",
410 | );
411 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
412 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
413 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
414 | GCC_WARN_UNDECLARED_SELECTOR = YES;
415 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
416 | GCC_WARN_UNUSED_FUNCTION = YES;
417 | GCC_WARN_UNUSED_VARIABLE = YES;
418 | IPHONEOS_DEPLOYMENT_TARGET = 7.1;
419 | ONLY_ACTIVE_ARCH = YES;
420 | SDKROOT = iphoneos;
421 | TARGETED_DEVICE_FAMILY = "1,2";
422 | };
423 | name = Debug;
424 | };
425 | 6003F5BE195388D20070C39A /* Release */ = {
426 | isa = XCBuildConfiguration;
427 | buildSettings = {
428 | ALWAYS_SEARCH_USER_PATHS = NO;
429 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
430 | CLANG_CXX_LIBRARY = "libc++";
431 | CLANG_ENABLE_MODULES = YES;
432 | CLANG_ENABLE_OBJC_ARC = YES;
433 | CLANG_WARN_BOOL_CONVERSION = YES;
434 | CLANG_WARN_CONSTANT_CONVERSION = YES;
435 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
436 | CLANG_WARN_EMPTY_BODY = YES;
437 | CLANG_WARN_ENUM_CONVERSION = YES;
438 | CLANG_WARN_INT_CONVERSION = YES;
439 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
440 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
441 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
442 | COPY_PHASE_STRIP = YES;
443 | ENABLE_NS_ASSERTIONS = NO;
444 | GCC_C_LANGUAGE_STANDARD = gnu99;
445 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
446 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
447 | GCC_WARN_UNDECLARED_SELECTOR = YES;
448 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
449 | GCC_WARN_UNUSED_FUNCTION = YES;
450 | GCC_WARN_UNUSED_VARIABLE = YES;
451 | IPHONEOS_DEPLOYMENT_TARGET = 7.1;
452 | SDKROOT = iphoneos;
453 | TARGETED_DEVICE_FAMILY = "1,2";
454 | VALIDATE_PRODUCT = YES;
455 | };
456 | name = Release;
457 | };
458 | 6003F5C3195388D20070C39A /* Debug */ = {
459 | isa = XCBuildConfiguration;
460 | baseConfigurationReference = 5F5286D8580E554F428C1841 /* Pods-Granola.debug.xcconfig */;
461 | buildSettings = {
462 | FRAMEWORK_SEARCH_PATHS = (
463 | "$(SDKROOT)/Developer/Library/Frameworks",
464 | "$(inherited)",
465 | "$(DEVELOPER_FRAMEWORKS_DIR)",
466 | );
467 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
468 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch";
469 | GCC_PREPROCESSOR_DEFINITIONS = (
470 | "DEBUG=1",
471 | "$(inherited)",
472 | );
473 | INFOPLIST_FILE = "Tests/Tests-Info.plist";
474 | PRODUCT_NAME = "$(TARGET_NAME)";
475 | TEST_HOST = "$(BUNDLE_LOADER)";
476 | WRAPPER_EXTENSION = xctest;
477 | };
478 | name = Debug;
479 | };
480 | 6003F5C4195388D20070C39A /* Release */ = {
481 | isa = XCBuildConfiguration;
482 | baseConfigurationReference = 647C6D368DFEFC0AB9546C4F /* Pods-Granola.release.xcconfig */;
483 | buildSettings = {
484 | FRAMEWORK_SEARCH_PATHS = (
485 | "$(SDKROOT)/Developer/Library/Frameworks",
486 | "$(inherited)",
487 | "$(DEVELOPER_FRAMEWORKS_DIR)",
488 | );
489 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
490 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch";
491 | INFOPLIST_FILE = "Tests/Tests-Info.plist";
492 | PRODUCT_NAME = "$(TARGET_NAME)";
493 | TEST_HOST = "$(BUNDLE_LOADER)";
494 | WRAPPER_EXTENSION = xctest;
495 | };
496 | name = Release;
497 | };
498 | /* End XCBuildConfiguration section */
499 |
500 | /* Begin XCConfigurationList section */
501 | 2EAAF0431C44CB1300897920 /* Build configuration list for PBXNativeTarget "Granola Test Swift" */ = {
502 | isa = XCConfigurationList;
503 | buildConfigurations = (
504 | 2EAAF0441C44CB1300897920 /* Debug */,
505 | 2EAAF0451C44CB1300897920 /* Release */,
506 | );
507 | defaultConfigurationIsVisible = 0;
508 | defaultConfigurationName = Release;
509 | };
510 | 6003F585195388D10070C39A /* Build configuration list for PBXProject "Granola" */ = {
511 | isa = XCConfigurationList;
512 | buildConfigurations = (
513 | 6003F5BD195388D20070C39A /* Debug */,
514 | 6003F5BE195388D20070C39A /* Release */,
515 | );
516 | defaultConfigurationIsVisible = 0;
517 | defaultConfigurationName = Release;
518 | };
519 | 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "Granola" */ = {
520 | isa = XCConfigurationList;
521 | buildConfigurations = (
522 | 6003F5C3195388D20070C39A /* Debug */,
523 | 6003F5C4195388D20070C39A /* Release */,
524 | );
525 | defaultConfigurationIsVisible = 0;
526 | defaultConfigurationName = Release;
527 | };
528 | /* End XCConfigurationList section */
529 | };
530 | rootObject = 6003F582195388D10070C39A /* Project object */;
531 | }
532 |
--------------------------------------------------------------------------------
/Pod/Classes/OMHSerializer.m:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Open mHealth
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #import "OMHSerializer.h"
18 | #import "NSDate+RFC3339.h"
19 | #import
20 | #import "OMHHealthKitConstantsMapper.h"
21 |
22 | @interface OMHSerializer()
23 | @property (nonatomic, retain) HKSample* sample;
24 | + (BOOL)canSerialize:(HKSample*)sample error:(NSError**)error;
25 | + (NSException*)unimplementedException;
26 | @end
27 |
28 |
29 | @implementation OMHSerializer
30 |
31 | + (NSArray*)supportedTypeIdentifiersWithOMHSchema {
32 | static NSArray* OMHSchemaTypeIds = nil;
33 | if(OMHSchemaTypeIds == nil){
34 | OMHSchemaTypeIds = @[
35 | HKQuantityTypeIdentifierHeight,
36 | HKQuantityTypeIdentifierBodyMass,
37 | HKQuantityTypeIdentifierStepCount,
38 | HKQuantityTypeIdentifierHeartRate,
39 | HKQuantityTypeIdentifierBloodGlucose,
40 | HKQuantityTypeIdentifierActiveEnergyBurned,
41 | HKQuantityTypeIdentifierBasalEnergyBurned,
42 | HKQuantityTypeIdentifierBodyMassIndex,
43 | HKQuantityTypeIdentifierBodyFatPercentage,
44 | HKQuantityTypeIdentifierOxygenSaturation,
45 | HKQuantityTypeIdentifierRespiratoryRate,
46 | HKQuantityTypeIdentifierBodyTemperature,
47 | HKQuantityTypeIdentifierBasalBodyTemperature,
48 | HKCategoryTypeIdentifierSleepAnalysis, //Samples with Asleep value use this serializer, samples with InBed value use generic category serializer
49 | HKCorrelationTypeIdentifierBloodPressure
50 | ];
51 |
52 | }
53 | return OMHSchemaTypeIds;
54 | }
55 |
56 | + (NSArray*)supportedTypeIdentifiers {
57 | return [[OMHHealthKitConstantsMapper allSupportedTypeIdentifiersToClasses] allKeys];
58 | }
59 |
60 | + (NSString*)getCategoryValueForTypeWithValue: (HKCategoryType*) categoryType categoryValue:(NSInteger)categoryValue {
61 |
62 | if ( [categoryType.description isEqualToString:HKCategoryTypeIdentifierAppleStandHour.description] ) {
63 | return [OMHHealthKitConstantsMapper stringForHKAppleStandHourValue:(int)categoryValue];
64 | }
65 | else if ([categoryType.description isEqualToString:HKCategoryTypeIdentifierSleepAnalysis.description]) {
66 | return [OMHHealthKitConstantsMapper stringForHKSleepAnalysisValue:(int)categoryValue];
67 | }
68 | else if ([categoryType.description isEqualToString:HKCategoryTypeIdentifierCervicalMucusQuality.description]) {
69 | return [OMHHealthKitConstantsMapper stringForHKCervicalMucusQualityValue:(int)categoryValue];
70 | }
71 | else if ([categoryType.description isEqualToString:HKCategoryTypeIdentifierIntermenstrualBleeding]) {
72 | /* Samples of this type represent the presence of intermenstrual bleeding and as such does not have a categorical value. HealthKit
73 | specifies that the value field for this type is "HKCategoryValueNotApplicable" which is a nonsensical value, so we use the name
74 | of the represented measure as the value. */
75 | return @"Intermenstrual bleeding";
76 | }
77 | else if ([categoryType.description isEqualToString:HKCategoryTypeIdentifierMindfulSession]) {
78 | /* Samples of this type also use "HKCategoryValueNotApplicable".
79 | https://developer.apple.com/documentation/healthkit/hkcategorytypeidentifiermindfulsession?language=objc
80 | */
81 | return @"Mindful session";
82 | }
83 | else if ([categoryType.description isEqualToString:HKCategoryTypeIdentifierMenstrualFlow]) {
84 | return [OMHHealthKitConstantsMapper stringForHKMenstrualFlowValue:(int)categoryValue];
85 | }
86 | else if ([categoryType.description isEqualToString:HKCategoryTypeIdentifierOvulationTestResult]) {
87 | return [OMHHealthKitConstantsMapper stringForHKOvulationTestResultValue:(int)categoryValue];
88 | }
89 | else if ([categoryType.description isEqualToString:HKCategoryTypeIdentifierSexualActivity]) {
90 | /* Samples of this type represent times during which sexual activity occurred. This means that during the time frame of each
91 | sample, sexual activity was occurring. As such, this measure does not have a categorical value. HealthKit specifies that the
92 | value field for this type is "HKCategoryValueNotApplicable" which is a nonsensical value, so we use the name of the represented
93 | measure as the value. */
94 | return @"Sexual activity";
95 | }
96 | else{
97 | NSException *e = [NSException
98 | exceptionWithName:@"InvalidHKCategoryType"
99 | reason:@"Incorrect category type parameter for method."
100 | userInfo:nil];
101 | @throw e;
102 | }
103 |
104 | }
105 |
106 | + (BOOL)canSerialize:(HKSample*)sample error:(NSError**)error {
107 | @throw [self unimplementedException];
108 | }
109 |
110 | - (id)initWithSample:(HKSample*)sample {
111 | self = [super init];
112 | if (self) {
113 | _sample = sample;
114 | } else {
115 | return nil;
116 | }
117 | return self;
118 | }
119 |
120 | /**
121 | Serializes HealthKit samples into Open mHealth compliant JSON data points.
122 | @param sample the HealthKit sample to be serialized
123 | @param error an NSError that is passed by reference and can be checked to identify specific errors
124 | @return a formatted JSON string containing the sample's data in a format that adheres to the appropriate Open mHealth schema
125 | */
126 | - (NSString*)jsonForSample:(HKSample*)sample error:(NSError**)error {
127 | NSParameterAssert(sample);
128 | // first, verify we support the sample type
129 | NSArray* supportedTypeIdentifiers = [[self class] supportedTypeIdentifiers];
130 | NSString* sampleTypeIdentifier = sample.sampleType.identifier;
131 | NSString* serializerClassName;
132 | if ([supportedTypeIdentifiers includes:sampleTypeIdentifier]){
133 | serializerClassName = [OMHHealthKitConstantsMapper allSupportedTypeIdentifiersToClasses][sampleTypeIdentifier];
134 | }
135 | else{
136 | if (error) {
137 | NSString* errorMessage =
138 | [NSString stringWithFormat: @"Unsupported HKSample type: %@", sampleTypeIdentifier];
139 | NSDictionary* userInfo = @{ NSLocalizedDescriptionKey : errorMessage };
140 | *error = [NSError errorWithDomain: OMHErrorDomain
141 | code: OMHErrorCodeUnsupportedType
142 | userInfo: userInfo];
143 | }
144 | return nil;
145 | }
146 | // if we support it, select appropriate subclass for sample
147 |
148 | //For sleep analysis, the OMH schema does not capture an 'inBed' state, so if that value is set we need to use a generic category serializer
149 | //otherwise, it defaults to using the OMH schema for the 'asleep' state.
150 | if ([sampleTypeIdentifier isEqualToString:HKCategoryTypeIdentifierSleepAnalysis]){
151 | HKCategorySample* categorySample = (HKCategorySample*)sample;
152 | if(categorySample.value == HKCategoryValueSleepAnalysisInBed){
153 | serializerClassName = @"OMHSerializerGenericCategorySample";
154 | }
155 | }
156 | Class serializerClass = NSClassFromString(serializerClassName);
157 | // subclass verifies it supports sample's values
158 | if (![serializerClass canSerialize:sample error:error]) {
159 | return nil;
160 | }
161 | // instantiate a serializer
162 | OMHSerializer* serializer = [[serializerClass alloc] initWithSample:sample];
163 | NSData* jsonData = [NSJSONSerialization dataWithJSONObject:[serializer data]
164 | options:NSJSONWritingPrettyPrinted
165 | error:error];
166 | if (!jsonData) {
167 | return nil; // return early if JSON serialization failed
168 | }
169 | NSString* jsonString = [[NSString alloc] initWithData:jsonData
170 | encoding:NSUTF8StringEncoding];
171 | return jsonString;
172 | }
173 |
174 | + (NSString*)parseUnitFromQuantity:(HKQuantity*)quantity {
175 | NSArray *arrayWithSplitUnitAndValue = [quantity.description
176 | componentsSeparatedByCharactersInSet:[NSCharacterSet
177 | whitespaceCharacterSet]];
178 | return arrayWithSplitUnitAndValue[1];
179 | }
180 |
181 | - (NSDictionary*) populateTimeFrameProperty:(NSDate*)startDate endDate:(NSDate*)endDate {
182 |
183 | NSString* timeZoneString = [self.sample.metadata objectForKey:HKMetadataKeyTimeZone];
184 |
185 | if (timeZoneString != nil) {
186 | NSTimeZone* timeZone = [NSTimeZone timeZoneWithName:timeZoneString];
187 | if ([startDate isEqualToDate:endDate]) {
188 | return @{
189 | @"date_time":[startDate RFC3339StringAtTimeZone:timeZone]
190 | };
191 | }
192 | else {
193 | return @{
194 | @"time_interval": @{
195 | @"start_date_time": [startDate RFC3339StringAtTimeZone:timeZone],
196 | @"end_date_time": [endDate RFC3339StringAtTimeZone:timeZone]
197 | }
198 | };
199 | }
200 | }
201 |
202 | if ([startDate isEqualToDate:endDate]) {
203 | return @{
204 | @"date_time":[startDate RFC3339String]
205 | };
206 | }
207 | else {
208 | return @{
209 | @"time_interval": @{
210 | @"start_date_time": [startDate RFC3339String],
211 | @"end_date_time": [endDate RFC3339String]
212 | }
213 | };
214 | }
215 | }
216 |
217 | + (NSDictionary*) serializeMetadataArray:(NSDictionary*)metadata {
218 | if(metadata) {
219 | NSMutableArray *serializedArray = [NSMutableArray new];
220 | for (id key in metadata) {
221 | if ([[metadata valueForKey:key] isKindOfClass:[NSDate class]]){
222 | NSDate *dateMetadataValue = [metadata valueForKey:key];
223 | [serializedArray addObject:@{@"key":key,@"value":[dateMetadataValue RFC3339String]}];
224 | }
225 | else if ([[metadata valueForKey:key] isKindOfClass:[HKQuantity class]]) {
226 | // For now, ignore HKQuantity metadata values to avoid a JSON serialization ObjC exception.
227 | // See previous commit for how we might parse/add the value. That would also require schema
228 | // and version changes, which is more than we (LifeOmic) need for now.
229 | }
230 | else{
231 | [serializedArray addObject:@{@"key":key,@"value":[metadata valueForKey:key]}];
232 | }
233 |
234 | }
235 | return @{@"metadata":[serializedArray copy]};
236 | }
237 | return @{};
238 | }
239 |
240 | #pragma mark - Private
241 |
242 | - (id)data {
243 | NSDictionary *serializedBodyDictionaryWithoutMetadata = [self bodyData];
244 | NSMutableDictionary *serializedBodyDictionaryWithMetadata = [NSMutableDictionary dictionaryWithDictionary:serializedBodyDictionaryWithoutMetadata];
245 | [serializedBodyDictionaryWithMetadata addEntriesFromDictionary:[OMHSerializer serializeMetadataArray:self.sample.metadata]];
246 |
247 | return @{
248 | @"header": @{
249 | @"id": self.sample.UUID.UUIDString,
250 | @"creation_date_time": [self.sample.startDate RFC3339String],
251 | @"schema_id": @{
252 | @"namespace": [self schemaNamespace],
253 | @"name": [self schemaName],
254 | @"version": [self schemaVersion]
255 | },
256 | },
257 | @"body":serializedBodyDictionaryWithMetadata
258 | };
259 |
260 | }
261 |
262 | - (NSString*)schemaName {
263 | @throw [[self class] unimplementedException];
264 | }
265 |
266 | - (NSString*)schemaVersion {
267 | @throw [[self class] unimplementedException];
268 | }
269 |
270 | - (id)bodyData {
271 | @throw [[self class] unimplementedException];
272 | }
273 |
274 | - (NSString*)schemaNamespace {
275 | @throw [[self class] unimplementedException];
276 | }
277 |
278 | + (NSException*)unimplementedException {
279 | return [NSException exceptionWithName:NSInternalInconsistencyException
280 | reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]
281 | userInfo:nil];
282 | }
283 | @end
284 |
285 | /**
286 | This serializer maps data from HKQuantityTypeIdentifierStepCount samples to JSON that conforms to the Open mHealth [step-count schema](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_step-count).
287 | */
288 | @interface OMHSerializerStepCount : OMHSerializer; @end;
289 | @implementation OMHSerializerStepCount
290 | + (BOOL)canSerialize:(HKQuantitySample*)sample error:(NSError**)error {
291 | return YES;
292 | }
293 | - (id)bodyData {
294 | HKUnit* unit = [HKUnit unitFromString:@"count"];
295 | double value = [[(HKQuantitySample*)self.sample quantity] doubleValueForUnit:unit];
296 |
297 | return @{
298 | @"step_count": [NSNumber numberWithDouble:value],
299 | @"effective_time_frame": [self populateTimeFrameProperty:self.sample.startDate endDate:self.sample.endDate]
300 | };
301 | }
302 | - (NSString*)schemaName {
303 | return @"step-count";
304 | }
305 | - (NSString*)schemaVersion {
306 | return @"1.0";
307 | }
308 | - (NSString*)schemaNamespace{
309 | return @"omh";
310 | }
311 | @end
312 |
313 | /**
314 | This serializer maps data from HKQuantityTypeIdentifierHeight samples to JSON that conforms to the Open mHealth [body-height schema](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-height).
315 | */
316 | @interface OMHSerializerHeight : OMHSerializer; @end;
317 | @implementation OMHSerializerHeight
318 | + (BOOL)canSerialize:(HKQuantitySample*)sample error:(NSError**)error {
319 | return YES;
320 | }
321 | - (id)bodyData {
322 | NSString* unitString = @"cm";
323 | HKUnit* unit = [HKUnit unitFromString:unitString];
324 | double value = [[(HKQuantitySample*)self.sample quantity] doubleValueForUnit:unit];
325 |
326 | return @{
327 | @"body_height": @{
328 | @"value": [NSNumber numberWithDouble:value],
329 | @"unit": unitString
330 | },
331 | @"effective_time_frame": [self populateTimeFrameProperty:self.sample.startDate endDate:self.sample.endDate]
332 | };
333 | }
334 | - (NSString*)schemaName {
335 | return @"body-height";
336 | }
337 | - (NSString*)schemaVersion {
338 | return @"1.0";
339 | }
340 | - (NSString*)schemaNamespace{
341 | return @"omh";
342 | }
343 | @end
344 |
345 | /**
346 | This serializer maps data from HKQuantityTypeIdentifierBodyMass samples to JSON that conforms to the Open mHealth [body-weight schema](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-weight).
347 | */
348 | @interface OMHSerializerWeight : OMHSerializer; @end;
349 | @implementation OMHSerializerWeight
350 | + (BOOL)canSerialize:(HKQuantitySample*)sample error:(NSError**)error {
351 | return YES;
352 | }
353 | - (id)bodyData {
354 | NSString* unitString = @"lb";
355 | HKUnit* unit = [HKUnit unitFromString:unitString];
356 | double value = [[(HKQuantitySample*)self.sample quantity] doubleValueForUnit:unit];
357 |
358 | return @{
359 | @"body_weight": @{
360 | @"value": [NSNumber numberWithDouble:value],
361 | @"unit": unitString
362 | },
363 | @"effective_time_frame": [self populateTimeFrameProperty:self.sample.startDate endDate:self.sample.endDate]
364 | };
365 | }
366 | - (NSString*)schemaName {
367 | return @"body-weight";
368 | }
369 | - (NSString*)schemaVersion {
370 | return @"1.0";
371 | }
372 | - (NSString*)schemaNamespace{
373 | return @"omh";
374 | }
375 | @end
376 |
377 | /**
378 | This serializer maps data from HKQuantityTypeIdentifierHeartRate samples to JSON that conforms to the Open mHealth [heart-rate schema](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_heart-rate).
379 | */
380 | @interface OMHSerializerHeartRate : OMHSerializer; @end;
381 | @implementation OMHSerializerHeartRate
382 | + (BOOL)canSerialize:(HKQuantitySample*)sample error:(NSError**)error {
383 | return YES;
384 | }
385 | - (id)bodyData {
386 | HKUnit* unit = [HKUnit unitFromString:@"count/min"];
387 | double value = [[(HKQuantitySample*)self.sample quantity] doubleValueForUnit:unit];
388 |
389 | return @{
390 | @"heart_rate": @{
391 | @"value": [NSNumber numberWithDouble:value],
392 | @"unit": @"beats/min"
393 | },
394 | @"effective_time_frame":[self populateTimeFrameProperty:self.sample.startDate endDate:self.sample.endDate]
395 | };
396 | }
397 | - (NSString*)schemaName {
398 | return @"heart-rate";
399 | }
400 | - (NSString*)schemaVersion {
401 | return @"1.0";
402 | }
403 | - (NSString*)schemaNamespace{
404 | return @"omh";
405 | }
406 | @end
407 |
408 | /**
409 | This serializer maps data from HKQuantityTypeIdentifierBloodGlucose samples to JSON that conforms to the Open mHealth [blood-glucose schema](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_blood-glucose).
410 | */
411 | @interface OMHSerializerBloodGlucose : OMHSerializer; @end;
412 | @implementation OMHSerializerBloodGlucose
413 | + (BOOL)canSerialize:(HKQuantitySample*)sample error:(NSError**)error {
414 | return YES;
415 | }
416 | - (id)bodyData {
417 | NSString* unitString = @"mg/dL";
418 | HKUnit* unit = [HKUnit unitFromString:unitString];
419 | double value = [[(HKQuantitySample*)self.sample quantity] doubleValueForUnit:unit];
420 |
421 | return @{
422 | @"blood_glucose": @{
423 | @"value": [NSNumber numberWithDouble:value],
424 | @"unit": unitString
425 | },
426 | @"effective_time_frame": [self populateTimeFrameProperty:self.sample.startDate endDate:self.sample.endDate]
427 | };
428 | }
429 | - (NSString*)schemaName {
430 | return @"blood-glucose";
431 | }
432 | - (NSString*)schemaVersion {
433 | return @"1.0";
434 | }
435 | - (NSString*)schemaNamespace{
436 | return @"omh";
437 | }
438 | @end
439 |
440 | /**
441 | This serializer maps data from HKQuantityTypeIdentifierActiveEnergyBurned samples to JSON that conforms to the Open mHealth [calories-burned schema](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_calories-burned).
442 | */
443 | @interface OMHSerializerEnergyBurned : OMHSerializer; @end;
444 | @implementation OMHSerializerEnergyBurned
445 | + (BOOL)canSerialize:(HKQuantitySample*)sample error:(NSError**)error {
446 | return YES;
447 | }
448 | - (id)bodyData {
449 | NSString* unitString = @"kcal";
450 | HKUnit* unit = [HKUnit unitFromString:unitString];
451 | double value = [[(HKQuantitySample*)self.sample quantity] doubleValueForUnit:unit];
452 |
453 | return @{
454 | @"kcal_burned": @{
455 | @"value": [NSNumber numberWithDouble:value],
456 | @"unit": unitString
457 | },
458 | @"effective_time_frame": [self populateTimeFrameProperty:self.sample.startDate endDate:self.sample.endDate]
459 |
460 | };
461 | }
462 | - (NSString*)schemaName {
463 | return @"calories-burned";
464 | }
465 | - (NSString*)schemaVersion {
466 | return @"1.0";
467 | }
468 | - (NSString*)schemaNamespace{
469 | return @"omh";
470 | }
471 | @end
472 |
473 | /**
474 | This serializer maps data from HKQuantityTypeIdentifierOxygenSaturation samples to JSON that conforms to the Open mHealth [oxygen-saturation schema](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_oxygen-saturation).
475 | */
476 | @interface OMHSerializerOxygenSaturation : OMHSerializer; @end;
477 | @implementation OMHSerializerOxygenSaturation
478 | + (BOOL)canSerialize:(HKQuantitySample*)sample error:(NSError**)error {
479 | return YES;
480 | }
481 | - (id)bodyData {
482 | NSString* unitString = @"%";
483 | HKUnit* unit = [HKUnit unitFromString:unitString];
484 | double value = [[(HKQuantitySample*)self.sample quantity] doubleValueForUnit:unit];
485 |
486 | return @{
487 | @"oxygen_saturation": @{
488 | @"value": [NSNumber numberWithDouble:value*100],
489 | @"unit": unitString
490 | },
491 | @"effective_time_frame": [self populateTimeFrameProperty:self.sample.startDate endDate:self.sample.endDate]
492 |
493 | };
494 | }
495 | - (NSString*)schemaName {
496 | return @"oxygen-saturation";
497 | }
498 | - (NSString*)schemaVersion {
499 | return @"1.0";
500 | }
501 | - (NSString*)schemaNamespace{
502 | return @"omh";
503 | }
504 | @end
505 |
506 | /**
507 | This serializer maps data from HKQuantityTypeIdentifierRespiratoryRate samples to JSON that conforms to the Open mHealth [respiratory-rate schema](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_respiratory-rate).
508 | */
509 | @interface OMHSerializerRespiratoryRate : OMHSerializer; @end;
510 | @implementation OMHSerializerRespiratoryRate
511 | + (BOOL)canSerialize:(HKQuantitySample*)sample error:(NSError**)error {
512 | return YES;
513 | }
514 | - (id)bodyData {
515 | NSString* unitString = @"count/min";
516 | HKUnit* unit = [HKUnit unitFromString:unitString];
517 | float value = [[(HKQuantitySample*)self.sample quantity] doubleValueForUnit:unit];
518 | return @{
519 | @"respiratory_rate": @{
520 | @"value": [NSNumber numberWithDouble:value],
521 | @"unit": @"breaths/min"
522 | },
523 | @"effective_time_frame": [self populateTimeFrameProperty:self.sample.startDate endDate:self.sample.endDate]
524 |
525 | };
526 | }
527 | - (NSString*)schemaName {
528 | return @"respiratory-rate";
529 | }
530 | - (NSString*)schemaVersion {
531 | return @"1.0";
532 | }
533 | - (NSString*)schemaNamespace{
534 | return @"omh";
535 | }
536 | @end
537 |
538 | /**
539 | This serializer maps data from HKQuantityTypeIdentifierBodyTemperature samples to JSON that conforms to the Open mHealth [body-temperature schema](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-temperature).
540 | */
541 | @interface OMHSerializerBodyTemperature : OMHSerializer; @end;
542 | @implementation OMHSerializerBodyTemperature
543 | + (BOOL)canSerialize:(HKQuantitySample*)sample error:(NSError**)error {
544 | return YES;
545 | }
546 | - (id)bodyData {
547 | HKUnit* unit = [HKUnit degreeCelsiusUnit];
548 | float value = [((HKQuantitySample*)self.sample).quantity doubleValueForUnit:unit];
549 |
550 |
551 | NSMutableDictionary* serializedValues = [NSMutableDictionary dictionaryWithDictionary:@{
552 | @"body_temperature": @{
553 | @"value": [NSNumber numberWithDouble:value],
554 | @"unit": @"C"
555 | },
556 | @"effective_time_frame": [self populateTimeFrameProperty:self.sample.startDate endDate:self.sample.endDate]
557 | }];
558 |
559 | if([self.sample.sampleType.description isEqualToString:HKQuantityTypeIdentifierBasalBodyTemperature]) {
560 | BOOL userEntered = (BOOL)[self.sample.metadata objectForKey:HKMetadataKeyWasUserEntered];
561 | if(userEntered == true){
562 | /* Basal body temperature should be taken during sleep or immediately upon waking. It is not possible to tell whether a
563 | measurement was taken during sleep, however if the measurement was self-entered by the user then we assume they took that
564 | measurement first thing in the morning, at waking. */
565 | [serializedValues setObject:@"on waking" forKey:@"temporal_relationship_to_sleep"];
566 | }
567 | }
568 |
569 | NSNumber* bodyTemperatureLocation = self.sample.metadata[HKMetadataKeyBodyTemperatureSensorLocation];
570 | if (bodyTemperatureLocation!=nil){
571 | NSString* measurementLocationString = [self getBodyTemperatureLocationFromConstant:bodyTemperatureLocation];
572 | if(measurementLocationString!=nil){
573 | [serializedValues setObject:measurementLocationString forKey:@"measurement_location"];
574 | }
575 | }
576 |
577 | return serializedValues;
578 | }
579 | - (NSString*) getBodyTemperatureLocationFromConstant:(NSNumber*)temperatureLocationConstant {
580 |
581 | switch ([temperatureLocationConstant intValue]) {
582 | case HKBodyTemperatureSensorLocationArmpit:
583 | return @"axillary";
584 | case HKBodyTemperatureSensorLocationBody:
585 | return nil;
586 | case HKBodyTemperatureSensorLocationEar:
587 | return @"tympanic";
588 | case HKBodyTemperatureSensorLocationEarDrum:
589 | return @"tympanic";
590 | case HKBodyTemperatureSensorLocationFinger:
591 | return @"finger";
592 | case HKBodyTemperatureSensorLocationForehead:
593 | return @"forehead";
594 | case HKBodyTemperatureSensorLocationGastroIntestinal:
595 | return nil;
596 | case HKBodyTemperatureSensorLocationMouth:
597 | return @"oral";
598 | case HKBodyTemperatureSensorLocationOther:
599 | return nil;
600 | case HKBodyTemperatureSensorLocationRectum:
601 | return @"rectal";
602 | case HKBodyTemperatureSensorLocationTemporalArtery:
603 | return @"temporal artery";
604 | case HKBodyTemperatureSensorLocationToe:
605 | return @"toe";
606 | default:
607 | return nil;
608 | }
609 | }
610 | - (NSString*)schemaName {
611 | return @"body-temperature";
612 | }
613 | - (NSString*)schemaVersion {
614 | return @"2.0";
615 | }
616 | - (NSString*)schemaNamespace{
617 | return @"omh";
618 | }
619 | @end
620 |
621 | /**
622 | This serializer maps data from HKCategoryValueSleepAnalysis samples with the HKCategoryValueSleepAnalysisAsleep value to JSON that conforms to the Open mHealth [sleep-duration schema](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_sleep-duration).
623 | */
624 | @interface OMHSerializerSleepAnalysis : OMHSerializer; @end;
625 | @implementation OMHSerializerSleepAnalysis
626 | + (BOOL)canSerialize:(HKCategorySample*)sample error:(NSError**)error {
627 | if (sample.value == HKCategoryValueSleepAnalysisAsleep) return YES;
628 | if (error) {
629 | NSString* errorMessage =
630 | @"HKCategoryValueSleepAnalysis value HKCategoryValueSleepAnalysisInBed uses OMHSerializerGenericCategorySample";
631 | NSDictionary* userInfo = @{ NSLocalizedDescriptionKey : errorMessage };
632 | *error = [NSError errorWithDomain: OMHErrorDomain
633 | code: OMHErrorCodeUnsupportedValues
634 | userInfo: userInfo];
635 | }
636 | return NO;
637 | }
638 | - (id)bodyData {
639 | id value =
640 | [NSNumber numberWithFloat:
641 | [self.sample.endDate timeIntervalSinceDate:self.sample.startDate]];
642 | return @{
643 | @"sleep_duration": @{
644 | @"value": value,
645 | @"unit": @"sec"
646 | },
647 | @"effective_time_frame":[self populateTimeFrameProperty:self.sample.startDate endDate:self.sample.endDate]
648 | };
649 | }
650 | - (NSString*)schemaName {
651 | return @"sleep-duration";
652 | }
653 | - (NSString*)schemaVersion {
654 | return @"1.0";
655 | }
656 | - (NSString*)schemaNamespace{
657 | return @"omh";
658 | }
659 | @end
660 |
661 | /**
662 | This serializer maps data from HKCategoryValueSleepAnalysis samples with the HKQuantityTypeIdentifierBodyFatPercentage value to JSON that conforms to the Open mHealth [body-fat-percentage schema](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-fat-percentage).
663 | */
664 | @interface OMHSerializerBodyFatPercentage : OMHSerializer; @end;
665 | @implementation OMHSerializerBodyFatPercentage
666 | + (BOOL)canSerialize:(HKQuantitySample*)sample error:(NSError**)error {
667 | return YES;
668 | }
669 | - (id)bodyData {
670 | NSString* unitString = @"%";
671 | HKUnit* unit = [HKUnit unitFromString:unitString];
672 | double value = [[(HKQuantitySample*)self.sample quantity] doubleValueForUnit:unit];
673 | return @{
674 | @"body_fat_percentage": @{
675 | @"value": [NSNumber numberWithDouble:value*100],
676 | @"unit": unitString
677 | },
678 | @"effective_time_frame": [self populateTimeFrameProperty:self.sample.startDate endDate:self.sample.endDate]
679 |
680 | };
681 | }
682 | - (NSString*)schemaName {
683 | return @"body-fat-percentage";
684 | }
685 | - (NSString*)schemaVersion {
686 | return @"1.0";
687 | }
688 | - (NSString*)schemaNamespace{
689 | return @"omh";
690 | }
691 | @end
692 |
693 |
694 | /**
695 | This serializer maps data from HKCorrelationTypeIdentifierBloodPressure samples to JSON that conforms to the Open mHealth [blood-pressure schema](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_blood-pressure).
696 | */
697 | @interface OMHSerializerBloodPressure : OMHSerializer; @end;
698 | @implementation OMHSerializerBloodPressure
699 | + (BOOL)canSerialize:(HKCorrelation*)sample error:(NSError**)error {
700 | NSSet* samples = sample.objects;
701 | HKSample* (^firstSampleOfType)(NSString* typeIdentifier) =
702 | ^HKSample*(NSString* typeIdentifier){
703 | return [[samples select:^BOOL(HKSample* sample) {
704 | return [[sample sampleType].identifier isEqual:typeIdentifier];
705 | }] firstObject];
706 | };
707 | HKSample* systolicSample =
708 | firstSampleOfType(HKQuantityTypeIdentifierBloodPressureSystolic);
709 | HKSample* diastolicSample =
710 | firstSampleOfType(HKQuantityTypeIdentifierBloodPressureDiastolic);
711 | if (systolicSample && diastolicSample) return YES;
712 | if (error) {
713 | NSString* errorMessage = @"Missing Diastolic or Systolic sample";
714 | NSDictionary* userInfo = @{ NSLocalizedDescriptionKey : errorMessage };
715 | *error = [NSError errorWithDomain: OMHErrorDomain
716 | code: OMHErrorCodeUnsupportedValues
717 | userInfo: userInfo];
718 | }
719 | return NO;
720 | }
721 | - (id)bodyData {
722 | NSString* unitString = @"mmHg";
723 | HKUnit* unit = [HKUnit unitFromString:unitString];
724 | HKCorrelation* sample = (HKCorrelation*)self.sample;
725 | double (^valueForFirstSampleOfType)(NSString* typeIdentifier) =
726 | ^(NSString* typeIdentifier) {
727 | HKQuantitySample* found =
728 | [[sample.objects select:^BOOL(HKSample* sample) {
729 | return [[sample sampleType].identifier isEqual:typeIdentifier];
730 | }] firstObject];
731 | return [found.quantity doubleValueForUnit:unit];
732 | };
733 | double systolicValue =
734 | valueForFirstSampleOfType(HKQuantityTypeIdentifierBloodPressureSystolic);
735 | double diastolicValue =
736 | valueForFirstSampleOfType(HKQuantityTypeIdentifierBloodPressureDiastolic);
737 | return @{
738 | @"systolic_blood_pressure": @{
739 | @"unit": unitString,
740 | @"value": [NSNumber numberWithDouble: systolicValue]
741 | },
742 | @"diastolic_blood_pressure": @{
743 | @"unit": unitString,
744 | @"value": [NSNumber numberWithDouble: diastolicValue]
745 | },
746 | @"effective_time_frame": [self populateTimeFrameProperty:self.sample.startDate endDate:self.sample.endDate]
747 | };
748 | }
749 | - (NSString*)schemaName {
750 | return @"blood-pressure";
751 | }
752 | - (NSString*)schemaVersion {
753 | return @"1.0";
754 | }
755 | - (NSString*)schemaNamespace{
756 | return @"omh";
757 | }
758 | @end
759 |
760 | /**
761 | This serializer maps data from HKQuantityTypeIdentifierBodyMassIndex samples to JSON that conforms to the Open mHealth [body-mass-index schema](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/omh_body-mass-index).
762 | */
763 | @interface OMHSerializerBodyMassIndex : OMHSerializer; @end;
764 | @implementation OMHSerializerBodyMassIndex
765 |
766 | + (BOOL)canSerialize:(HKSample *)sample error:(NSError *__autoreleasing *)error {
767 | if ([sample.sampleType.description isEqualToString:HKQuantityTypeIdentifierBodyMassIndex]){
768 | return YES;
769 | }
770 | return NO;
771 | }
772 | - (id)bodyData {
773 | HKUnit *unit = [HKUnit unitFromString:@"count"];
774 | HKQuantitySample *quantitySample = (HKQuantitySample*)self.sample;
775 | double value = [[quantitySample quantity] doubleValueForUnit:unit];
776 |
777 | return @{
778 | @"body_mass_index": @{
779 | @"value":[NSNumber numberWithDouble:value],
780 | @"unit":@"kg/m2"
781 | },
782 | @"effective_time_frame":[self populateTimeFrameProperty:quantitySample.startDate endDate:quantitySample.endDate]
783 | };
784 |
785 | }
786 | - (NSString*)schemaName {
787 | return @"body-mass-index";
788 | }
789 | - (NSString*)schemaVersion {
790 | return @"1.0";
791 | }
792 | - (NSString*)schemaNamespace{
793 | return @"omh";
794 | }
795 | @end
796 |
797 | /**
798 | This serializer maps data from HKQuantitySamples to JSON that conforms to the generic, Granola-specific [hk-quantity-sample schema](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-quantity-sample).
799 |
800 | This serializer is used for all quantity types that are not supported by Open mHealth curated schemas. The [supportedTypeIdentifiersWithOMHSchema]([OMHSerializer supportedTypeIdentifiersWithOMHSchema]) method provides a list of schemas that _are_ supported by Open mHealth curated schemas.
801 | */
802 | @interface OMHSerializerGenericQuantitySample : OMHSerializer; @end;
803 | @implementation OMHSerializerGenericQuantitySample
804 |
805 | + (BOOL)canSerialize:(HKSample *)sample error:(NSError *__autoreleasing *)error {
806 | @try{
807 | HKQuantitySample *quantitySample = (HKQuantitySample*)sample;
808 | if([[quantitySample.quantityType description] containsString:@"HKQuantityType"]){
809 | return YES;
810 | }
811 | if (error) {
812 | NSString* errorMessage =
813 | @"HKQuantitySamples should have a quantity type that begins with 'HKQuantityType'";
814 | NSDictionary* userInfo = @{ NSLocalizedDescriptionKey : errorMessage };
815 | *error = [NSError errorWithDomain: OMHErrorDomain
816 | code: OMHErrorCodeIncorrectType
817 | userInfo: userInfo];
818 | }
819 | return NO;
820 | }
821 | @catch (NSException *exception) {
822 | if (error) {
823 | NSString* errorMessage =
824 | @"OMHSerializerGenericQuantitySample is used for HKQuantitySamples only";
825 | NSDictionary* userInfo = @{ NSLocalizedDescriptionKey : errorMessage };
826 | *error = [NSError errorWithDomain: OMHErrorDomain
827 | code: OMHErrorCodeUnsupportedValues
828 | userInfo: userInfo];
829 | }
830 | return NO;
831 | }
832 | }
833 | - (id)bodyData {
834 | HKQuantitySample *quantitySample = (HKQuantitySample*)self.sample;
835 | HKQuantity *quantity = [quantitySample quantity];
836 | NSMutableDictionary *serializedUnitValues = [NSMutableDictionary new];
837 |
838 | if ([[OMHSerializer parseUnitFromQuantity:quantity] isEqualToString:@"%"]) {
839 |
840 | // Types that use "%" units are compatible with the "count" unit (in the next condition), so this condition to pre-empts that.
841 | NSNumber* value = [NSNumber numberWithDouble:[quantity doubleValueForUnit:[HKUnit percentUnit]]];
842 |
843 | [serializedUnitValues addEntriesFromDictionary:@{
844 | @"unit_value":@{
845 | @"value": @([value doubleValue] * 100),
846 | @"unit": @"%"
847 | }
848 | }
849 | ];
850 | }
851 | else if ([quantity isCompatibleWithUnit:[HKUnit unitFromString:@"count"]]) {
852 | [serializedUnitValues addEntriesFromDictionary:@{
853 | @"count": [NSNumber numberWithDouble:[quantity doubleValueForUnit:[HKUnit unitFromString:@"count"]]]
854 | }
855 | ];
856 | }
857 | else{
858 | NSString *unitString = [OMHSerializer parseUnitFromQuantity:quantity];
859 | [serializedUnitValues addEntriesFromDictionary:@{
860 | @"unit_value":@{
861 | @"value": [NSNumber numberWithDouble:[quantity doubleValueForUnit:[HKUnit unitFromString:unitString]]],
862 | @"unit": unitString
863 |
864 | }
865 | }
866 | ];
867 | }
868 |
869 | NSDictionary *partialSerializedDictionary =
870 | @{
871 | @"quantity_type":[quantitySample quantityType].description,
872 | @"effective_time_frame":[self populateTimeFrameProperty:quantitySample.startDate endDate:quantitySample.endDate]
873 | } ;
874 | NSMutableDictionary *fullSerializedDictionary = [partialSerializedDictionary mutableCopy];
875 | [fullSerializedDictionary addEntriesFromDictionary:serializedUnitValues];
876 | return fullSerializedDictionary;
877 | }
878 | - (NSString*)schemaName {
879 | return @"hk-quantity-sample";
880 | }
881 | - (NSString*)schemaVersion {
882 | return @"1.0";
883 | }
884 | - (NSString*)schemaNamespace{
885 | return @"granola";
886 | }
887 | @end
888 |
889 | /**
890 | This serializer maps data from HKCategorySamples to JSON that conforms to the generic, Granola-specific [hk-category-sample schema](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-category-sample).
891 |
892 | This serializer is used for all quantity types that are not supported by Open mHealth curated schemas. The [supportedTypeIdentifiersWithOMHSchema]([OMHSerializer supportedTypeIdentifiersWithOMHSchema]) method provides a list of schemas that _are_ supported by Open mHealth curated schemas.
893 | */
894 | @interface OMHSerializerGenericCategorySample : OMHSerializer; @end;
895 | @implementation OMHSerializerGenericCategorySample
896 |
897 | + (BOOL)canSerialize:(HKSample *)sample error:(NSError *__autoreleasing *)error {
898 | BOOL canSerialize = YES;
899 | @try{
900 | HKCategorySample *categorySample = (HKCategorySample*) sample;
901 | NSArray* categoryTypes = [[OMHHealthKitConstantsMapper allSupportedCategoryTypeIdentifiersToClasses] allKeys];
902 | if(![categoryTypes containsObject:categorySample.categoryType.description]){
903 | if (error) {
904 | NSString* errorMessage = @"The category type is not currently supported.";
905 | NSDictionary* userInfo = @{ NSLocalizedDescriptionKey : errorMessage };
906 | *error = [NSError errorWithDomain: OMHErrorDomain
907 | code: OMHErrorCodeUnsupportedType
908 | userInfo: userInfo];
909 | }
910 | canSerialize = NO;
911 | }
912 |
913 | return canSerialize;
914 | }
915 | @catch(NSException *exception){
916 | if (error) {
917 | NSString* errorMessage =
918 | @"OMHSerializerGenericCategorySample is used for HKCategorySamples only";
919 | NSDictionary* userInfo = @{ NSLocalizedDescriptionKey : errorMessage };
920 | *error = [NSError errorWithDomain: OMHErrorDomain
921 | code: OMHErrorCodeIncorrectType
922 | userInfo: userInfo];
923 | }
924 | return NO;
925 | }
926 | }
927 |
928 | - (id)bodyData {
929 | HKCategorySample *categorySample = (HKCategorySample*) self.sample;
930 |
931 | //Error checking for correct types is done in the canSerialize method.
932 | NSString *schemaMappedValue = [OMHSerializer getCategoryValueForTypeWithValue:categorySample.categoryType categoryValue:categorySample.value];
933 |
934 | return @{
935 | @"effective_time_frame":[self populateTimeFrameProperty:categorySample.startDate endDate:categorySample.endDate],
936 | @"category_type": [categorySample categoryType].description,
937 | @"category_value": schemaMappedValue
938 | };
939 | }
940 |
941 | - (NSString*)schemaName {
942 | return @"hk-category-sample";
943 | }
944 | - (NSString*)schemaVersion {
945 | return @"1.0";
946 | }
947 | - (NSString*)schemaNamespace{
948 | return @"granola";
949 | }
950 | @end
951 |
952 | /**
953 | This serializer maps data from HKCorrelation samples to JSON that conforms to the generic, Granola-specific [hk-correlation schema](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-correlation).
954 |
955 | This serializer is used for all quantity types that are not supported by Open mHealth curated schemas. The [supportedTypeIdentifiersWithOMHSchema]([OMHSerializer supportedTypeIdentifiersWithOMHSchema]) method provides a list of schemas that _are_ supported by Open mHealth curated schemas.
956 | */
957 | @interface OMHSerializerGenericCorrelation : OMHSerializer; @end;
958 | @implementation OMHSerializerGenericCorrelation
959 |
960 | + (BOOL)canSerialize:(HKSample *)sample error:(NSError *__autoreleasing *)error {
961 | @try {
962 | HKCorrelation *correlationSample = (HKCorrelation*)sample;
963 | if(![correlationSample.correlationType.description isEqualToString:HKCorrelationTypeIdentifierBloodPressure] &&
964 | ![correlationSample.correlationType.description isEqualToString:HKCorrelationTypeIdentifierFood]){
965 |
966 | if (error) {
967 | NSString* errorMessage =
968 | [NSString stringWithFormat:@"%@ is not a supported correlation type in HealthKit",correlationSample.correlationType.description];
969 | NSDictionary* userInfo = @{ NSLocalizedDescriptionKey : errorMessage };
970 | *error = [NSError errorWithDomain: OMHErrorDomain
971 | code: OMHErrorCodeUnsupportedValues
972 | userInfo: userInfo];
973 | }
974 |
975 | return NO;
976 | }
977 | }
978 | @catch (NSException *exception){
979 | if (error) {
980 | NSString* errorMessage =
981 | @"OMHSerializerGenericCorrelation is used for HKCorrelation samples only";
982 | NSDictionary* userInfo = @{ NSLocalizedDescriptionKey : errorMessage };
983 | *error = [NSError errorWithDomain: OMHErrorDomain
984 | code: OMHErrorCodeIncorrectType
985 | userInfo: userInfo];
986 | }
987 | return NO;
988 | }
989 | return YES;
990 | }
991 |
992 | - (id)bodyData {
993 | HKCorrelation *correlationSample = (HKCorrelation*)self.sample;
994 |
995 | NSMutableArray *quantitySampleArray = [NSMutableArray new];
996 | NSMutableArray *categorySampleArray = [NSMutableArray new];
997 | for (NSObject *sample in correlationSample.objects) {
998 | if ([sample isKindOfClass:[HKQuantitySample class]]){
999 | //create serialized with the sample input, then call body
1000 | OMHSerializerGenericQuantitySample *quantitySampleSerializer = [OMHSerializerGenericQuantitySample new];
1001 | quantitySampleSerializer = [quantitySampleSerializer initWithSample:(HKQuantitySample*)sample];
1002 | NSError *error = nil;
1003 | if([OMHSerializerGenericQuantitySample canSerialize:(HKSample*)sample error:&error]){
1004 | NSDictionary *serializedQuantitySample = (NSDictionary*)[quantitySampleSerializer bodyData];
1005 | [quantitySampleArray addObject:serializedQuantitySample];
1006 | }
1007 | else{
1008 | NSLog(@"%@",[error localizedDescription]);
1009 | }
1010 | }
1011 | else if ([sample isKindOfClass:[HKCategorySample class]]){
1012 | OMHSerializerGenericCategorySample *categorySampleSerializer = [OMHSerializerGenericCategorySample new];
1013 | categorySampleSerializer = [categorySampleSerializer initWithSample:(HKCategorySample*)sample];
1014 | NSError *error = nil;
1015 | if([OMHSerializerGenericCategorySample canSerialize:(HKSample*)sample error:&error]){
1016 | NSDictionary *serializedCategorySample = (NSDictionary*)[categorySampleSerializer bodyData];
1017 | [quantitySampleArray addObject:serializedCategorySample];
1018 | }
1019 | else{
1020 | NSLog(@"%@",[error localizedDescription]);
1021 | }
1022 | }
1023 | else {
1024 | NSException *e = [NSException
1025 | exceptionWithName:@"CorrelationContainsInvalidSample"
1026 | reason:@"HKCorrelation can only contain samples of type HKQuantitySample or HKCategorySample"
1027 | userInfo:nil];
1028 | @throw e;
1029 | }
1030 | }
1031 |
1032 | return @{@"effective_time_frame":[self populateTimeFrameProperty:correlationSample.startDate endDate:correlationSample.endDate],
1033 | @"correlation_type":correlationSample.correlationType.description,
1034 | @"quantity_samples":quantitySampleArray,
1035 | @"category_samples":categorySampleArray
1036 | };
1037 | }
1038 | - (NSString*)schemaName {
1039 | return @"hk-correlation";
1040 | }
1041 | - (NSString*)schemaVersion {
1042 | return @"1.0";
1043 | }
1044 | - (NSString*)schemaNamespace{
1045 | return @"granola";
1046 | }
1047 | @end
1048 |
1049 | /**
1050 | This serializer maps data from HKWorkout samples to JSON that conforms to the generic, Granola-specific [hk-workout schema](http://www.openmhealth.org/documentation/#/schema-docs/schema-library/schemas/granola_hk-workout).
1051 |
1052 | This serializer is used for all quantity types that are not supported by Open mHealth curated schemas. The [supportedTypeIdentifiersWithOMHSchema]([OMHSerializer supportedTypeIdentifiersWithOMHSchema]) method provides a list of schemas that _are_ supported by Open mHealth curated schemas.
1053 | */
1054 | @interface OMHSerializerGenericWorkout : OMHSerializer; @end
1055 | @implementation OMHSerializerGenericWorkout
1056 |
1057 | + (BOOL)canSerialize:(HKSample *)sample error:(NSError *__autoreleasing *)error {
1058 | if([sample isKindOfClass:[HKWorkout class]]){
1059 | return YES;
1060 | }
1061 | else{
1062 | if (error) {
1063 | NSString* errorMessage =
1064 | @"OMHSerializerGenericWorkout is used for HKWorkout samples only";
1065 | NSDictionary* userInfo = @{ NSLocalizedDescriptionKey : errorMessage };
1066 | *error = [NSError errorWithDomain: OMHErrorDomain
1067 | code: OMHErrorCodeIncorrectType
1068 | userInfo: userInfo];
1069 | }
1070 | return NO;
1071 | }
1072 | }
1073 |
1074 | - (id)bodyData {
1075 | HKWorkout *workoutSample = (HKWorkout*)self.sample;
1076 |
1077 | NSMutableDictionary *fullSerializedDictionary = [NSMutableDictionary new];
1078 | if(workoutSample.totalDistance){
1079 | NSString *unitString = [OMHSerializer parseUnitFromQuantity:workoutSample.totalDistance];
1080 | [fullSerializedDictionary setObject:@{@"value":[NSNumber numberWithDouble:[workoutSample.totalDistance doubleValueForUnit:[HKUnit unitFromString:unitString]]],@"unit":unitString} forKey:@"distance"];
1081 | }
1082 | if(workoutSample.totalEnergyBurned){
1083 | [fullSerializedDictionary setObject:@{@"value":[NSNumber numberWithDouble:[workoutSample.totalEnergyBurned doubleValueForUnit:[HKUnit unitFromString:@"kcal"]]],@"unit":@"kcal"} forKey:@"kcal_burned"];
1084 | }
1085 | if(workoutSample.duration){
1086 | [fullSerializedDictionary setObject:@{@"value":[NSNumber numberWithDouble:workoutSample.duration],@"unit":@"sec"} forKey:@"duration"];
1087 | }
1088 |
1089 | [fullSerializedDictionary addEntriesFromDictionary:@{
1090 | @"effective_time_frame":[self populateTimeFrameProperty:workoutSample.startDate endDate:workoutSample.endDate],
1091 | @"activity_name":[OMHHealthKitConstantsMapper stringForHKWorkoutActivityType:workoutSample.workoutActivityType]
1092 |
1093 | }];
1094 | return fullSerializedDictionary;
1095 | }
1096 |
1097 | - (NSString*)schemaName {
1098 | return @"hk-workout";
1099 | }
1100 | - (NSString*)schemaVersion {
1101 | return @"1.0";
1102 | }
1103 | - (NSString*)schemaNamespace{
1104 | return @"granola";
1105 | }
1106 | @end
1107 |
1108 |
--------------------------------------------------------------------------------