Objective-C Coding Style

My Objective-C style guide.

A combination of the rules that I use when working on an Objective-C project.

Introduction

The reason I made this style guide was so that I could keep the code in my projects similar and allow for easier movement between projects.

One of my key aims is to create projects that are easy to understand from the developers point-of-view so I often favour verboseness when it ensures that the true meaning of what we are attempting is more clearly expressed.

Table of Contents

Language

US English should be used.

Preferred:

UIColor *myColor = [UIColor whiteColor];  

Not Preferred:

UIColor *myColour = [UIColor whiteColor];  

Code Organization

Use #pragma mark - to categorize methods in functional groupings and protocol/delegate implementations following this general structure.

The descriptor of the pragma mark should be capitalised and use camel casing.

If needed, you can create sub-groups under a single group by #pragma mark

Preferred:

#pragma mark - ButtonActions

#pragma mark NavigationButtonActions

Not Preferred:

#pragma mark - butttonActions
#pragma mark - buttton actions

#pragma mark - NavigationButtonActions

Braces

Braces should appear on a newline.

Preferred:

if (user.isHappy)  
{
    //Do something
}
else  
{
    //Do something else
}

Not Preferred:

if (user.isHappy) {  
  //Do something
} else {
  //Do something else
}

This is especially true for blocks

Preferred:

[UIView animateWithDuration:1.0 animations:^
{
  // something
}
                 completion:^(BOOL finished)
{
  // something
}];

Not Preferred:

[UIView animateWithDuration:1.0 animations:^{
  // something
} completion:^(BOOL finished) {
  // something
}];

Naming

Apple naming conventions should be adhered to wherever possible, especially those related to memory management rules

Long, descriptive method and variable names are good, remember you are developing this app to be maintained.

Preferred:

UIButton *settingsButton;  

Not Preferred:

UIButton *setBut;  
UIButton *buttonSettings;  

Types

NSInteger and NSUInteger should be used instead of int, long, etc per Apple's best practices and 64-bit safety. CGFloat is preferred over float for the same reasons.

All Apple types should be used over primitive ones. For example, if you are working with time intervals, use NSTimeInterval instead of double even though it is synonymous.

Images

Images should be used within xcassets resource bundles.

The naming of each individual should follow the same naming conventions as the naming of methods and variables with the exception that rather than using camel casing, a "-" should be used to separate different words

Preferred:

background-request-empty  

Not Preferred:

backgroundRequestEmpty  

Prefixing

A three letter prefix should always be used for all class names, category names, category methods and externs.

Preferred:

WBSLoadingView //class  
UIView+UCCFrame //category  
ucc_frameMethod //category method  
WBSLoggedNotification //extern  

Not Preferred:

LoadingView //class  
UIView+Frame //category  
frameMethod //category name  
LoggedNotification //extern  

Parentheses

There should be no spaces between parentheses and their contents.

Preferred:

if(count == 1)  
{
}

Not Preferred:

if( count == 1 )  
{
}

Properties

Properties should be camel-case with the leading word being lowercase. Use auto-synthesis for properties rather than manual @synthesize statements unless you have good reason.

Preferred:

@property (nonatomic, strong) NSString *postName;

Not Preferred:

@property (nonatomic, strong) NSString *PostName;

Attributes on a property should always be explicitly listed.

Properties with mutable counterparts (e.g. NSString) should prefer copy instead of strong.

Preferred:

@property (nonatomic, copy) NSString *postName;

Not Preferred:

@property (nonatomic, strong) NSString *postName;

The ordering of a property declaration should follow:

Preferred:

@property (nonatomic, assign, getter=isPostRead) BOOL postRead;
@property (nonatomic, strong, readonly) BOOL postName;
@property (nonatomic, strong, readwrite) BOOL postStatus;

Not Preferred:

@property (assign, getter=isPostRead, nonatomic) BOOL *postRead;
@property (nonatomic, readonly, strong) BOOL postName;
@property (nonatomic, strong, readwrite) BOOL postStatus;

If you need to use @synthesize then ensure that you use an underscore in front of the instance variable declaration.

@synthesize declarations should appear directly below the @implementation statement.

Preferred:

@synthesize post = _post;

Not Preferred:

@synthesize post = iPost;

Properties in the header should always be immutable.

If the property should only be initialised in the container class then it should be exposed as readonly and overridden in the class extension to allow read write access.

Instance variables

Instance variables should only ever be accessed within a custom getter/setter of the property or the init method of the class. When outside of these scopes the instance variable's property should be used.

Preferred:

- (void)jamFound
{
    self.jamSearchStatus = @"Jam found!";
}

Not Preferred:

- (void)jamFound
{
    _jamSearchStatus = @"Jam found!";
}

Methods

In method signatures, there should be a space after the method type (-/+ symbol). There should be a space between the method segments (matching Apple's style). Always include a keyword and be descriptive with the word before the argument which describes the argument.

The usage of the word "and" is reserved. It should not be used for multiple parameters as illustrated in the initWithWidth:height: example below.

Preferred:

- (void)setExampleText:(NSString *)text image:(UIImage *)image;
- (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag;
- (id)viewWithTag:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;

Not Preferred:

-(void)setT:(NSString *)text i:(UIImage *)image;
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
- (id)taggedView:(NSInteger)tag;
- (instancetype)initWithWidth: (CGFloat)width andHeight: (CGFloat)height;
- (instancetype)initWith:(int) width and:(int) height;

All methods be declared either in header of the class or the class extension.

Each method in the implementation of the class should be separated by a one new line space between other methods, pragma marks, etc.

Unless in exceptional circumstances a method should only have one return statement.

When calling a method that accepts parameters, there should be no space between the ":" and the parameter passed in.

Preferred:

[self sendAction:action];

Not Preferred:

[self sendAction: action];

Variables

Variables should be named as descriptively as possible. Single letter variable names should be avoided.

Asterisks indicating pointers belong with the variable, e.g., NSString *text not NSString* text or NSString * text, except in the case of constants.

Preferred:

for(NSUInteger index = 0, index < [self.items count]; index++)  
{
}

Not Preferred:

for(NSUInteger i = 0, i < [self.items count]; i++)  
{
}

There should be no spaces between parentheses and their contents.

Protocols

Protocols follow the same naming conventions as classes, with the following exceptions:

Protocols which reference a behavior type should end with a gerund (-ing).
Protocols which describe a set of actions should describe the functional property of these collective actions.
Protocols which are a delegate should end with the word Delegate.

@protocol WBSScrolling; // Gerund; behavior type is "this object scrolls"
@protocol WBSFocusable; // Action set; describes actions related to "focusing" and "WBSFocusing" seems inappropriate ("this object focuses" vs. "this object performs actions related to focusing")
@protocol TiScrollViewDelegate; // Delegate

When relating to only one class, protocols should be defined above the implementation of the class, in the class's header.

When used by more than one class, the protocol should be split out and defined within its own class.

Any methods that are optional within a protocol should be marked as such using the @optional keyword. Any class that implements a protocol must implement all non-optional methods.

When calling an optional method on a protocol's implementation a check should always be performed to ensure that the method is implemented

if([self.delegate respondsToSelector:@selector(allHeaderFields)])  
{
}

When a method is non-optional the above check should not be performed, it is preferable for the app to crash and immediately inform the developer of their error.

If a method is moved between optional and non-optional, it is the developer who made that decision responsibly to ensure that all classes that implement this protocol are updated.

Dot-Notation Syntax

Dot syntax is purely a convenient wrapper around accessor method calls. When you use dot syntax, the property is still accessed or changed using getter and setter methods. Read more here

Dot-notation should always be used for accessing and mutating properties, as it makes code more concise. Bracket notation is preferred in all other instances.

Preferred:

NSInteger arrayCount = [self.array count];  
view.backgroundColor = [UIColor orangeColor];  
[UIApplication sharedApplication].delegate;

Not Preferred:

NSInteger arrayCount = self.array.count;  
[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;  

Literals

NSString, NSDictionary, NSArray, and NSNumber literals should be used whenever creating immutable instances of those objects. Pay special care that nil values can not be passed into NSArray and NSDictionary literals, as this will cause a crash.

The items for a NSDictionary should each be on a new line.

Preferred:

NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];  
NSDictionary *productManagers = @{  
    @"iPhone": @"Kate",
    @"iPad": @"Kamal",
    @"Mobile Web": @"Bill"
  };
NSNumber *shouldUseLiterals = @YES;  
NSNumber *buildingStreetNumber = @10018;  

Not Preferred:

NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];  
NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil];  
NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];  
NSNumber *buildingStreetNumber = [NSNumber numberWithInteger:10018];  

Constants

Constants are preferred over in-line string literals or numbers, as they allow for easy reproduction of commonly used variables and can be quickly changed without the need for find and replace. Constants should be declared as static constants and not #defines unless explicitly being used as a macro.
This behaviour applies on literals or numbers which are used more than once in a class, single occurrences are best used in line.

Preferred:

static NSString * const kKeyForImage = @"imageKey";

static CGFloat const kImageThumbnailHeight = 50.0f;  

Not Preferred:

#define kKeyForImage @"imageKey"

#define thumbnailHeight 50.0

If a const will be present in the header file, it must be an extern (externs don't use the "k" prefix but rather use the project's three character prefix e.g. "WBSKeyForImage").

Enumerated Types

When using enums, it is recommended to use the new fixed underlying type specification because it has stronger type checking and code completion. The SDK now includes a macro to facilitate and encourage use of fixed underlying types: NS_ENUM()

For Example:

typedef NS_ENUM(NSInteger, WBSLeftMenuTopItemType)  
{
  WBSLeftMenuTopItemTypeMain,
  WBSLeftMenuTopItemTypeShows,
  WBSLeftMenuTopItemTypeSchedule
};

You can also make explicit value assignments:

typedef NS_ENUM(NSInteger, WBSGlobalConstants)  
{
  WBSGlobalConstantsPinSizeMin = 1,
  WBSGlobalConstantsPinSizeMax = 5,
  WBSGlobalConstantsPinCountMin = 100,
  WBSGlobalConstantsPinCountMax = 500,
};

Older k-style constant definitions should be avoided unless writing CoreFoundation C code (unlikely).

Not Preferred:

enum GlobalConstants  
{
  kMaxPinSize = 5,
  kMaxPinCount = 500,
};

Case Statements

Braces are required for case statements, unless enforced by the complier

Preferred:

switch (condition)  
{
  case 1:
  {
    // ...
    break;  
  }
  case 2:
  {
      // ...
    break;
  }
  default:
  {
    break;
  }
}

Not Preferred:

switch (condition)  
{
  case 1:
    // ...
    break;
  case 2:
    // ...
    break;
  default:
    // ...
    break;
}

There are times when the same code can be used for multiple cases, and a fall-through should be used. A fall-through is the removal of the 'break' statement for a case thus allowing the flow of execution to pass to the next case value. A fall-through should be commented for coding clarity.

switch (condition)  
{
  case 1:
    // ** fall-through! **
  case 2:
  {
    // code executed for values 1 and 2
    break;
  }
  default:
  {
    // ...
    break;
  }
}

When using an enumerated type for a switch, 'default' is not needed. For example:

switch (menuType) {  
  case WBSLeftMenuTopItemMain:
  {
    // ...
    break;
  }
  case WBSLeftMenuTopItemShows:
  {
    // ...
    break;
  }
  case WBSLeftMenuTopItemSchedule:
  {
    // ...
    break;
  }
}

Private Properties

Private properties should be declared in class extensions (anonymous categories) in the implementation file of a class. Named categories (such as WBSPrivate or private) should never be used unless extending another class. The Anonymous category can be shared/exposed for testing using the +Private.h file naming convention.

For Example:

@interface WBSDetailViewController ()

@property (nonatomic, strong) GADBannerView *googleAdView;
@property (nonatomic, strong) ADBannerView *iAdView;
@property (nonatomic, strong) UIWebView *adXWebView;

@end

Booleans

Objective-C uses YES and NO. Therefore true and false should only be used for CoreFoundation, C or C++ code. Since nil resolves to NO it is unnecessary to compare it in conditions. Never compare something directly to YES, because YES is defined to 1 and a BOOL can be up to 8 bits.

This allows for more consistency across files and greater visual clarity.

Preferred:

if (post)  
if (![comment boolValue])  

Not Preferred:

if (post == nil)  
if ([comment boolValue] == NO)  
if (self.isAwesome == YES)  
if (self.isAwesome == true)  

If the name of a BOOL property is expressed as an adjective, the property can omit the “is” prefix but specifies the conventional name for the get accessor, for example:

@property (assign, getter=isEditable) BOOL editable;

Conditionals

Conditional bodies should always use braces even when a conditional body could be written without braces (e.g., it is one line only) to prevent errors. These errors include adding a second line and expecting it to be part of the if-statement. Another, even more dangerous defect may happen where the line "inside" the if-statement is commented out, and the next line unwittingly becomes part of the if-statement. In addition, this style is more consistent with all other conditionals, and therefore more easily scannable.

Preferred:

if (!error)  
{
  return success;
}

Not Preferred:

if (!error)  
  return success;

or

if (!error) return success;  

Where an if statement contains more than one element under evaluation each element should occupy it's own line with the operator between elements on the trailing line.

Preferred:

if ([self.items count] > 0 &&  
    self.opened)
{
  return success;
}

Not Preferred:

if ([self.items count] > 0 && self.opened)  
{
  return success;
}

Ternary Operator

The Ternary operator, ?: , should only be used when it increases clarity or code neatness. A single condition is usually all that should be evaluated. Evaluating multiple conditions is usually more understandable as an if statement, or refactored into instance variables. In general, the best use of the ternary operator is during assignment of a variable and deciding which value to use.

Non-boolean variables should be compared against something, and parentheses are added for improved readability. If the variable being compared is a boolean type, then no parentheses are needed.

Preferred:

NSInteger value = 5;  
result = (value != 0) ? x : y;

BOOL isHorizontal = YES;  
result = isHorizontal ? x : y;  

Not Preferred:

result = a > b ? x = c > d ? c : d : y;  

Init Methods

Each class should have one designated initialiser that all other initialisers funnel into

- (instancetype)init
{
  self = [super init];

  if (self)
  {

  }

  return self;
}

Class Constructor Methods

Where class constructor methods are used, these should always return type of 'instancetype' and never 'id'. This ensures the compiler correctly infers the result type.

These methods should always be the first methods in your class, below any class methods.

@interface Airplane
+ (instancetype)airplaneWithType:(WBSAirplaneType)type;
@end

Class Cleanup Methods

You should always implement 'dealloc' and 'didRecieveMemoryWarnings' methods in classes whenever necessary, especially when using KVO and notifications.

These methods should be at the end of the class with dealloc being the final method.

Happy Path

When coding with conditionals, the first branch should always be the happy path with unhappy explicitly defined in an else branch.
Preferred:

- (void)postUpdated
{
  if ([post boolValue])
  {
    //Happy
  }
  else
  {
      //Unhappy
  }

}

Not Preferred:

- (void)postUpdated
{
  if (![post boolValue])
  {
    //Unhappy
  }

  //Continue
}

One exception to this rule is the declarations of properties where we will only use the unhappy path.

- (NSObject *)feedLatestUpdate
{
    if (!_feedLatestUpdate)
    {
        _feedLatestUpdate = [NSObject alloc] init;
    }

    return _feedLatestUpdate;
}

Error handling

When methods return an error parameter by reference, switch on the returned value, not the error variable.

Preferred:

NSError *error;  
if (![self postWithError:&error])  
{
  // Handle Error
}

Not Preferred:

NSError *error;  
[self postWithError:&error];

if (error)  
{
  // Handle Error
}

Singletons

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

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

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

  return sharedInstance;
}

Line Breaks

Where a method contains more than one parameter the second parameter should be shown on its own line

Preferred:

self.productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers  
                                                                      prices:prices];

Not Preferred:

self.productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers prices:prices];  

Magic strings

Where possible avoid the use of magic strings because while XCode should find and update them during a refactor, it doesn't always do so and we can end up with bugs that only surface during run-time

Preferred:

[self post:NSStringFromClass[WBSLoadingView class]];

Not Preferred:

[self post:@"WBSLoadingView"];

Warnings

Compiler generated warnings should not be present in our code (if a warning is generated in a 3rd party library and you can fix it - do, and submit the fix to that 3rd party project), a check must always be made to ensure that all warnings are resolved before committing your changes.

The only exception to this is developer-generated warning where the keyword #warning has been explicitly used to highlight an issue with the current implementation. Developer-generated warning are intended to be short lived and need to be addressed within a reasonable timescale, after each sprint any existing warning in the app need to be discussed in the sprint planning meeting.

It's important to note that building the app against different configurations can result in different warnings, it's your responsibility to to ensure that your commit is warning free in all configurations.

Xcode project

The physical files should be kept in sync with the Xcode project files in order to avoid file sprawl. Any Xcode groups created should be reflected by folders in the filesystem. Code should be grouped not only by type, but also by feature for greater clarity.

Autolayout vs Frames

Dynamic positioning is preferred to static values, i.e check the superview's / screen's width rather than writing 320. Autolayout is encouraged where possible but is frequently not appropriate.

Button Action

Button actions should have the structure [buttonName]Pressed.

Preferred:

- (void)searchButtonPressed:(UIButton *)sender;

Not Preferred:

- (void) searchButtonAction: (UIButton *) sender;

- (void) searchButtonClicked: (UIButton *) sender;

Predicate Format

The format for the predicate string should use spaces between all elements and equality comparisons.

Preferred:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"feed.feedID == %@ && order != %@", self.feedID, self.pageOrder];  

Not Preferred:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"feed.feedID==%@ && order!=%@", self.feedID, self.pageOrder];  

Relationship Inverse

If the relationship is bidirectional and was set up as inverse in the Data Model, then we should set the value only in one of the sides.

Preferred:

[user addListsIsMemberObject:list];

Not Preferred:

[user addListsIsMemberObject:list];
[list addMembersObject:user];

Parentheses in mathematical operations

To avoid possible problems if someone do not know the rule, it is better to add parentheses to encapsulate every operation. With this rule we will avoid problems and a faster readability.

Preferred:

NSInteger *integer = ((4 * 8) / 2) + 2;  

Not Preferred:

NSInteger *integer = 4 * 8 / 2 + 2;  

Singular and plural use in class naming

To ease understanding of what kind of methods we can expect a class to contain, it is important that we define as Singular only those classes that will take of a single object whereas we use plural for those that use more than one.

Preferred: WBSConversationsAPIManager - this class will contain methods to manage single WBSConversations but also methods managing more than one simultaneously.

Not Preferred: WBSConversationsTableViewCell - this class represent a single WBSConversation therefore it shall be singular WBSConversationTableViewCell.

Dancing

Its really important to dance when the mood takes you but never sing