Rightpoint / Raizlabs-Cocoa-Style

The Raizlabs iOS Style Guide
MIT License
32 stars 2 forks source link

Raizlabs Objective-C Style Guide

This guide outlines the coding conventions and best practices for the Objective-C developers at Raizlabs.

Table of Contents generated with DocToc

Dot Syntax

Use dot notation for all property access and manipulation. Never access _ivars directly when a property has been declared, except where required:

Preferred:

self.foo = 4;
int bar = self.foo;

Not:

[self setFoo:4];
int bar = [self foo];
_bar = 4;

For clarity, you may use bracket notation for overridden setters/getters:

- (void)setFoo:(int)foo
{
    // some extra code goes here

    _foo = foo;
}

- (int)foo
{
    // some extra code goes here

    return _foo;
}

- (void)aMethod
{
    [self setFoo:4];
    int test = [self foo];
}

Never use dot notation on a non-idempotent property or method. For example, count isn't actually a property on NSArray; the compiler just infers because there's a method called count. However, it is an idempotent method, so it is safe to use dot-notation:

NSUInteger foo = myArray.count;

Avoid non-idempotent setters

Bad:

- (void)setFoo:(id)foo
{
    _foo = foo;
    _lastTimeFooWasSet = [NSDate date];
   [self.tableView reloadData];
}

Better:

- (void)updateFoo:(id)foo refresh:(BOOL)refresh
{
    self.foo = foo;
    if ( refresh ) {
        _lastTimeFooWasSet = [NSDate date];
        [self.tableView reloadData];
    }
}

This is not to say that you shouln't override setters; you just need to be careful that the side effects are obvious, and with low potential danger.

Whitespace

Newlines

- (void)viewDidLoad
{
    // set up foo object
    UIFoo *foo = [[UIFoo alloc] init];
    foo.property = value;

    // set up bar object
    UIBar *bar = [[UIBar alloc] initWithThing:foo];
}

Indentation

Naming

Variables

Variables always use camel case:

likeThis;

Variables of type Class start with a capital letter. Note that a variable of type Class should use Nil, not nil, to express emptiness:

Class SomeClassVariable = Nil;
SomeClassVariable = [MyClass class];

Properties

Never give properties generic names. Instead, prefix the variable name with a descriptor such as, but not limited to, the class name.

Preferred:

@property (strong, nonatomic) UICollectionView *myClassCollectionView;

Not:

@property (strong, nonatomic) UICollectionView *collectionView;

Instance Variables

Instance variables begin with an underscore and rename the variable to _propertyName.

@synthesize ivarName = _ivarName;

However, the use of explicitly declared or synthesized instance variables is discouraged except where required.

Constants

Constants are camel-case, and should use the following format:

// [k][class prefix][class name][constant name]
static const NSInteger kRZMyClassSomeErrorCode = -1;

See also: Cocoa naming conventions for variables and types.

Variables

Asterisks indicating pointers belong with the variable, except in the case of constants:

Preferred:

NSString *text;

Not:

NSString* text;
NSString * text

Always use @property-declared variables instead of instance variables (except for where you have to).

Preferred:

@interface RWTTutorial : NSObject

@property (copy, nonatomic) NSString *tutorialName;

@end

Not:

@interface RWTTutorial : NSObject
{
    NSString *tutorialName;
}

Instance variables are required in the following case:

Subclasses don't have visibility into auto-synthesized properties defined on ancestor classes. Redefining the property requires duplicating the property semantics, which might change. Declaring the instance variable is actually correct in this case. If you want to hide it, mark it @private or use a private header.

Properties

Preferred:

@property (strong, nonatomic) NSObject *someObject;

Not:

@property (nonatomic, strong) NSObject *someObject;
@property (strong, nonatomic) NSObject* someObject;
@property (strong, nonatomic) NSObject * someObject;
@property(strong, nonatomic) NSObject *someObject;
@property(strong, nonatomic)NSObject *someObject;

Conditionals

Preferred:

if (expression) {
    // if code
}
else if (other expression) {
    // else if code
}
else {
    // else code
}

Not:

if ( expression )
{ // shouldn't be on next line
    // if code
} else if ( expression ) // else should start on new line
{
    // else if code
}
else
    // else code // NEVER forgo braces

Mathematical operators

Unary operators stick to the number they modify:

int x = -10;
NSNumber *y = @(x * -3);

Use spaces between all binary and ternary mathematical operators. Fully parenthesize mathematical expressions and any logical expression with 1+ operator:

int x = ((1 + 1) / 1);

Ternary conditional tests must be enclosed in parens:

CGFloat result = (x > 2) ? someValue : otherValue;

Non-conditionals do not need parens:

CGFloat result = self.isLoading ? someValue : otherValue;

No nesting of ternary expressions.

BOOL dontDoThis = self.otherBOOL ? ((self.dont) ? self.do : self.this) : self.please;

CGFloat

Preferred:

CGSizeMake(2.0f, 2.0f);

Switch statements

switch ( expression ) {
    case 1:
        // code
        break;
    case 2: {
        // code
        // code
        break;
    }

    default:
        // default code
        break;
}

We strongly encourage you to put fallthroughs at the end of the statement:

switch ( expression ) {
    case 1: {
        // case 1 code
        break;
    }
    case 2: // fall-through
    case 3:
        // code executed for values 2 and 3
        break;
    default:
        // default code
        break;
}

Do not use a default if there isn't any handling for the default case:

Preferred:

switch ( expression ) {
    case 1: {
        // case 1 code
        break;
    }
    default: {
        // default code
        // more default code
        break;
    }
}

Not:

switch ( expression ) {
    case 1: {
        // case 1 code
        break;
    }
    default: // nothing here, no need for default!
        break;
}

Comments

...preceding code...
...preceding code...

// Explanatory comment
...code being explained...

...other code unrelated to comment...
...other code unrelated to comment...

Method Signatures

Preferred:

- (NSObject *)methodNameWithParam:(NSObject *)param otherParam:(NSObject *)otherParam;

Not:

- (NSObject *)methodNameWithParam:(NSObject *)param andOtherParam:(NSObject *)otherParam;
-(void)setT:(NSString *)text i:(UIImage *)image;
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag; // Never do this
- (id)taggedView:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width andHeight:(CGFloat)height;
- (instancetype)initWith:(int)width and:(int)height; // Never do this.

Colon-align long method signatures (3 lines or more) (unless there is a block parameter!):

- (id)initWithTableView:(UITableView *)tableView
         collectionList:(id<RZCollectionList>)collectionList
               delegate:(id<RZCollectionListTableViewDataSourceDelegate>)delegate

When the first parameter is not as long as the latter ones, left-align all lines. (This is what Xcode’s default auto-format behavior, so it runs the least risk of being changed by mistake later.)

- (void)align:(BOOL)this
veryVeryVeryVeryLong:(BOOL)method
signatureThatIsStillNotAsLongAsManyTotallyLegitimateCocoa:(BOOL)methods

See also: Cocoa naming conventions for methods.

Return statements

Using only one return at the end of a method end is extremely preferred. Instead of bailing early, modify a return variable within the method:

Preferred:

- (int)foo
{
    int ret = 0;

    // code to modify "ret"
    switch ( self.bar ) {
        case 0: {
            ret = 12;
            break;
        }
        case 1: {
            ret = 42;
            break;
        }
        default:
            // handle default case
            ret = 11;
            break;
        }
    }

    return ret;
}

Not:

- (int)foo
{
    switch ( self.bar ) {
        case 0:
            return 12;
        case 1:
            return 42;
        default:
            return 0;
    }
}

Early returns are permitted only at the beginning of a method, when you need to bail quickly:

- (id)doSomething
{
    if ( doingSomething ) {
        return nil;
    }

    // do awesome things
    return awesomeThing;
}

Protocols

Preferred:

@protocol RZSomeClassDelegate;

@interface RZSomeClass : NSObject

@property (weak, nonatomic) id <RZSomeClassDelegate> delegate;

@end

@protocol RZSomeClassDelegate <NSObject>

@required

// required methods

@optional

// optional methods

@end

Not:

@protocol RZSomeClassDelegate <NSObject>

@required

// required methods

@optional

// optional methods

@end

@interface RZSomeClass : NSObject

@property (weak, nonatomic) id <RZSomeClassDelegate> delegate;

@end

Blocks

If you can do it with with a completion block, don't use a protocol.

Naming

// some .h file
typedef void (^RZCompletionBlock)(BOOL succeeded, NSError *error);

Do not use a newline before the opening curly brace.

Preferred:

[UIView animateWithDuration:0.2 animations:^{
    // animation code
} completion:nil];

Not:

[UIView animateWithDuration:0.2
                animations:^
                {
                    // animation code
                } completion:nil];

Spacing

When the block takes parameters, put a space between the closing parenthesis and the the opening curly brace:

[self.thing enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    // code
}];

When the block takes no parameters, do not put a space between the ^ and the {:

[UIView animateWithDuration:9.41 animations:^{
    // code
}];

Block Parameters

When you don’t want to pass a block to a parameter, use nil, not NULL. This is because blocks are Objective-C objects, and because you may want to send messages such as -copy to them even if they are nil.

[self presentViewController:aViewController
                   animated:YES
                 completion:nil];

Constants

User-Facing Strings

Always use NSLocalizedString for User-facing strings.

Preferred:

#define kRZClassNameStringConstant NSLocalizedString(@"Hello World", @"A hello world string")

Other string constants (non-user-facing)

Preferred:

static NSString* const kRZLoginUsername = @"com.raizlabs.login.username";

If you want to make it public, put this in the .h file:

OBJC_EXTERN NSString* const kRZLoginUsername;

And in the .m:

NSString* const kRZLoginUsername = @"com.raizlabs.login.username";

Magic Strings

- (void)someMethod
{
    NSString *message = @"Error, you broke the app!";
}

Numbers

Preferred:

// .m file
const int intName = 4;

// .h file
OBJC_EXTERN const int intName;

Always make internal, private constants static.

Preferred:

// .h file
// This space intentionally left blank

// .m file
static const CGFloat buttonHeight = 44.0f;

Magic numbers are allowed for numbers that can't change (like dividing by 2 to get the center of something)

Structs

If you need a constant struct, use the designated intializer syntax:

static const CGSize kRZTestViewControllerShadowOffset = { .width = 0.0f, .height = 3.0f };

Enumerations

Preferred:

typedef NS_ENUM(NSInteger, RZFoo) {
    RZFooBlue = 0,
    RZFooRed,
    RZFooGreen
};

Not:

typedef enum
{
    RZFooBlue,
    RZFooRed
    RZGreen
}RZFoo;

enum
{
    RZFooBlue,
    RZFooRed
    RZGreen
};

Example:

typedef NS_ENUM(NSInteger, RZFoo) {
    RZFooUnknown = -1,
    RZFooBlue,
    RZFooRed
};

If you want to accept mutilple sub-values, use a bitmask

Add an "All"-suffixed subtype when applicable

Example:

typedef NS_OPTIONS(NSUInteger, RZFoo) {
    RZFooUnknown,
    RZFooBlue,
    RZFooRed,
    RZFooGreen,
    RZFooAll
};

Initializers

Preferred:

- (instancetype)init;

Not:

- (id)init;

Singletons

Singleton objects should use a thread-safe GCD pattern for creating their shared instance:

+ (instancetype)sharedInstance
{
    static id sharedInstance = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[[self class] alloc] init];
    });

   return sharedInstance;
}

Error handling

Always handle errors and return values

Preferred:

- (void)doSomething
{
    [someObject doSomethingWithCompletion:^(BOOL success, NSError *error) {
        if ( success ) {
            // Handle success
        }
        else if ( error ) {
            // Handle error with an error object returned
        }
        else {
            // Handle error without an error object
        }
    }];
}

Not:

- (void)doSomething
{
    [someObject doSomethingWithCompletion:^(BOOL success, NSError *error) {
        if ( error ) {
            // Handle error
        }
        else {
            // Assume success
        }
    }];
}

Name error pointers something more specific than error when there are nested/multiple calls that return errors in the scope of a method

For example:

// Ignoring above advice about checking return value
// for the sake of a concise example.
- (void)doColor
{
    [self blueWithError:^(NSError *blueError) {
            if ( blueError ) {
                // handle blueError
            }

            [self redWithError:^(NSError *redError) {
                if ( redError ) {
                    // handle redError
                }
            }];
        }];

    [self yellowWithError:^(NSError *yellowError) {
        if ( yellowError ) {
            // handle yellowError
        }
    }];
}

Out Errors (NSError **)

- (BOOL)doActionReturningError:(NSError **)outError;
- (BOOL)doActionWithThing:(NSObject *)thing error:(NSError **)outError;

Literals

Use Objective-C literals wherever possible.

Preferred:

NSArray *foo = @[object, object, object];

Not:

NSArray *array = [[NSArray alloc] initWithObjects:@"foo", @"bar", nil];

NSArray *anotherArray = [NSArray arrayWithObjects:@"foo", @"bar", nil];

Rule of three

Protocol Conformation

If a class conforms to three or more protocols, separate each declaration with line breaks:

Preferred: (who doesn't love alphabetizing?)

@interface RZViewController : UIViewController
<RZBeerDelegate,
RZInfiniteChipotleDelegate,
RZKitchenDelegate,
RZLunchFinderDelegate>

Not:

@interface RZViewController : UIViewController <RZKitchenDelegate, RZInfiniteChipotleDelegate, RZLunchFinderDelegate, RZBeerDelegate>

Method calls/signatures

If a method has 3 or more parameters, separate the paramters with line breaks:

Preferred:

- (void)doSomethingWithArray:(NSArray *)array
                      string:(NSString *)string
                        bool:(BOOL)bool
{
    [super doSomethingWithArray:array
                         string:string
                           bool:bool];
}

Not:

- (void)doSomethingWithArray:(NSArray *)array string:(NSString *)string bool:(BOOL)bool
{
    [super doSomethingWithArray:array string:string number:number bool:bool];
}

Don't align method calls that take non-nil block parameters

[super doSomethingWithArray:array string:string bool:bool completion:^{
    // block code
}];

Unnecessary code

Advances in Clang and Objective-C have made certain conventions obsolete. 99% of the time, we should no longer use the following:

File Organization

Objective-C files should generally be organized in the following order. See the included RZSampleViewController.h and RZSampleViewController.m to see these rules in practice.

Header Files (.h)

When to use a _Private.h file

When you have a base class of which you have multiple subclasses. For example:

What to do:

  1. Create a new class extension file
  2. Name it YourClass_Private.h
  3. Put all your shared interface elements in that private interface
  4. Import that interface file in your subclasses' implementation files

An example structure:

Implementation Files (.m)