Objective-C Coding Style
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
- Code Organization
- Braces
- Naming
- Types
- Images
- Prefixing
- Parentheses
- Properties
- Instance variables
- Methods
- Protocols
- Variables
- Dot-Notation Syntax
- Literals
- Constants
- Enumerated Types
- Case Statements
- Private Properties
- Booleans
- Conditionals
- Ternary Operator
- Init Methods
- Class Constructor Methods
- Class Cleanup Methods
- Happy Path
- Error handling
- Singletons
- Line Breaks
- Magic strings
- Warnings
- Xcode project
- Autolayout vs Frames
- Button Action
- Predicate Format
- Relationship Inverse
- Parentheses in mathematical operations
- Singular and plural use in class naming
- Dancing
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 ###define
s 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 enum
s, 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