Skip to content

Commit

Permalink
Start APCConsentManager to modularize consenting
Browse files Browse the repository at this point in the history
  • Loading branch information
p2 committed May 11, 2015
1 parent 7229e09 commit af0e942
Show file tree
Hide file tree
Showing 8 changed files with 283 additions and 17 deletions.
8 changes: 8 additions & 0 deletions APCAppCore/APCAppCore.xcodeproj/project.pbxproj
Expand Up @@ -326,6 +326,8 @@
EE028FE41AF94B36001C8251 /* APCKeychainStore+Passcode.m in Sources */ = {isa = PBXBuildFile; fileRef = EE028FE21AF94B36001C8251 /* APCKeychainStore+Passcode.m */; };
EE4B95251AF82BA6000097C7 /* NSError+Bridge.h in Headers */ = {isa = PBXBuildFile; fileRef = EE4B95231AF82BA6000097C7 /* NSError+Bridge.h */; };
EE4B95261AF82BA6000097C7 /* NSError+Bridge.m in Sources */ = {isa = PBXBuildFile; fileRef = EE4B95241AF82BA6000097C7 /* NSError+Bridge.m */; };
EEC608AC1B0157680028BEB2 /* APCConsentManager.h in Headers */ = {isa = PBXBuildFile; fileRef = EEC608AA1B0157680028BEB2 /* APCConsentManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
EEC608AD1B0157680028BEB2 /* APCConsentManager.m in Sources */ = {isa = PBXBuildFile; fileRef = EEC608AB1B0157680028BEB2 /* APCConsentManager.m */; };
F50738C01A682E12004CF100 /* APCDateRange.h in Headers */ = {isa = PBXBuildFile; fileRef = F50738BE1A682E12004CF100 /* APCDateRange.h */; settings = {ATTRIBUTES = (Public, ); }; };
F50738C11A682E12004CF100 /* APCDateRange.m in Sources */ = {isa = PBXBuildFile; fileRef = F50738BF1A682E12004CF100 /* APCDateRange.m */; };
F5306CCD1A8BE7F600732E60 /* ORKQuestionResult+APCHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = F5306CCB1A8BE7F600732E60 /* ORKQuestionResult+APCHelper.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -1013,6 +1015,8 @@
EE028FE21AF94B36001C8251 /* APCKeychainStore+Passcode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "APCKeychainStore+Passcode.m"; sourceTree = "<group>"; };
EE4B95231AF82BA6000097C7 /* NSError+Bridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+Bridge.h"; sourceTree = "<group>"; };
EE4B95241AF82BA6000097C7 /* NSError+Bridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+Bridge.m"; sourceTree = "<group>"; };
EEC608AA1B0157680028BEB2 /* APCConsentManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APCConsentManager.h; sourceTree = "<group>"; };
EEC608AB1B0157680028BEB2 /* APCConsentManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = APCConsentManager.m; sourceTree = "<group>"; };
F50738BE1A682E12004CF100 /* APCDateRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APCDateRange.h; sourceTree = "<group>"; };
F50738BF1A682E12004CF100 /* APCDateRange.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = APCDateRange.m; sourceTree = "<group>"; };
F5179B2919D09128001DCCB7 /* APCAppCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = APCAppCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -1743,6 +1747,8 @@
7B6C8CAC1AA26ECE0007B560 /* Consent */ = {
isa = PBXGroup;
children = (
EEC608AA1B0157680028BEB2 /* APCConsentManager.h */,
EEC608AB1B0157680028BEB2 /* APCConsentManager.m */,
7B6C8CB31AA26ECE0007B560 /* APCConsentTask.h */,
7B6C8CB41AA26ECE0007B560 /* APCConsentTask.m */,
7B6C8CB11AA26ECE0007B560 /* APCConsentQuestion.h */,
Expand Down Expand Up @@ -3157,6 +3163,7 @@
08970F121A8D407600EA46C3 /* APCFoodInsight.h in Headers */,
7B6C8CB71AA26ECE0007B560 /* APCConsentBooleanQuestion.h in Headers */,
5B827B4C1A80A39900C685A3 /* APCDashboardMoreInfoViewController.h in Headers */,
EEC608AC1B0157680028BEB2 /* APCConsentManager.h in Headers */,
369E27FA1A96B7A200D35DFA /* APCMedicationUltraSimpleSelfInflator.h in Headers */,
CFBD289A1AA4408800F49161 /* APCMedicationNameTableViewCell.h in Headers */,
369E280C1A96B7A200D35DFA /* APCMedTrackerPrescription+Helper.h in Headers */,
Expand Down Expand Up @@ -3485,6 +3492,7 @@
F5B948051A73272C0034C522 /* APCPointSelector.m in Sources */,
F5F12AAD1A2F78490015982C /* APCSignUpGeneralInfoViewController.m in Sources */,
F5B948151A73272C0034C522 /* APCScoring.m in Sources */,
EEC608AD1B0157680028BEB2 /* APCConsentManager.m in Sources */,
F5B947B81A73272C0034C522 /* APCStepProgressBar+Appearance.m in Sources */,
CFFDEDFB1A95734000B25581 /* APCSetupTableViewCell.m in Sources */,
7B8C20141AA3AB8600BFEFDA /* APCTheme.m in Sources */,
Expand Down
1 change: 1 addition & 0 deletions APCAppCore/APCAppCore/APCAppCore.h
Expand Up @@ -61,6 +61,7 @@ FOUNDATION_EXPORT const unsigned char APCAppCoreVersionString[];
#import <APCAppCore/APCUtilities.h>

// Tasks
#import <APCAppCore/APCConsentManager.h>
#import <APCAppCore/APCConsentTask.h>
#import <APCAppCore/APCConsentQuestion.h>
#import <APCAppCore/APCConsentBooleanQuestion.h>
Expand Down
63 changes: 63 additions & 0 deletions APCAppCore/APCAppCore/Consent/APCConsentManager.h
@@ -0,0 +1,63 @@
//
// APCConsentManager.h
// APCAppCore
//
// Copyright (c) 2015, Apple, Inc. All rights reserved.
// Copyright (c) 2015, Boston Children's Hospital. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder(s) nor the names of any contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission. No license is granted to the trademarks of
// the copyright holders even if such marks are included in this software.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

#import <Foundation/Foundation.h>

@class APCConsentManager;


/**
* Protocol to be implemented by the app delegate.
*/
@protocol APCConsentManagerDelegate <NSObject>

@required
/// The app's consent manager
- (APCConsentManager *)consentManager;

@end


/**
* The app holds on to an instance of this class to handle consent related tasks.
*/
@interface APCConsentManager : NSObject

/** The name of the JSON file, not including the .json extension, of the consent configuration file in the app bundle. */
- (NSString *)configurationFileName;

/** Return the consent sections, as defined in `fileName`.json. */
- (NSArray *)consentSectionsAndHtmlContent:(NSString **)htmlContent;

@end
184 changes: 184 additions & 0 deletions APCAppCore/APCAppCore/Consent/APCConsentManager.m
@@ -0,0 +1,184 @@
//
// APCConsentManager.m
// APCAppCore
//
// Copyright (c) 2015, Apple, Inc. All rights reserved.
// Copyright (c) 2015, Boston Children's Hospital. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder(s) nor the names of any contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission. No license is granted to the trademarks of
// the copyright holders even if such marks are included in this software.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

#import "APCConsentManager.h"
#import <ResearchKit/ResearchKit.h>


@implementation APCConsentManager


- (NSString *)configurationFileName
{
return @"APHConsentSection"; // Keeping "APH" to not break backwards compatibility, should be "APC"
}

- (NSArray *)consentSectionsAndHtmlContent:(NSString **)htmlContent
{
ORKConsentSectionType(^toSectionType)(NSString*) = ^(NSString* sectionTypeName) {
ORKConsentSectionType sectionType = ORKConsentSectionTypeCustom;

if ([sectionTypeName isEqualToString:@"overview"]) {
sectionType = ORKConsentSectionTypeOverview;
} else if ([sectionTypeName isEqualToString:@"privacy"]) {
sectionType = ORKConsentSectionTypePrivacy;
} else if ([sectionTypeName isEqualToString:@"dataGathering"]) {
sectionType = ORKConsentSectionTypeDataGathering;
} else if ([sectionTypeName isEqualToString:@"dataUse"]) {
sectionType = ORKConsentSectionTypeDataUse;
} else if ([sectionTypeName isEqualToString:@"timeCommitment"]) {
sectionType = ORKConsentSectionTypeTimeCommitment;
} else if ([sectionTypeName isEqualToString:@"studySurvey"]) {
sectionType = ORKConsentSectionTypeStudySurvey;
} else if ([sectionTypeName isEqualToString:@"studyTasks"]) {
sectionType = ORKConsentSectionTypeStudyTasks;
} else if ([sectionTypeName isEqualToString:@"withdrawing"]) {
sectionType = ORKConsentSectionTypeWithdrawing;
} else if ([sectionTypeName isEqualToString:@"custom"]) {
sectionType = ORKConsentSectionTypeCustom;
} else if ([sectionTypeName isEqualToString:@"onlyInDocument"]) {
sectionType = ORKConsentSectionTypeOnlyInDocument;
}

return sectionType;
};

NSString *kDocumentHtml = @"htmlDocument";
NSString *kConsentSections = @"sections";
NSString *kSectionType = @"sectionType";
NSString *kSectionTitle = @"sectionTitle";
NSString *kSectionFormalTitle = @"sectionFormalTitle";
NSString *kSectionSummary = @"sectionSummary";
NSString *kSectionContent = @"sectionContent";
NSString *kSectionHtmlContent = @"sectionHtmlContent";
NSString *kSectionImage = @"sectionImage";
NSString *kSectionAnimationUrl = @"sectionAnimationUrl";

NSString *resource = [[NSBundle bundleForClass:[self class]] pathForResource:[self configurationFileName] ofType:@"json"];
NSAssert(resource, @"Unable to locate file \"%@.json\" with Consent Section data in bundle, as defined in `configurationFileName`", [self configurationFileName]);

NSData *consentSectionData = [NSData dataWithContentsOfFile:resource];
NSAssert(consentSectionData, @"Unable to create NSData with Consent Section data");

NSError *error = nil;
NSDictionary *consentParameters = [NSJSONSerialization JSONObjectWithData:consentSectionData options:NSJSONReadingMutableContainers error:&error];
NSAssert(consentParameters, @"badly formed Consent Section data - error", error);

NSString *documentHtmlContent = consentParameters[kDocumentHtml];
NSAssert(documentHtmlContent == nil || documentHtmlContent && [documentHtmlContent isKindOfClass:[NSString class]], @"Improper Document HTML Content type");

if (documentHtmlContent != nil && htmlContent != nil) {
NSString *path = [[NSBundle mainBundle] pathForResource:documentHtmlContent ofType:@"html" inDirectory:@"HTMLContent"];
NSAssert(path != nil, @"Unable to locate HTML file: %@", documentHtmlContent);

NSError *error = nil;
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];

NSAssert(content != nil, @"Unable to load content of file \"%@\": %@", path, error);

*htmlContent = content;
}

NSArray *parametersConsentSections = consentParameters[kConsentSections];
NSAssert(parametersConsentSections != nil && [parametersConsentSections isKindOfClass:[NSArray class]], @"Badly formed Consent Section data");

NSMutableArray *consentSections = [NSMutableArray arrayWithCapacity:parametersConsentSections.count];

for (NSDictionary *section in parametersConsentSections) {
// Custom types do not have predefined title, summary, content, or animation
NSAssert([section isKindOfClass:[NSDictionary class]], @"Improper section type");

NSString *typeName = section[kSectionType];
NSAssert(typeName != nil && [typeName isKindOfClass:[NSString class]], @"Missing Section Type or improper type");

ORKConsentSectionType sectionType = toSectionType(typeName);

NSString *title = section[kSectionTitle];
NSString *formalTitle = section[kSectionFormalTitle];
NSString *summary = section[kSectionSummary];
NSString *content = section[kSectionContent];
NSString *htmlContent = section[kSectionHtmlContent];
NSString *image = section[kSectionImage];
NSString *animationUrl = section[kSectionAnimationUrl];

NSAssert(title == nil || title != nil && [title isKindOfClass:[NSString class]], @"Missing Section Title or improper type");
NSAssert(formalTitle == nil || formalTitle != nil && [formalTitle isKindOfClass:[NSString class]], @"Missing Section Formal title or improper type");
NSAssert(summary == nil || summary != nil && [summary isKindOfClass:[NSString class]], @"Missing Section Summary or improper type");
NSAssert(content == nil || content != nil && [content isKindOfClass:[NSString class]], @"Missing Section Content or improper type");
NSAssert(htmlContent == nil || htmlContent != nil && [htmlContent isKindOfClass:[NSString class]], @"Missing Section HTML Content or improper type");
NSAssert(image == nil || image != nil && [image isKindOfClass:[NSString class]], @"Missing Section Image or improper typte");
NSAssert(animationUrl == nil || animationUrl != nil && [animationUrl isKindOfClass:[NSString class]], @"Missing Animation URL or improper type");

ORKConsentSection *section = [[ORKConsentSection alloc] initWithType:sectionType];
section.title = title;
section.formalTitle = formalTitle;
section.summary = summary;
section.content = content;

if (htmlContent != nil) {
NSString *path = [[NSBundle mainBundle] pathForResource:htmlContent ofType:@"html" inDirectory:@"HTMLContent"];
NSAssert(path, @"Unable to locate HTML file: %@", htmlContent);

NSError *error = nil;
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];

NSAssert(content, @"Unable to load content of file \"%@\": %@", path, error);
section.htmlContent = content;
}
if (image != nil) {
section.customImage = [UIImage imageNamed:image];
NSAssert(section.customImage != nil, @"Unable to load image: %@", image);
}
if (animationUrl != nil) {
NSString *nameWithScaleFactor = animationUrl;
if ([[UIScreen mainScreen] scale] >= 3) {
nameWithScaleFactor = [nameWithScaleFactor stringByAppendingString:@"@3x"];
} else {
nameWithScaleFactor = [nameWithScaleFactor stringByAppendingString:@"@2x"];
}
NSURL *url = [[NSBundle mainBundle] URLForResource:nameWithScaleFactor withExtension:@"m4v"];
NSError *error = nil;

NSAssert([url checkResourceIsReachableAndReturnError:&error], @"Animation file--%@--not reachable: %@", animationUrl, error);
section.customAnimationURL = url;
}

[consentSections addObject:section];
}

return consentSections;
}


@end
1 change: 1 addition & 0 deletions APCAppCore/APCAppCore/Library/Categories/NSError+Bridge.m
Expand Up @@ -32,6 +32,7 @@
//

#import "NSError+Bridge.h"
#import "APCLog.h"
#import <BridgeSDK/BridgeSDK.h>

static NSString *kSageMessageKey = @"message";
Expand Down
5 changes: 2 additions & 3 deletions APCAppCore/APCAppCore/Startup/APCAppDelegate.h
Expand Up @@ -36,13 +36,14 @@
#import "APCOnboarding.h"
#import "APCPasscodeViewController.h"
#import "APCProfileViewController.h"
#import "APCConsentManager.h"
#import "APCConsentTask.h"

extern NSUInteger const kTheEntireDataModelOfTheApp;

@class APCDataSubstrate, APCDataMonitor, APCScheduler, APCOnboarding, APCPasscodeViewController, APCTasksReminderManager, APCPassiveDataCollector, APCFitnessAllocation;

@interface APCAppDelegate : UIResponder <UIApplicationDelegate, APCOnboardingDelegate, APCOnboardingTaskDelegate, APCPasscodeViewControllerDelegate>
@interface APCAppDelegate : UIResponder <UIApplicationDelegate, APCOnboardingDelegate, APCConsentManagerDelegate, APCOnboardingTaskDelegate, APCPasscodeViewControllerDelegate>

@property (nonatomic, strong) APCFitnessAllocation *sevenDayFitnessAllocationData;
@property (strong, nonatomic) UITabBarController *tabster;
Expand Down Expand Up @@ -89,7 +90,6 @@ extern NSUInteger const kTheEntireDataModelOfTheApp;
- (void) setUpInitializationOptions;
- (void) setUpAppAppearance;
- (void) registerCatastrophicStartupError: (NSError *) error;
- (void) configureObserverQueries;

//For User in Subclasses
- (void) signedInNotification:(NSNotification *)notification;
Expand All @@ -110,7 +110,6 @@ extern NSUInteger const kTheEntireDataModelOfTheApp;
- (void)showPasscodeIfNecessary;

- (ORKTaskViewController *)consentViewController;
- (NSMutableArray*)consentSectionsAndHtmlContent:(NSString**)htmlContent;

- (void)instantiateOnboardingForType:(APCOnboardingTaskType)type;

Expand Down
5 changes: 5 additions & 0 deletions APCAppCore/APCAppCore/Startup/APCAppDelegate.m
Expand Up @@ -645,6 +645,11 @@ - (NSArray *)reviewConsentActions
return nil;
}

- (APCConsentManager *)consentManager
{
return [APCConsentManager new];
}

// The block of text for the All Set
- (NSArray *)allSetTextBlocks
{
Expand Down

0 comments on commit af0e942

Please sign in to comment.