Skip to content

Latest commit

 

History

History
445 lines (298 loc) · 16 KB

coding-style-guide.md

File metadata and controls

445 lines (298 loc) · 16 KB

ResearchKit Coding Style Guide

Always follow the Coding Guidelines for Cocoa for naming variables, properties, methods, classes and functions. Do not use any abbreviations except the ones mentioned in Acceptable Abbreviations and Acronyms.

1. Visual Style

1.1. Whitespace

Indent with Spaces

Use groups of 4 spaces (instead of tabs) to denote different indentation levels.

Xcode does this by default. Double-check your settings in: Preferences -> Text Editing -> Indentation.

Xcode indentation settings

Spaces in Declarations

Single spaces should be used in the cases detailed in this section. Don't use double spaces and don't add spaces elsewhere.


In @interface declarations, there should be one space between: the subclass name; the colon symbol; the superclass name; the adopted protocols section; and any adopted protocols.

// DO
@interface ORKProtocolAdoptingClass : NSObject <ORKProtocolA, ORKProtocolB>

// DON'T
@interface ORKProtocolAdoptingClass:NSObject <ORKProtocolA, ORKProtocolB>
@interface ORKProtocolAdoptingClass : NSObject<ORKProtocolA, ORKProtocolB>
@interface ORKProtocolAdoptingClass : NSObject <ORKProtocolA,ORKProtocolB>

In @property declarations, there should be one space between: the @property keyword; the property attributes section; any property attribute; the property type; and the pointer asterisk.

// DO
@property (nonatomic, weak, nullable) id<ORKDelegateProtocol> delegate;

// DON'T
@property(nonatomic, weak, nullable) id <ORKDelegateProtocol> delegate;
@property (nonatomic,weak,nullable) id <ORKDelegateProtocol> delegate;
@property (nonatomic, weak, nullable) id<ORKDelegateProtocol>delegate;

In method declarations, there should be one space between: the - or + character and the (returnType); and any argument type and its pointer asterisk.

// DO
- (void)doSomethingWithString:(NSString *)string number:(NSNumber *)number

// DON'T
-(void)doSomethingWithString:(NSString *)string number:(NSNumber *)number
- (void)doSomethingWithString:(NSString*)string number:(NSNumber *)number
- (void)doSomethingWithString:(NSString *)string  number:(NSNumber *)number

In variable declarations, add one space between the type and the pointer asterisk, and omit it between the asterisk and the variable name.

Similarly, omit the space between the asterisk and non-prefixed variable modifiers such as const. On the other hand, use one exactly one space before underscored annotations such as _Nullable or _Nonnull.

// DO
ORKTask *task = [ORKTask new];
static NSString *const ActivityUnknown = @"unknown";
CGFloat ORKWidthForSignatureView(UIWindow * _Nullable window);
- (BOOL)recreateFileWithError:(NSError * _Nullable *)error;
Spaces between Operators

Use spaces if the operator has two or more arguments:

// DO
steps += 2
calories = 5
calories == caloryGoal
goalAchieved && notReminded
flightsClimbed > 4
success ? YES : NO

Omit the space when the operator takes only one argument:

// DO
calories++
&error
!success
Spaces in array and dictionary literals

Add exactly one space before the first and after the last element in array and dictionary literals. Use exactly one space after each comma-separated element. Do not add any space between the key and the colon symbol on dictionary literals, but add exactly one space between the colon and the pointed object.

// DO
@[ @"Abdomen", @"Chest", @"Back" ];         // Array literal
@{ @"red": redImage, @"blue": blueImage };  // Dictionary literal

// DON'T
@[ @"Abdomen",@"Chest",@"Back" ];
@{ @"red" : redImage, @"blue":blueImage };

1.2. Brackets

Opening brackets should be on the same line as the statement they refer to. Closing brackets should be on their own line, except when followed by else.

// DO
- (void)doSomethingWithString:(NSString *)string {
    if (condition) {
        ...
    } else {
        ...
    }
}

// DON'T
- (void)doSomethingWithString:(NSString *)string
{
    if (condition)
    {
        ...
    }
    else
    {
        ...
    }
}

Always use brackets even when the conditional code is only one statement.

// DO
if (condition) {
    return;
}

// DON'T
if (condition)
    return;

1.3. Line Wrapping

Hard-wrap lines that exceed 140 characters. You can configure the column guide on Xcode: Preferences -> Text Editing -> Page guide at column: 140.

When hard-wrapping method calls, give each parameter its own line. Align each parameter using the colon before the parameter (Xcode does this for you by default).

// DO
- (void)doSomethingWithFoo:(Foo *)foo
                       bar:(Bar *)bar
                  interval:(NSTimeInterval)interval {
    ...

Method invocations should be formatted much like method declarations. Invocations should have all arguments on one line or have one argument per line, with colons aligned.

// DO
[myObject doSomethingWithFoo:foo bar:bar interval:interval];

[myObject doSomethingWithFoo:foo
                         bar:bar
                    interval:interval];

// DON'T
[myObject doSomethingWithFoo:foo bar:bar
                    interval:interval];

[myObject doSomethingWithFoo:foo
          bar:bar
          interval:interval];

1.4. Appledoc Header Comments

ResearchKit uses appledoc to generate its documentation from specially marked comments in header files.

Follow these guidelines when writing appledoc comments:

  • Multiline appledoc comments start with the /** character sequence.
  • When documenting methods, use the @param and @return keywords to detail the parameters and return value.
  • When you name classes or methods, enclose them in backticks so appledoc creates a reference ( `ORKStep` is ...).
  • For multiline code examples, surround them with a triple backtick (```) for cross references within the code block not to be automatically generated.
  • Don't use abbreviations such as e.g. or i.e. in the documentation.
  • Hard-wrap comment lines at column 100.
  • Read the latest ResearchKit documentation for inspiration and try to follow the same literary style.

1.5. Newlines

Use exactly two empty lines to separate:

  • The /* Copyright header */ and the #import section.
  • The #import section and the class @interface or @implementation line (or its associated forward declarations or appledoc comment).
  • Different @interface or @implementation sections within the same file.

Do not use two or more empty lines in any other cases.

Use exactly one empty line to separate:

  • The last forward declaration and the @interface declaration.
  • The @interface declaration and the first method or property declaration.
  • The last method or property declaration and the @end keyword.
  • The @implementation line and the first method definition.
  • The closing bracket of the last method definition and the @end keyword.
  • The @param section and the @return line in an appledoc comment.

Header and implementation files must have one, and only one, trailing empty line.

Do not use empty lines to separate:

  • Forward declarations from other contiguous forward declarations.
  • An appledoc comment from its related class or method.
  • Contiguous @param lines within the same appledoc comment.
  • Last statement in a method definition and its closing bracket.

Note that forward declarations should appear before any class appledoc comment.

You can optionally use one (and only one) blank like to separate:

  • Groups of related #import statements.
  • Groups of related statements in a single method implementation.

2. Code Style

2.1. Variable Declarations

Declare one variable per line even if they have the same type. In general it's a good idea to initialize primitive type variables with a reasonable value.

// DO
int floatVariable = -1;
double doubleVariable = 0.0;
int *cPointerVariable = NULL;
CGContextRef context = NULL;

Strong, weak, and autoreleasing stack variables are implicitly initialized with nil so you can either explicitly initialize them (with nil or any valid object) for visual homogeneity, or skip initializing them altogether.

// DO
id object = nil;
NSString *string = nil;
UIView *view = nil;
// ALSO OK
id object;
NSString *string;
UIView *view;

When declaring pointers, there should be a space between the asterisk and the variable type, but none between the asterisk and the variable.

// DON'T
int* variablePointer2;
int* variablePointer, variable;
UIView* view;

2.2. Forward Declarations

Use one line for each forward declarations:

// DO
@protocol ORKProtocol;
@protocol ORKAnotherProtocol;
@class ORKClass;
@class ORKAnotherClass;
@class ORKYetAnotherClass;

// DON'T
@protocol ORKProtocol, ORKAnotherProtocol;
@class ORKClass, ORKAnotherClass, ORKYetAnotherClass;

2.3. Constant Declarations

If a constant is only used inside one method, declare it locally to that method. If a constant is used in several methods of a single class, declare it as a static constant in the class implementation file. If a constant is used from several files, declare it as an extern constant and prefix its name with a suitable ORK* prefix.

Static or global constant names should start in uppercase. Constants should never start with the k prefix (that naming convention is deprecated). These rules also apply to enum value names.

// Method-local constant
- (void)animateView {
    const CGFloat animationDuration = 0.2;
    ...
}


// Class-local constant
static const CGFloat DefaultLineWidth = 10.0;
- (void)init {
    if (self = [super init]) {
        _lineWidth = DefaultLineWidth
        ...
    }
    return self;
}

- (void)resetView {
    _lineWidth = DefaultLineWidth
    ...
}


// Global constant

// ORKSkin.h
ORK_EXTERN NSString *const ORKToolBarTintColorKey;
ORK_EXTERN const CGFloat ORKScreenMetricMaxDimension;

// ORKSkin.m
NSString *const ORKBackgroundColorKey = @"ORKBackgroundColorKey";
const CGFloat ORKScreenMetricMaxDimension = 10000.0;


// Global enum
typedef NS_ENUM(NSInteger, ORKQuestionType) {
    ORKQuestionTypeNone,
    ORKQuestionTypeScale,
    ...
} ORK_ENUM_AVAILABLE;

2.4. Dot Notation

Dot notation (object.property) is a syntax for using properties in a convenient and compact way. Accessing or setting a property through dot notation is completely equivalent to calling the property accessor methods:

- (PropertyType *)property
- (void)setProperty:(PropertyType *)property

// DO
NSString *oldName = user.name;              // Equivalent to 'NSString *oldName = [user name]'
user.name = @"John Appleseed";              // Equivalent to '[user setName:@"John Appleseed"]'

Dot notation should be used when accessing proper properties, but should be avoided when invoking regular methods. Use the syntax corresponding to the official documentation or relevant header declaration.

// DO
CGRect viewFrame = view.frame;                                      // Declared as a property
NSUInteger numberOfItems = array.count;                             // Declared as a property since iOS 8
NSUInteger stringLength = string.length;                            // Declared as a property since iOS 8
[autoreleasePool drain];                                            // A method
NSArray<NSLayoutConstraint *> *constraints = [view constraints];    // A method

// DON'T
CGRect viewFrame = [view frame];                                    // 'frame' is not declared as a method
NSUInteger numberOfItems = [array count];                           // 'count' is no longer declared as a method
NSUInteger stringLength = [string length];                          // 'length' is no longer declared as a method
autoreleasePool.drain;                                              // Not a property
NSArray<NSLayoutConstraint *> *constraints = view.constraints;      // Not a property

2.5. Nullability Annotations

Always include nullability annotations in header files.

Generally, it's a good idea to make the entirety of headers as audited for nullability, which makes any simple pointer type to be assumed as nonnull by the compiler. You do this by wrapping the whole file with the NS_ASSUME_NONNULL_BEGIN and NS_ASSUME_NONNULL_END macros. You can then opt any property or argument declaration that can take nil values out by annotating it as nullable.

// DO

NS_ASSUME_NONNULL_BEGIN

- (instancetype)initWithStep:(nullable ORKStep *)step;
@property (nonatomic, copy, nullable) NSString *aNullableProperty;

NS_ASSUME_NONNULL_END

When annotating function or blocks, use the _Nullable keyword (available since Xcode 7), instead of the legacy __nullable.

// DO
CGFloat ORKWidthForSignatureView(UIWindow * _Nullable window);

// DON'T
CGFloat ORKWidthForSignatureView(UIWindow * __nullable window);

Do not add nullability annotations to implementation files.

See Section 3 for a nullability-annotated Header File Example.

2.6. Lightweight Generics

Always use lightweight generic parametrization when declaring NSArray, NSSet and NSDictionary types. This tells the compiler which kind of objects these Foundation collection classes will contain. It improves type-safety and interoperability with Swift.

Use the same whitespace rules as when declaring protocol conformance, but omit the space between the type and the opening bracket:

// DO
@property NSArray<ORKStep *> *steps;
@property NSDictionary<NSString *, ORKStepNavigationRule *> *stepNavigationRules;
@property NSSet<ORKTask *> *tasks;

// DON'T
@property NSArray <ORKStep *>*steps;
@property NSDictionary<NSString *,ORKStepNavigationRule *> *stepNavigationRules;
@property NSSet<ORKTask*> *tasks;

3. Header File Example

/*
 Copyright (c) 2015, John Appleseed. All rights reserved.
 ...
 */


#import <Foundation/Foundation.h>
#import <ResearchKit/ORKDefines.h>


NS_ASSUME_NONNULL_BEGIN

@protocol ORKTask;
@class ORKStep;

/**
 appledoc class comment.
 */
ORK_CLASS_AVAILABLE
@interface ORKMyClass : NSObject <NSSecureCoding, NSCopying>

/**
 appledoc method comment.

 @param parameterA   The first parameter.
 @param parameterB   The second parameter.

 @return A new MyClass.
 */
- (instancetype)initWithParameterA:(NSString *)parameterA parameterB:(NSString *)parameterB NS_DESIGNATED_INITIALIZER;

/**
 appledoc property comment.
 */
@property (nonatomic, copy, readonly) NSString *aProperty;

/**
 appledoc property comment.
 */
@property (nonatomic, copy, nullable) NSString *aNullableProperty;

@end


/**
 appledoc class comment.
 */
ORK_CLASS_AVAILABLE
@interface ORKMyOtherClass : ORKMyClass

@end

NS_ASSUME_NONNULL_END