This guide outlines the coding conventions and best practices for the Objective-C developers at Raizlabs.
Table of Contents generated with DocToc
CGFloat
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.
- (void)viewDidLoad
{
// set up foo object
UIFoo *foo = [[UIFoo alloc] init];
foo.property = value;
// set up bar object
UIBar *bar = [[UIBar alloc] initWithThing:foo];
}
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];
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 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 are camel-case, and should use the following format:
k
prefix// [k][class prefix][class name][constant name]
static const NSInteger kRZMyClassSomeErrorCode = -1;
See also: Cocoa naming conventions for variables and types.
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.
@property
, specifiers, and property typeSpecifier order:
strong
, weak
, assign
, copy
nonatomic
, atomic
readwrite
, readonly
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;
else if
/else
) on new line below closing bracePreferred:
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
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
CGFloat
is defined as double
in 64-bit architecture and float
in 32-bitf
when sending a float literal to a CGFloat
parameterx.f
when there is no decimal value. Instead, use x.0f
x.f
compiles perfectly fine, it is unclear (especially for our clients who may not be used to this abstract notation)Preferred:
CGSizeMake(2.0f, 2.0f);
case
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;
}
Comment whenever you are mitigating an OS bug (including the OS revision and when it might be able to be removed, if you know)
Comment whenever you write code that might appear weird or intimidating to a new developer
In general, comment any nontrivial code
Don’t comment trivial code where the meaning should be inferred from good variable and method naming
Never use your name in comments or code
git blame
Never reference bug numbers from another bug tracker (Jira, Github) in code
Use double slash comments (//
)
One space always immediately after slashes
In general, put comments on the line before the code being explained. One newline should come before the comment and after the code fragment being explained to avoid confusion with following code unrelated to comment:
...preceding code...
...preceding code...
// Explanatory comment
...code being explained...
...other code unrelated to comment...
...other code unrelated to comment...
You may comment "trivial" code if it aids readability in some way (eg. visually distinguishing multiple tasks in a long method)
You may comment in-line where appropriate. Eg. to identify the closing brace of a nested code block.
Special comment identifiers
// !!!:
// ???:
// TODO:
/* */
/** */
Documentation Comments
Documentation comments give semantic and contextual meaning to our APIs
These are required for open-source frameworks, but can also be useful to document internal code, especially core components of an app, like common API and data classes
Can be parsed by AppleDoc to create documentation file from code
Use ///
only for 1-line documentation comments
Install VVDocumenter via Alcatraz to automatically fill in AppleDoc-style comments when you type ///
For more info on documentation in Xcode, see this stackoverflow answer
-
, +
) and return typeand
or or
for parameter names.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.
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;
}
class prefix
][class name
][protocol function
]@interface
definition; protocol definition comes after.weak
.@required
and @optional
only need be present if both types of methods exist. If they are both omitted, every method is required by default.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
If you can do it with with a completion block, don't use a protocol.
typedef
blocks that are specific to a class or functiontypedef
ed names should follow the constant naming protocol// 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];
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
}];
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];
Always use NSLocalizedString for User-facing strings.
#define
NSLocalizedString constants.Preferred:
#define kRZClassNameStringConstant NSLocalizedString(@"Hello World", @"A hello world string")
Do not use #define
#undef
ed. It could also be redefined at any time.Always use static or extern string constants
use OBJC_EXTERN
instead of extern
use reverse-domain syntax with the domain of the project for internal (non-user-facing and non-api-facing) string constants
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";
NEVER use them!
- (void)someMethod
{
NSString *message = @"Error, you broke the app!";
}
#define
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)
If you need a constant struct, use the designated intializer syntax:
static const CGSize kRZTestViewControllerShadowOffset = { .width = 0.0f, .height = 3.0f };
NS_ENUM
(see this NSHipster post)Preferred:
typedef NS_ENUM(NSInteger, RZFoo) {
RZFooBlue = 0,
RZFooRed,
RZFooGreen
};
Not:
typedef enum
{
RZFooBlue,
RZFooRed
RZGreen
}RZFoo;
enum
{
RZFooBlue,
RZFooRed
RZGreen
};
The name of the type should act as a prefix for the subtypes
Typedefs should have class prefixes
It is common to use an "Unknown" type. If present, it should always be the first item in the enum.
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
};
instacetype
, not id
.[[[self class] alloc] init]
when instantiating an object of same type as self
, so that subclasses that call these methods will get back an object of the correct class.Preferred:
- (instancetype)init;
Not:
- (id)init;
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;
}
Always handle errors and return values
check BOOL
or object return value before checking the error inout parameter
The parameters of completion blocks should include a success BOOL
when applicable (e.g. web service calls). Test against this BOOL
, not the error
object, to determine whether the operation was successful
It is never safe to assume that a method will return a valid error object without first checking the return value, especially when using Apple APIs
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
}
}];
}
NSError **
)BOOL
indicating successNSError
double pointers outError
:- (BOOL)doActionReturningError:(NSError **)outError;
- (BOOL)doActionWithThing:(NSObject *)thing error:(NSError **)outError;
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];
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>
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
}];
Advances in Clang and Objective-C have made certain conventions obsolete. 99% of the time, we should no longer use the following:
@synthesize
d properties (except for readonly properties as of Xcode 6)#pragma mark - Private Methods
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.
.h
)Framework @import
s
Application header #import
s ("..."
)
.m
or _Private.h
if you can. This can improve build times by reducing the redundancy of header imports.forward @class
declarations
forward @protocol
declarations
typedef
ed enumerations and block signatures
OBJC_EXTERN
ed constant declarations
@interface
- protocol conformations should be used here judiciously — consider using in .m
or _Private.h
; see also Rule of Three)
Nothing should be public unless it explicitly needs to be used by other classes
@property
declarations
UIView
subclassesNSObject
subclassesNSLayoutConstraint
sclass method declarations
public interface method declarations
IBOutlet
/IBAction
should never appear in .h
files!
@protocol
definitions
@required
and @optional
only necessary if both types of methods are present_Private.h
fileWhen you have a base class of which you have multiple subclasses. For example:
What to do:
An example structure:
RZMainViewController.m
RZMainViewController.h
RZMainViewController_Private.h
#import
s RZMainViewController_Private.h
RZMainViewController~iphone.m
RZMainViewController~iphone.h
RZMainViewController~iphone.xib
#import
s RZMainViewController_Private.h
RZMainViewController~ipad.m
RZMainViewController~ipad.h
RZMainViewController~ipad.xib
.m
)framework @import
s
application header imports
typedef
ed enum
s, block signatures
macros
constant definitions
@interface
extension
IBAction
method declarations
@implementation
#pragma mark -
@synthesize
statements
init
& dealloc
IBAction
handlers