ResearchKit study notes

We can know some coding practices of Apple when reading ResearchKit source code.

1. Use layout debug and layout test

    self.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.2];
    _titleLabel.backgroundColor = [[UIColor greenColor] colorWithAlphaComponent:0.2];
    _valueLabel.backgroundColor = [[UIColor greenColor] colorWithAlphaComponent:0.2];

    self.timeLeft = 60*5;
    self.hasHeartRate = YES;
    self.hasDistance = YES;
    self.distanceInMeters = 100;
    self.heartRate = @"22";

2. Storyboard VS code

in ResearchKit, all views are written by code, but in AppCore, some views and view controllers are prepared in nibs or storyboards.

3. Small classes

They create many small classes in one file, even these classes are similar.

@interface APCTableViewDashboardProgressItem : APCTableViewDashboardItem

@property (nonatomic) CGFloat progress;


@interface APCTableViewDashboardGraphItem : APCTableViewDashboardItem

@property (nonatomic, strong) APCScoring *graphData;

@property (nonatomic) APCDashboardGraphType graphType;

@property (nonatomic, strong) UIImage *minimumImage;

@property (nonatomic, strong) UIImage *maximumImage;

@property (nonatomic, strong) UIImage *averageImage;


@interface APCTableViewDashboardMessageItem : APCTableViewDashboardItem

@property (nonatomic) APCDashboardMessageType messageType;


4. Add Nullability annotations

nullable, nonnull, NS_ASSUME_NONNULL_BEGIN and NS_ASSUME_NONNULL_END are used in the framework. So it can support Swift better.

5. Use C functions as global functions

In ORKHelpers.h

void ORKEnableAutoLayoutForViews(NSArray *views);

In ORKHelpers.m

void ORKEnableAutoLayoutForViews(NSArray *views) {
[views enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    [(UIView *)obj setTranslatesAutoresizingMaskIntoConstraints:NO];

6. Add UIContentSizeCategoryDidChangeNotification to labels and text fields

- (void)init_ORKAnswerTextField {
  [[NSNotificationCenter defaultCenter] addObserver:self
   [self updateAppearance];

- (void)updateAppearance {
    self.font = [[self class] defaultFont];
    [self invalidateIntrinsicContentSize];

7. System design

ResearchKit is a pure framework which provides all basic elements, while AppCore is a layer built on top of ResearchKit. AppCore builds basic layouts and basic functions for an app, such as timer, status restore, core data, JSON serialization and network. So it’s easy to create a ResearchKit app based on AppCore. This is an interesting way to organize codes. AppCore is just like a template app. When creating a new app, we just add some customization to AppCore code base.

8. Other interesting things

1) NSDictionaryOfVariableBindings is a useful macro to combine keys and values together.

NSDictionary *dictionary = NSDictionaryOfVariableBindings(_countDownLabel, _startTimerButton);

2) __unused macro is used to avoid a warning.

- (UIImageView *)imageView {
 __unused UIView *view = [self view];
 return _activeStepView.imageView;

3) Using NSProcessInfo to check iOS version

if ([[NSProcessInfo processInfo] 
  isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){8, 2, 0}]) {
        [[NSNotificationCenter defaultCenter] addObserver:self

4) Play system sound

- (void)playSound {
 if (_alertSoundURL == nil) {
    _alertSoundURL = [NSURL URLWithString:@"/System/Library/Audio/UISounds/short_low_high.caf"];
    AudioServicesCreateSystemSoundID((__bridge CFURLRef)(_alertSoundURL), &_alertSound);

- (void)dealloc {

5) Use NS_DESIGNATED_INITIALIZER to trigger a warning

This article explains Xcode 6 Objective-C modernization tool.

- (instancetype)initWithIdentifier:(NSString *)identifier
              recorderSettings:(nullable NSDictionary *)recorderSettings
                          step:(nullable ORKStep *)step
               outputDirectory:(nullable NSURL *)outputDirectory 

6) Use Visibility Attributes to export classes, functions

#import <ResearchKit/ResearchKit_Private.h>


@interface ORKAudioStepViewController : ORKActiveStepViewController



#define ORK_CLASS_AVAILABLE __attribute__((visibility("default")))