Skip to content

Commit 2295110

Browse files
authored
Public Release 3.2.0
1 parent d247402 commit 2295110

File tree

105 files changed

+5017
-1088
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

105 files changed

+5017
-1088
lines changed

RELEASE-NOTES.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,43 @@
11
# ResearchKit Release Notes
22

3+
## ResearchKit 3.2.0 Release Notes
4+
### API Changes
5+
- **`ORKRecorderConfiguration`**
6+
Deprecated `recorder(for:outputDirectory:)` method in favor of setting the `outputDirectory` property when initializing `ORKConfiguration`, then calling its new `recorder(for:)` method.
7+
8+
### General bug fixes
9+
- **`ORKRecorder`**
10+
- Fixed issue where results for active tasks using `ORKRecorder` did not account for all recorded files.
11+
- Added file rolling as an API-level setting for `ORKRecorder` configuration.
12+
- Added `timestampSince1970` for logged `CMLogItems` for better usability.
13+
14+
- **Active Tasks**
15+
- **Amsler Grid**
16+
- Fixed issue where the names of the dat files created for the recorded data contained two periods.
17+
- Fixed serialization issue preventing `ORKAmslerGridResult` from being properly serialized.
18+
19+
- **dBHL Tone Audiometry**
20+
- Updated the current retSPL dBSPL tables for improved accuracy in audio-based active tasks.
21+
22+
- **Reaction Time**
23+
- Fixed issue where all device motion files were not taken into account when reporting results.
24+
25+
- **Shoulder Range of Motion**
26+
- Fixed issue where some instructions were not vocalized during the task.
27+
28+
- **Spatial Span Memory**
29+
- Fixed crash when serializing `ORKSpatialSpanMemoryGameTouchSample` objects.
30+
31+
- **Tremor Test**
32+
- Fixed serialization issue preventing tremor test results from being properly serialized.
33+
34+
- **`ORKFormStepViewController`**
35+
- Fixed crash in `ORKFormStepViewController` that could occur during form step
36+
navigation.
37+
38+
- **`ORKCheckMarkView`**
39+
- Fixed recursive crash that occurred when setting tint color on `ORKCheckMarkView` components.
40+
341
## ResearchKit 3.1.1 Release Notes
442
General bug fixes for the following:
543

ResearchKit.xcodeproj/project.pbxproj

Lines changed: 41 additions & 3 deletions
Large diffs are not rendered by default.

ResearchKit/Common/ORKDataLogger.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,13 @@ ORK_CLASS_AVAILABLE
122122
@param url The URL of the directory in which to place log files
123123
@param logName The prefix on the log file name in an ASCII string. Note that
124124
the string must not contain the hyphen character ("-"), because a hyphen is used as a separator in the log naming scheme.
125+
@param fileExtension The file extension to use when creating files on disk.
125126
@param formatter The type of formatter to use for the log, such as `ORKJSONLogFormatter`.
126127
@param delegate The initial delegate. May be `nil`.
127128
128129
@return An initialized data logger.
129130
*/
130-
- (instancetype)initWithDirectory:(NSURL *)url logName:(NSString *)logName formatter:(ORKLogFormatter *)formatter delegate:(nullable id<ORKDataLoggerDelegate>)delegate NS_DESIGNATED_INITIALIZER;
131+
- (instancetype)initWithDirectory:(NSURL *)url logName:(NSString *)logName fileExtension:(NSString *)fileExtension formatter:(ORKLogFormatter *)formatter delegate:(nullable id<ORKDataLoggerDelegate>)delegate NS_DESIGNATED_INITIALIZER;
131132

132133
/// The delegate to be notified when file sizes change or the log rolls over.
133134
@property (weak, nullable) id<ORKDataLoggerDelegate> delegate;
@@ -483,12 +484,13 @@ ORK_CLASS_AVAILABLE
483484
Adds a data logger with a particular formatter to the directory.
484485
485486
@param logName The log name prefix for the data logger.
487+
@param fileExtension The file extension to use when creating files on disk.
486488
@param formatter The log formatter instance to use for this logger.
487489
488490
@return The `ORKDataLogger` object that was added, or the existing one if one already existed for
489491
that log name.
490492
*/
491-
- (ORKDataLogger *)addDataLoggerForLogName:(NSString *)logName formatter:(ORKLogFormatter *)formatter;
493+
- (ORKDataLogger *)addDataLoggerForLogName:(NSString *)logName fileExtension:(NSString *)fileExtension formatter:(ORKLogFormatter *)formatter;
492494

493495
/**
494496
Retrieves the already existing data logger for a log name.

ResearchKit/Common/ORKDataLogger.m

Lines changed: 45 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -73,25 +73,30 @@ - (void)resume;
7373

7474
@implementation NSURL (ORKDataLogger)
7575

76+
- (NSString *)ork_fileName {
77+
NSString *fileName = [[self lastPathComponent] stringByDeletingPathExtension];
78+
return fileName;
79+
}
80+
7681
- (NSString *)ork_logName {
77-
NSString *lastComponent = [self lastPathComponent];
78-
NSRange idx = [lastComponent rangeOfString:@"-"];
82+
NSString *fileName = [self ork_fileName];
83+
NSRange idx = [fileName rangeOfString:@"-"];
7984
if (!idx.length) {
8085
@throw [NSException exceptionWithName:NSGenericException reason:@"URL is not a completed log file" userInfo:@{@"url":self}];
8186
}
8287

83-
NSString *logName = [lastComponent substringToIndex:idx.location];
88+
NSString *logName = [fileName substringToIndex:idx.location];
8489
return logName;
8590
}
8691

8792
- (NSString *)ork_logDateComponent {
88-
NSString *lastComponent = [self lastPathComponent];
89-
NSRange idx = [lastComponent rangeOfString:@"-"];
93+
NSString *fileName = [self ork_fileName];
94+
NSRange idx = [fileName rangeOfString:@"-"];
9095
if (!idx.length) {
9196
@throw [NSException exceptionWithName:NSGenericException reason:@"URL is not a completed log file" userInfo:@{@"url":self}];
9297
}
9398

94-
NSString *logDateComponent = [lastComponent substringFromIndex:idx.location + 1];
99+
NSString *logDateComponent = [fileName substringFromIndex:idx.location + 1];
95100
return logDateComponent;
96101
}
97102

@@ -145,8 +150,8 @@ - (NSString *)ork_logNameInDirectory:(NSURL *)directory {
145150
@throw [NSException exceptionWithName:NSGenericException reason:@"URL is not a fileURL" userInfo:@{@"url":self}];
146151
}
147152

148-
NSString *lastComponent = [self lastPathComponent];
149-
NSRange idx = [lastComponent rangeOfString:@"-"];
153+
NSString *fileName = [self ork_fileName];
154+
NSRange idx = [fileName rangeOfString:@"-"];
150155
if (!idx.length) {
151156
@throw [NSException exceptionWithName:NSGenericException reason:@"URL is not a completed log file" userInfo:@{@"url":self}];
152157
}
@@ -155,7 +160,7 @@ - (NSString *)ork_logNameInDirectory:(NSURL *)directory {
155160
@throw [NSException exceptionWithName:NSGenericException reason:@"URL is not in expected directory" userInfo:@{@"url":self}];
156161
}
157162

158-
NSString *logName = [lastComponent substringToIndex:idx.location];
163+
NSString *logName = [fileName substringToIndex:idx.location];
159164
return logName;
160165
}
161166

@@ -446,6 +451,7 @@ @implementation ORKDataLogger {
446451
ORKObjectObserver *_observer;
447452

448453
NSString *_oldLogsPrefix;
454+
NSString *_fileExtension;
449455

450456
NSFileHandle *_currentFileHandle;
451457

@@ -457,7 +463,7 @@ @implementation ORKDataLogger {
457463
}
458464

459465
+ (ORKDataLogger *)JSONDataLoggerWithDirectory:(NSURL *)url logName:(NSString *)logName delegate:(id<ORKDataLoggerDelegate>)delegate {
460-
return [[ORKDataLogger alloc] initWithDirectory:url logName:logName formatter:[ORKJSONLogFormatter new] delegate:delegate];
466+
return [[ORKDataLogger alloc] initWithDirectory:url logName:logName fileExtension:@"json" formatter:[ORKJSONLogFormatter new] delegate:delegate];
461467
}
462468

463469
+ (instancetype)new {
@@ -468,7 +474,7 @@ - (instancetype)init {
468474
ORKThrowMethodUnavailableException();
469475
}
470476

471-
- (instancetype)initWithDirectory:(NSURL *)url logName:(NSString *)logName formatter:(ORKLogFormatter *)formatter delegate:(id<ORKDataLoggerDelegate>)delegate {
477+
- (instancetype)initWithDirectory:(NSURL *)url logName:(NSString *)logName fileExtension:(NSString *)fileExtension formatter:(ORKLogFormatter *)formatter delegate:(id<ORKDataLoggerDelegate>)delegate {
472478
self = [super init];
473479
if (self) {
474480
_url = [url copy];
@@ -486,13 +492,13 @@ - (instancetype)initWithDirectory:(NSURL *)url logName:(NSString *)logName forma
486492
_queue = dispatch_queue_create([queueId cStringUsingEncoding:NSUTF8StringEncoding], DISPATCH_QUEUE_SERIAL);
487493

488494
_directoryUpdateGroup = dispatch_group_create();
489-
495+
490496
self.logName = logName;
491497
self.logFormatter = formatter;
492498
self.delegate = delegate;
493499
self.fileProtectionMode = ORKFileProtectionNone;
494500
_oldLogsPrefix = [_logName stringByAppendingString:@"-"];
495-
501+
_fileExtension = fileExtension;
496502
_observer = [[ORKObjectObserver alloc] initWithObject:self keys:@[@"maximumCurrentLogFileLifetime", @"maximumCurrentLogFileSize"] selector:@selector(fileSizeLimitsDidChange)];
497503

498504
[self setupDirectorySource];
@@ -506,7 +512,7 @@ - (instancetype)initWithDirectory:(NSURL *)url configuration:(NSDictionary *)con
506512
@throw [NSException exceptionWithName:NSGenericException reason:[NSString stringWithFormat:@"%@ is not a class", configuration[@"formatterClass"]] userInfo:nil];
507513
}
508514

509-
self = [self initWithDirectory:url logName:configuration[@"logName"] formatter:[[formatterClass alloc] init] delegate:delegate];
515+
self = [self initWithDirectory:url logName:configuration[@"logName"] fileExtension:configuration[@"fileExtension"] formatter:[[formatterClass alloc] init] delegate:delegate];
510516
if (self) {
511517
// Don't notify about initial setup
512518
[_observer pause];
@@ -519,6 +525,7 @@ - (instancetype)initWithDirectory:(NSURL *)url configuration:(NSDictionary *)con
519525

520526
- (NSDictionary *)configuration {
521527
return @{@"logName": self.logName,
528+
@"fileExtension": _fileExtension,
522529
@"formatterClass": NSStringFromClass([self.logFormatter class]),
523530
@"fileProtectionMode": @(self.fileProtectionMode),
524531
@"maximumCurrentLogFileSize": @(self.maximumCurrentLogFileSize),
@@ -574,12 +581,18 @@ - (void)finishCurrentLog {
574581
}
575582

576583
- (NSURL *)currentLogFileURL {
577-
return [_url URLByAppendingPathComponent:_logName];
584+
return [[_url URLByAppendingPathComponent:_logName] URLByAppendingPathExtension:_fileExtension];
578585
}
579586

580-
- (BOOL)urlMatchesLogName:(NSURL *)url {
581-
NSString *lastComponent = [url lastPathComponent];
582-
return ([lastComponent isEqualToString:_logName] || [lastComponent hasPrefix:_oldLogsPrefix]);
587+
- (BOOL)doesURLMatchLogName:(NSURL *)url {
588+
NSString *fileName = [url ork_fileName];
589+
return [fileName isEqualToString:_logName];
590+
}
591+
592+
- (BOOL)didCreateLogAt:(NSURL *)url {
593+
BOOL doesURLMatchLogName = [self doesURLMatchLogName:url];
594+
NSString *fileName = [url ork_fileName];
595+
return (doesURLMatchLogName || [fileName hasPrefix:_oldLogsPrefix]);
583596
}
584597

585598
- (NSFileHandle *)fileHandle {
@@ -724,10 +737,10 @@ - (BOOL)queue_enumerateLogs:(void (^)(NSURL *logFileUrl, BOOL *stop))block error
724737
NSError *error = nil;
725738
NSMutableArray *urls = [NSMutableArray array];
726739
for (NSURL *url in enumerator) {
727-
if (![self urlMatchesLogName:url]) {
740+
if (![self didCreateLogAt:url]) {
728741
continue;
729742
}
730-
if ( [[url lastPathComponent] isEqualToString:_logName]) {
743+
if ([self doesURLMatchLogName:url]) {
731744
// Don't include the "current" log file
732745
continue;
733746
}
@@ -839,7 +852,7 @@ - (NSFileHandle *)queue_fileHandleWithError:(NSError **)errorOut {
839852
return _currentFileHandle;
840853
}
841854

842-
+ (NSURL *)nextUrlForDirectoryUrl:(NSURL *)directory logName:(NSString *)logName {
855+
+ (NSURL *)nextUrlForDirectoryUrl:(NSURL *)directory logName:(NSString *)logName fileExtension:(NSString *)fileExtension {
843856
static NSDateFormatter *dateFromatter = nil;
844857
static dispatch_once_t onceToken;
845858
dispatch_once(&onceToken, ^{
@@ -849,14 +862,14 @@ + (NSURL *)nextUrlForDirectoryUrl:(NSURL *)directory logName:(NSString *)logName
849862
});
850863

851864
NSString *datedLog = [NSString stringWithFormat:@"%@-%@",logName, [dateFromatter stringFromDate:[NSDate date]]];
852-
NSURL *destinationUrl = [directory URLByAppendingPathComponent:datedLog];
865+
NSURL *destinationUrl = [[directory URLByAppendingPathComponent:datedLog] URLByAppendingPathExtension:fileExtension];
853866

854867
NSFileManager *fileManager = [NSFileManager defaultManager];
855868
int digit = 0;
856869
while ([fileManager fileExistsAtPath:[destinationUrl path] isDirectory:NULL]) {
857870
digit ++;
858-
NSString *lastComponent = [datedLog stringByAppendingFormat:@"-%02d",digit];
859-
destinationUrl = [directory URLByAppendingPathComponent:lastComponent];
871+
NSString *newDatedLog = [datedLog stringByAppendingFormat:@"-%02d",digit];
872+
destinationUrl = [[directory URLByAppendingPathComponent:newDatedLog] URLByAppendingPathExtension:fileExtension];
860873
}
861874

862875
return destinationUrl;
@@ -878,7 +891,7 @@ - (void)queue_closeAndRenameLog {
878891

879892
if (((NSNumber *)parameters[NSURLIsRegularFileKey]).boolValue) {
880893
if (((NSNumber *)parameters[NSURLFileSizeKey]).intValue > 0) {
881-
NSURL *destinationUrl = [ORKDataLogger nextUrlForDirectoryUrl:_url logName:_logName];
894+
NSURL *destinationUrl = [ORKDataLogger nextUrlForDirectoryUrl:_url logName:_logName fileExtension:_fileExtension];
882895
ORK_Log_Debug("Rollover: %@ to %@", [url lastPathComponent], [destinationUrl lastPathComponent]);
883896
[fileManager moveItemAtURL:url toURL:destinationUrl error:nil];
884897
if (self.fileProtectionMode == ORKFileProtectionCompleteUnlessOpen) {
@@ -1135,17 +1148,17 @@ - (void)configurationDidChange {
11351148
}
11361149

11371150
- (ORKDataLogger *)addJSONDataLoggerForLogName:(NSString *)logName {
1138-
return [self addDataLoggerForLogName:logName formatter:[ORKJSONLogFormatter new]];
1151+
return [self addDataLoggerForLogName:logName fileExtension:@"json" formatter:[ORKJSONLogFormatter new]];
11391152
}
11401153

1141-
- (ORKDataLogger *)queue_addDataLoggerForLogName:(NSString *)logName formatter:(ORKLogFormatter *)formatter {
1142-
ORKDataLogger *dataLogger = [[ORKDataLogger alloc] initWithDirectory:_directory logName:logName formatter:formatter delegate:self];
1143-
dataLogger.delegate = nil;
1154+
- (ORKDataLogger *)queue_addDataLoggerForLogName:(NSString *)logName fileExtension:(NSString *)fileExtension formatter:(ORKLogFormatter *)formatter {
1155+
ORKDataLogger *dataLogger = [[ORKDataLogger alloc] initWithDirectory:_directory logName:logName fileExtension:fileExtension formatter:formatter delegate:self];
1156+
11441157
// Pick suitable defaults for a typical use pattern
11451158
dataLogger.maximumCurrentLogFileLifetime = ORKDataLoggerManagerDefaultLogFileLifetime;
11461159
dataLogger.maximumCurrentLogFileSize = ORKDataLoggerManagerDefaultLogFileSize;
11471160
dataLogger.delegate = self;
1148-
1161+
11491162
_records[logName] = dataLogger;
11501163
[self queue_synchronizeConfiguration];
11511164

@@ -1154,14 +1167,14 @@ - (ORKDataLogger *)queue_addDataLoggerForLogName:(NSString *)logName formatter:(
11541167
return dataLogger;
11551168
}
11561169

1157-
- (ORKDataLogger *)addDataLoggerForLogName:(NSString *)logName formatter:(ORKLogFormatter *)formatter {
1170+
- (ORKDataLogger *)addDataLoggerForLogName:(NSString *)logName fileExtension:(NSString *)fileExtension formatter:(ORKLogFormatter *)formatter {
11581171
if (_records[logName]) {
11591172
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"Duplicate logger with log name '%@'",logName] userInfo:nil];
11601173
}
11611174

11621175
__block ORKDataLogger *dataLogger = nil;
11631176
dispatch_sync(_queue, ^{
1164-
dataLogger = [self queue_addDataLoggerForLogName:logName formatter:formatter];
1177+
dataLogger = [self queue_addDataLoggerForLogName:logName fileExtension:fileExtension formatter:formatter];
11651178
});
11661179
return dataLogger;
11671180
}

ResearchKit/Common/ORKFormStep.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ - (instancetype)initWithIdentifier:(NSString *)identifier
5555
self.useCardView = YES;
5656
self.autoScrollEnabled = YES;
5757
self.cardViewStyle = ORKCardViewStyleDefault;
58+
self.headerTextAlignment = NSTextAlignmentNatural;
5859
}
5960
return self;
6061
}

ResearchKit/Common/ORKHelpers.m

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,3 +560,24 @@ void ORKAdjustPageViewControllerNavigationDirectionForRTL(UIPageViewControllerNa
560560
return numberFormatter;
561561
}
562562

563+
564+
// MARK: - NSPredicate
565+
566+
NSPredicate* _Nullable ORKPredicateWithFormat(NSString * _Nonnull predicateFormat,
567+
NSString * _Nonnull callerID) {
568+
NSPredicate *predicate;
569+
@try {
570+
predicate = [NSPredicate predicateWithFormat:predicateFormat];
571+
} @catch (NSException *exception) {
572+
ORK_Log_Fault(
573+
"%{public}@: Error creating predicate from predicateWithFormat string: \
574+
'%{public}@' (error: %{public}@)",
575+
callerID,
576+
predicateFormat,
577+
[exception debugDescription]
578+
);
579+
predicate = nil;
580+
}
581+
return predicate;
582+
}
583+

ResearchKit/Common/ORKHelpers_Internal.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,3 +421,18 @@ ORKLocalizedHiddenString(key)
421421
[NSNumberFormatter localizedStringFromNumber:number numberStyle:NSNumberFormatterNoStyle]
422422

423423
NS_ASSUME_NONNULL_END
424+
425+
// MARK: - NSPredicate
426+
427+
/**
428+
This function attempts to create an `NSPredicate` from a predicate format `NSString`.
429+
If this fails, an error message starting with the provided `callerID` `NSString` will be logged,
430+
and the `NSPredicate` returned will be `nil`.
431+
432+
@param predicateFormat The `NSString` to attempt creating the `NSPredicate` from.
433+
@param callerID An `NSString` identifying the calling class for error logging purposes.
434+
435+
@return An `NSPredicate` created from `predicateFormat` if the operation was successful, `nil` if not.
436+
*/
437+
NSPredicate* _Nullable ORKPredicateWithFormat(NSString * _Nonnull predicateFormat,
438+
NSString * _Nonnull callerID);

ResearchKit/Common/ORKPredicateFormItemVisibilityRule.m

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,8 @@ - (instancetype)init {
4343
}
4444

4545
- (nullable instancetype)initWithPredicateFormat:(NSString *)predicateFormat {
46-
NSPredicate *predicate;
47-
@try {
48-
predicate = [NSPredicate predicateWithFormat:predicateFormat];
49-
} @catch (NSException *exception) {
50-
ORK_Log_Fault("ORKPredicateFormItemVisibilityRule: Error creating predicate from predicateFormat string: '%{public}@' (error: %{public}@)", predicateFormat, [exception debugDescription]);
51-
predicate = nil;
52-
}
46+
NSPredicate *predicate = ORKPredicateWithFormat(predicateFormat, @"ORKPredicateFormItemVisibilityRule");
47+
5348
if (predicate != nil) {
5449
self = [self initWithPredicate:predicate];
5550
_predicateFormat = [predicateFormat copy];

ResearchKit/Common/ORKPredicateFormItemVisibilityRule_Private.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,16 @@ NS_ASSUME_NONNULL_BEGIN
3636

3737
/**
3838
To support deserialization, where deserialization of NSPredicates isn't supported, this class extension allows initializing
39-
an `ORKPredicateFormItemVisibilityRule` with a predicateFormat `NSString` instead of an `NSPredicate`
40-
and retains the original predicateFormat for serialization.
39+
an `ORKPredicateFormItemVisibilityRule` with a `predicateFormat` `NSString` instead of an `NSPredicate`
40+
and retains the original `predicateFormat` for serialization.
4141
*/
4242
- (nullable instancetype)initWithPredicateFormat:(NSString *)predicateFormat;
4343

44+
/**
45+
To support deserialization, where deserialization of NSPredicates isn't supported, this class extension allows initializing
46+
an `ORKPredicateFormItemVisibilityRule` with a `predicateFormat` `NSString` instead of an `NSPredicate`
47+
and retains the original `predicateFormat` for serialization by setting this property.
48+
*/
4449
@property (nonatomic, nullable, copy, readonly) NSString *predicateFormat;
4550

4651
@end

0 commit comments

Comments
 (0)