This style guide outlines the coding conventions of the iOS team at Intrepid Pursuits. It started as a fork of the coding convections of the New York Times iOS team (https://github.com/NYTimes/objective-c-style-guide).
We welcome your feedback. The current maintainers of this style guide are @cprime and @andrewdolce. If you have an issue with any of the existing rules and would like to see something changes, please open an issue or submit a documented PR with the desired change.
Here are some of the documents from Apple that informed the style guide. If something isn't mentioned here, it's probably covered in great detail in one of these:
Dot-notation should always be used for accessing and mutating properties. Bracket notation is preferred in all other instances.
For example:
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;
Not:
[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;
Bracket notation should be used to call methods.
Do this:
[set anyObject];
Not this:
set.anyObject;
For Cocoa objects, pay attention to the headers headers from the latest available SDK.
For example(as of iOS 8):
array.firstObject
array.count
Not:
[array firstObject]
[array count]
if
/else
/switch
/while
etc.) always open on the same line as the statement but close on a new line.For example:
if (user.isHappy) {
//Do something
} else if (user.isDead) {
//Do something else
} else {
//Do yet another thing
}
@synthesize
and @dynamic
should each be declared on new lines in the implementation.Conditional bodies should always use braces even when a conditional body could be written without braces. 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.
For example:
if (!error) {
return success;
}
Not:
if (!error)
return success;
or
if (!error) return success;
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.
For example:
result = a > b ? x : y;
Not:
result = a > b ? x = c > d ? c : d : y;
Use pragma marks to organize and break up the various features of your class files. They should also be used to group methods for specific protocols:
For example:
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//do stuff
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//do more stuff
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//do even more stuff
}
When methods return a success bool and uses 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).
In method signatures, there should be a space after the scope (-/+ symbol). There should be a space between the method segments.
For Example:
- (void)setExampleText:(NSString *)text image:(UIImage *)image;
More general methods should call more specific ones with default parameters
- (void)runBlock:(IPExecutionBlock)executionBlock {
[self runBlock:executionBlock timeout:5];
}
- (void)runBlock:(IPExecutionBlock)executionBlock timeout:(NSTimeInterval)timeout {
...
}
The ordering of the property specifiers should be:
@property(copy, nonatomic) NSNumber *aNumber;
@property(weak, nonatomic) id delegate;
@property(strong, nonatomic, readonly) RKObjectManager *manager;
Variables should be named as descriptively as possible. Single letter variable names should be avoided except in for()
loops.
Asterisks indicating pointers belong with the variable, e.g., NSString *text
not NSString* text
or NSString * text
, except in the case of constants.
Property definitions should be used in place of naked instance variables whenever possible. Direct instance variable access should be avoided except in initializer methods (init
, initWithCoder:
, etc…), dealloc
methods and within custom setters and getters. For more information on using Accessor Methods in Initializer Methods and dealloc, see here.
For example:
@interface NYTSection: NSObject
@property (nonatomic) NSString *headline;
@end
Not:
@interface NYTSection : NSObject {
NSString *headline;
}
When it comes to the variable qualifiers introduced with ARC, the qualifer (__strong
, __weak
, __unsafe_unretained
, __autoreleasing
) should be placed between the asterisks and the variable name, e.g., NSString * __weak text
.
Apple naming conventions should be adhered to wherever possible.
Long, descriptive method and variable names are good.
For example:
UIButton *shareButton;
Not
UIButton *shrBttn;
A two to three letter prefix (e.g. RM
or NYT
) should always be used for class names and constants. Constants should be camel-case with all words capitalized. Publicly accessible constants should be prefixed by the related class name for clarity.
For example:
static const NSTimeInterval NYTArticleViewControllerNavigationFadeAnimationDuration = 0.3;
Not:
static const NSTimeInterval fadetime = 1.7;
Properties and local variables should be camel-case with the leading word being lowercase.
Instance variables should be camel-case with the leading word being lowercase, and should be prefixed with an underscore. This is consistent with instance variables synthesized automatically by LLVM. If LLVM can synthesize the variable automatically, then let it.
For example:
@synthesize descriptiveVariableName = _descriptiveVariableName;
Not:
id varnm;
For rules on naming properties we use the standard Apple rules (Accessor Naming), with one expection. We recommend against changing the default setter/getter method names in order to minimize property declaration/use complexity.
For example:
@property (assign, nonatomic) BOOL editable;
Not:
@property (assign, nonatomic, getter=isEditable) BOOL editable;
When creating categories on classes not owned by you, category methods should be prefixed with your project class prefix in order to avoid method signature collisions with the original class or a another category on the same class (or even a superclass):
@interface UIColor (INTAdditions)
+ (UIColor *)int_navigationBarColor;
@end
Code should be as self-documenting as possible.
For example:
typedef NS_ENUM(NSInteger, DirectionCode) {
DirectionCodeNone = 0,
DirectionCodeRight = 2,
DirectionCodeLeft = 4,
};
- (DirectionCode)horizontalChangeWithDeviceInfo:(DeviceInfo)deviceInfo newPosition:(CGPoint)newPosition {
int oldPosition = deviceInfo.position.x;
if (newPosition.x < oldPosition.x) {
return DirectionCodeRight;
} else if(newPosition.x > oldPosition.x) {
return DirectionCodeLeft;
} else {
return DirectionCodeNone;
}
}
Not:
// calculate device's horizontal directional change
float _x = abs(p.x - deviceInfo.position.x) / scale;
int directionCode;
if (0 < _x & p.x != deviceInfo.position.x) {
if (0 > p.x - deviceInfo.position.x) {
directionCode = 0x04 /*left*/;
} else if (0 < p.x - deviceInfo.position.x) {
directionCode = 0x02 /*right*/;
}
}
When they are needed, comments should be used to explain why a particular piece of code does something. Any comments that are used must be kept up-to-date or deleted.
if (!accessGroup) {
#if TARGET_IPHONE_SIMULATOR
// Apps that are built for the simulator aren't signed, so there's no keychain access group
// for the simulator to check. This means that all apps can see all keychain items when run
// on the simulator.
//
// If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
// simulator will return -25243 (errSecNoAccessForItem).
#else
[genericPasswordQuery setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup];
#endif
}
init
methods should be structured like this:
- (instancetype)init {
self = [super init]; // or call the designated initalizer
if (self) {
// Custom initialization
}
return self;
}
NSString
, NSDictionary
, NSArray
, and NSNumber
literals should be used whenever creating immutable instances of those objects.
For example:
NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingZIPCode = @10018;
Not:
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 *buildingZIPCode = [NSNumber numberWithInteger:10018];
When accessing the x
, y
, width
, or height
of a CGRect
, always use the CGGeometry
functions instead of direct struct member access. From Apple's CGGeometry
reference:
All functions described in this reference that take CGRect data structures as inputs implicitly standardize those rectangles before calculating their results. For this reason, your applications should avoid directly reading and writing the data stored in the CGRect data structure. Instead, use the functions described here to manipulate rectangles and to retrieve their characteristics.
For example:
CGRect frame = self.view.frame;
CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
Not:
CGRect frame = self.view.frame;
CGFloat x = frame.origin.x;
CGFloat y = frame.origin.y;
CGFloat width = frame.size.width;
CGFloat height = frame.size.height;
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.
For example:
static NSString * const NYTAboutViewControllerCompanyName = @"The New York Times Company";
static const CGFloat NYTImageThumbnailHeight = 50.0;
Not:
#define CompanyName @"The New York Times Company"
#define thumbnailHeight 2
Enum values should be prefixed with the enum type name. Furthermore, when using enum
's, you should 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()
Example:
typedef NS_ENUM(NSInteger, NYTAdRequestState) {
NYTAdRequestStateInactive,
NYTAdRequestStateLoading
};
When working with bitmasks, use the NS_OPTIONS
macro.
Example:
typedef NS_OPTIONS(NSUInteger, NYTAdCategory) {
NYTAdCategoryAutos = 1 << 0,
NYTAdCategoryJobs = 1 << 1,
NYTAdCategoryRealState = 1 << 2,
NYTAdCategoryTechnology = 1 << 3
};
Private properties should be declared in class extensions (anonymous categories) in the implementation file of a class. Named categories (such as NYTPrivate
or private
) should never be used unless extending another class.
For example:
@interface NYTAdvertisement ()
@property (strong, nonatomic) GADBannerView *googleAdView;
@property (strong, nonatomic) ADBannerView *iAdView;
@property (strong, nonatomic) UIWebView *adXWebView;
@end
Raw image filenames should:
homescreen_arrow_left_selected.png
homescreen_arrow_left_unselected.png
Images should be contained in .xcassets file(s) unless there is a good reason not to.
A constant should be declared for every image in the image asset categlog, with the same name.
TODO: Create a script to generate the .xcassets and UIImages+[Prefix]ImageNames files
Since nil
resolves to NO
it is unnecessary to compare it in conditions. This allows for more consistency across files and greater visual clarity. (requires additional research)
For example:
if (!someObject) {
}
Not:
if (someObject == nil) {
}
Never implicily cast a non-BOOL expression to a BOOL because a BOOL is a char and if the thing that you are returning is larget than 8 bits, it can truncate to an unexpected value.
For example:
- (BOOL)hasObjects {
return array.count > 0;
}
Not:
- (BOOL)hasObjects {
return array.count;
}
For a BOOL
, here are two examples:
if (isAwesome)
if (![someObject boolValue])
Not:
if (isAwesome == YES) // Never do this.
if ([someObject boolValue] == NO)
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;
}
This will prevent possible and sometimes prolific crashes.
You should prefer forward declaration for over importing .h files in your .h files to avoid circuluar reference dependency.
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.
Custom colors and fonts for the project should be defined in specific category files, particularly when used in multiple locations.
+ (UIColor *)int_navigationBarBlueColor;
+ (UIColor *)int_submitButtonColor;
Custom fonts for the project sh
+ (UIFont *)int_navigationHeaderFontWithSize:(CGFloat)size;
+ (UIFont *)int_navigationHeaderFont;