This applies to new code, and as you're working with code in TouchDB. For now, existing TouchDB code is given a pass.
Read Apple's Coding Guidelines for Cocoa. The following is mostly about style; Apple's guidelines cover the essentials of naming.
Code style for CDTDatastore is defined with a clang format file (.clang-format) in the root of the project. All code should be formatted using the clang-format tool.
There are two options for using clang-format
, aside from directly invoking on the command line:
- Get
git
to enforce style on commit, for just changed portions of files (strongly recommended). This uses a commit hook. - Install
clang-format
into Xcode, and format sections as you change them.
Follow the instructions here.
Clang-format can be installed into Xcode using the ClangFormat-Xcode plug-in. The easiest way to do this is via Alcatraz. You can also install the plugin from source using the instractions at ClangFormat-Xcode.
You can set up ClangFormat-Xcode
to format the whole file on every save. Unfortunately,
many of the files in the codebase are not yet formatted, so we suggest not setting this
up as if you do you'll be picking apart hundreds of lines of whitespace changes from
your actual changes to separate their commits. Instead, run the formatter over just
the code you change.
You can set up a hotkey for formatting selected text as follows:
- Open System Preferences > Keyboard > Shortcuts > App Shortcuts. Click
+
. - Set the application to be Xcode.
- Set the menu title to "Format Selected Text".
- Set your shortcut to
ctrl-i
.
Your editor can be set up to enforce some of these.
4 spaces. No tabs. Set up your editor appropriately.
100 characters. Use Preferences > Text Editing > Page guide at column: 100 in Xcode.
Code review in GitHub is easier if one doesn't need to scroll horizontally.
- Blank lines should both precede and follow
@
entities like@interface
,@implementation
,@end
. - Group code into "paragraphs" within methods using a single blank line.
- One blank line between methods.
- Group imports with blank lines.
Commenting can be useful. Obviously it's more important to have readable code; comments are for why not how.
- For comments in methods, use
//
comments. - For doc comments, use appledoc
/** .... */
. If there is more than one line to the comment, wrap the first line too. - To comment out swathes of code, use
//
comments. Preferably at the start of lines to match Xcode's behaviour.
From the NYT guidelines.
When methods return an error parameter by reference, switch on the returned value, not the error variable.
For example:
NSError *error;
if (![self trySomethingWithError:&error]) {
// Handle Error
}
Not:
NSError *error;
[self trySomethingWithError:&error];
if (error) {
// Handle Error
}
Some of Apple’s APIs write garbage values to the error parameter (if non-NULL) in successful cases, so switching on the error can cause false negatives (and subsequently crash).
Any and all property accesses. Nowhere else.
Spaces between the keyword and the braces; braces on the same line.
if (something) {
// blah...
} else if (somethingElse) {
// blah...
} else {
// blah...
}
- Put common cases first.
- Prefer positive conditions (
if (something) {...} else {...}
overif (!something) {...} else {...}
). It's easy to miss a!
when reading a complex condition. Unless the negated condition is much more likely to happen.
A space after the -
or +
. A space after class names when typing parameters. The brace
on a new line.
- (NSString *)produceRandomStringWithLength:(NSInteger)length
{
...
}
Similar rules apply for spacing. When wrapping Cocoa messages, align by colon.
[myObject doFooWith:arg1 name:arg2 error:arg3];
[myObject doFooWith:arg1
name:arg2
error:arg3];
If wrapping, wrap all keywords onto a new line.
Always start a new line for a block, and indent four spaces from the left margin.
// Put the block's code on a new line, indented four spaces, with the
// closing brace aligned with the first character of the line on which
// block was declared.
[operation setCompletionBlock:^{
[self.delegate newDataAvailable];
}];
// Using a block with a C API follows the same alignment and spacing
// rules as with Objective-C.
dispatch_async(_fileIOQueue, ^{
NSString* path = [self sessionFilePath];
if (path) {
// ...
}
});
Always prefer literals to methods. That is, for example, prefer @{ var1, var2 }
over [NSArray arrayWithObjects:var1, var2, nil]
.
When wrapping literals, prefer 4-space indents:
NSArray* array = @[
@"This",
@"is",
@"an",
@"array"
];
But it's okay to use Xcode's odd default:
NSDictionary* discouraged = @{ AKey : @"a",
BLongerKey : @"b" };
Remember Apple now has macros to help with this:
# Enums:
typedef NS_ENUM(NSInteger, NYTAdRequestState) {
NYTAdRequestStateInactive,
NYTAdRequestStateLoading
};
# Bitmasks:
typedef NS_OPTIONS(NSUInteger, NYTAdCategory) {
NYTAdCategoryAutos = 1 << 0,
NYTAdCategoryJobs = 1 << 1,
NYTAdCategoryRealState = 1 << 2,
NYTAdCategoryTechnology = 1 << 3
};
4-spaces, as usual.
Always prefer properties to instance variables.
Always access properties with dot-notation. Apart from in initialisers, and obviously their own getter and setter methods.
Google's doc reminds us, "Instance subclasses may be in an inconsistent state during init and dealloc method execution, so code in those methods should avoid invoking accessors."
For more information on using Accessor Methods in Initializer Methods and dealloc, see here.