Skip to content

Instantly share code, notes, and snippets.

@SiriusDely
Last active August 29, 2015 14:02
Show Gist options
  • Save SiriusDely/ef4d4136cff478c1c248 to your computer and use it in GitHub Desktop.
Save SiriusDely/ef4d4136cff478c1c248 to your computer and use it in GitHub Desktop.
Compilation of Objective-C Style Guides

Compilation of Objective-C Style Guides

Table of contents

Purpose

Consistent coding conventions are one way to keep code readable and maintainable. Written coding conventions provide a handy reference for the times when one can't remember what conventions apply.

In line with George Orwell's last rule in Politics and the English Language (1946). Break any of these rules sooner than doing anything barbarous.

Xcode Project Settings

Static Analyser

Run the Static Analyser automatically with builds. Strive for zero warnings and errors with the default warnings and errors turned on. Use the Clang pragmas to silence warnings where you really have to.

Warnings in your own code should be fixed in general. Sometimes it is not possible to remove a warning but it is clear that to the developer that there is no problem with the underlying code. In this cases it is possible to silence a warning with pragma comments. This should be used very rarely and should always be documented.

Automatic Reference Counting

All new projects use ARC. The compiler is better placed to insert boilerplate code.

Unit Testing

Use the Unit Testing option when creating new projects from the templates. You may not always use strict TDD, but when the time comes where something needs to be tested automatically, rather than running through a series of steps with a running app, having this set up will be a timesaver.

That said, Unit Testing is a useful discipline, use it where you can. Don't let the lack of a complete set of tests put you off writing any unit tests. Something is better than nothing, as long as you remember that the tests are not comprehensive.

Standard Files

The Application Delegate class is named AppDelegate.

The project templates create standard files - a pch file, a main file, a plist file. Regardless of what the templates are set up to do rename them to Prefix.pch, main.m and Info.plist. Some changes will need to be made to the project settings that reference these files to make them work. Similar files are created for unit testing or other targets, but these need not be renamed; the simplified names are only be used for the main target of the project.

Arrange and keep Groups & Files on the left navigator pane on Xcode to reflect its real structure when opened using Finder. It is arranged alphabetically and within its folder. The default Groups (folders) must be exist, i.e., Controllers, Models & Views.

There are common macros that are used throughout my projects. This header file should be added to all projects and imported into the Prefix.pch file. The most up to date version is available as a gist on Github:

// Useful Macros.
// The best place to import this is in your project's pch file.
// See http://www.cimgf.com/2010/05/02/my-current-prefix-pch-file/ for details.
// Most current version at https://gist.github.com/325926 along with usage notes.

#ifndef jcscommonmacros
#define jcscommonmacros_1_0  10000
#define jcscommonmacros      jcscommonmacros_1_0
#endif

#ifdef DEBUG
  #define DLog(...) NSLog(@"%s %@", __PRETTY_FUNCTION__, [NSString stringWithFormat:__VA_ARGS__])
  #define ALog(...) [[NSAssertionHandler currentHandler] handleFailureInFunction:[NSString stringWithCString:__PRETTY_FUNCTION__ encoding:NSUTF8StringEncoding] file:[NSString stringWithCString:__FILE__ encoding:NSUTF8StringEncoding] lineNumber:__LINE__ description:__VA_ARGS__]
  #define ULog(fmt, ...)  { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:@"%s\n [Line %d] ", __PRETTY_FUNCTION__, __LINE__] message:[NSString stringWithFormat:fmt, ##__VA_ARGS__]  delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; }

#else
  #define DLog(...) do { } while (0)
  #ifndef NS_BLOCK_ASSERTIONS
    #define NS_BLOCK_ASSERTIONS
  #endif
  #define ALog(...) NSLog(@"%s %@", __PRETTY_FUNCTION__, [NSString stringWithFormat:__VA_ARGS__])
  #define ULog(...)
#endif

#define ZAssert(condition, ...) do { if (!(condition)) { ALog(__VA_ARGS__); }} while(0)

//
// This is a general test for emptiness
// Courtesy of Wil Shipley http://www.wilshipley.com/blog/2005/10/pimp-my-code-interlude-free-code.html

static inline BOOL isEmpty(id thing) {
  return thing == nil
    || ([thing respondsToSelector:@selector(length)]
    && [(NSData *)thing length] == 0)
    || ([thing respondsToSelector:@selector(count)]
    && [(NSArray *)thing count] == 0);
}

Schemes

Xcode creates a standard scheme named after the project for development. As part of setting up the project create a Release Scheme (and a TestFlight scheme if appropriate) and set them up to use the correct signing keys. Make these schemes shared and add them to version control.

Compiler

Use the most up to date compiler possible and all the associated benefits, Literals, ARC, auto-synthesis, the static analyser, auto-layout, storyboards, etc.

Code Style

imports

See the code layout section to see how to lay out the imports in the .h and .m files.

Although the common framework imports should be in the pch file, put all the required imports into the source files. Ifind it helps to see what frameworks a class is set up to deal with.

Only put superclass imports and protocol imports into the header file. Forward declarations should use the @class statement. The remaining headers should go into the .m file.

iVars

Avoid using iVars, certainly not as part of the public interface. Prefer the use of properties.

If using iVars treat them as private and add them to a class extension or the @implementation section if using a compiler that supports it.

// Bad - declared in the public header

@interface MyClass : NSObject {
  NSString *_aString;
}

// Better - declared in the .m file as part of a class extension

@interface MyClass () {
  NSString *_aString;
}

// Best - declared in the class implementation itself

@implementation MyClass {
  NSString *_aString;
}

Note the underscore prefix for iVars. I've gradually come around to the idea of using them. what they do is a)make clear that you are using an iVar and not a local variable, and b)remove the need to rename local variables or parameters to avoid clashing with iVars.

Properties

Prefer immutable to mutable types, and use copy not strong/retain for types that are part of a mutable / immutable class cluster unless you really need a mutable type.

Nonatomic/atomic declaration should come first. Prefer nonatomic to atomic type declarations.

The memory management declaration come second.

Choose a different getters or setters for BOOL types where appropriate.

@property (nonatomic, strong) UIColor *color;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, weak) id<SMProtocolDelegate> delegate;
@property (nonatomic, assign, getter=isDownloading) BOOL downloading;

Only expose as much in the public interface as needed. It's easier to have a smaller surface area of change when working through code, especially during discovery. If a property needs to be publicly readonly but readwrite for the class, redeclare the property as readwrite within a class extension.

// In the .h file
@interface MyClass : NSObject

@property (nonatomic, copy, readonly) NSString *aString;

@end

// In the .m file
@interface MyClass ()

@property (nonatomic, copy, readwrite) NSString *aString;

@end

Auto-synthesized properties generate an iVar with a leading underscore. If not using a compiler that supports auto-synthezise @sythezise backing stores in the same way.

Method and Variable Naming

Don't fear long descriptive names, Xcode has auto-completion.

Follow the conventions in Apple's Coding Guidelines for Cocoa

Dot Notation

Use it, but don't abuse it.

If it's possible to chain properties together neatly then use it. If the dots and brackets are becoming interleaved or nested too deep use brackets. This one is hard to nail down but if you look at the resulting line and you have to pick apart the calls - change it to all brackets - which should always work.

Only use dot notation for properties.

Method Signatures

Left aligned, with a space after the scope. There should be a space between the method segments, and if a parameter is a pointer, then there should be a space before the asterisk

// BAD
-(void)doSomethingWithString:(NSString*)aString;

// GOOD
- (void)doSomethingWithString:(NSString *)aString;

Private Methods

These should not be declared in the public interface or a class extension.

Put them at the end of the class implementation in their own #pragma section.

Empty Methods

If there is a method that has no implementation, or is just a call to super then it should be removed.

Types

integer

Use NSInteger and NSUInteger in preference to these. Make sure you use the standard framework methods with integer instead of int to get the correct types back.

For Foundation types, when iterating use CFIndex.

BOOL

Use YES and NO not true, false, 1, -1 or anything like that.

Don't compare BOOL returns against BOOLs:

// GOOD
if ([self aMethodReturningBOOL]) {

// THE DEVIL'S WORK
if ([self aMethodReturningBOOL] == YES) {

floats

Use CGFloat instead of float.

Error Handling

Use NSError wherever possible. Exceptions and Asserts are used to find programmer errors. Use the ALog and ZAssert macros (c.f. Standard Macros) for convenience.

Static Strings

Use static declarations in preference to #defines.

Use extern to declare a public constant that is defined within the class. Use a simple static otherwise

// declared in header defined in implementation
// .h
extern NSString * const kMyConstant;

// .m
NSString * const kMyConstant = @"MyConstant";

// For internal use
static NSString *sSomeString;

Enums

Use the new typed enumerations, or even better, use the Apple provided macros

// Good
typedef enum : unsigned char {
  Red,
  Green,
  Blue
} StandardColours;

// Better
typedef NS_ENUM(unsigned char, StandardColours) {
  Red,
  Green,
  Blue
};

For simple enumerations there is no need to assign values to the values unless they are really needed in specific situations. The exception is for bitmask enumerations which should specify the initial value and the remaining values should use the shift-left operator (<<).

// Good
typedef enum : NSUInteger {
  ABCOutputNone    = 0,
  ABCOutputSeconds = 1 << 0,
  ABCOutputMinutes = 1 << 1,
  ABCOutputHours   = 1 << 2
} ABCOutput;

// Better
typedef NS_OPTIONS(NSUInteger, ABCOutput) {
  ABCOutputNone    = 0,
  ABCOutputSeconds = 1 << 0,
  ABCOutputMinutes = 1 << 1,
  ABCOutputHours   = 1 << 2
};

TODOs

Put // TODO: comments to remind yourself of things and use search to find them.

If you want to mark critical parts that need to be implemented, don't bother with using build scripts to find the TODOs - just use a #warning instead:

#warning This method needs to be implemented.

This will raise a convenient warning with the message when building.

Code layout

#pragma Is Your Friend

Group methods into related groups and section them up with #pragma descriptions.

Import Ordering

See the code style section for what to import and where.

The header files should have the minimal number of imports, as forward declarations are preferred. In any case this is the way to lay them out.

  • The interface header (i.e. the .h file for the current .m file)
  • A blank line
  • The other imports, alphabetically
  • A blank line
  • Framework headers (These should also be in the .pch file)

If a suitable tool is available the Framework headers and the other imports may be ordered alphabetically within each of these groups, but it's not often worth the effort, unless you are looking for some boyscouting to do.

Method Ordering

  • Class methods.
  • Instance methods.
    • Public methods declared in the header file.
    • Action methods declared in the header file.
    • Custom accessors (setters and/or getters) for declared properties that you don't want to leave to auto-synthesize.
    • Custom initialisers.
    • Overrides initialisers.
    • dealloc (if needed).
    • For controllers - their common methods: i.e. view lifecycle methods for view controllers, table view datasource and delegate methods for table view controllers, etc.
    • Super class overrides.
    • Action methods declared only in implementation file.
    • Other delegate and protocol methods that the class implements.
    • Private methods for the class's own internal use.
  • Private Class methods.

Try and keep the order of the methods in the header file the same as in the implementation file. This is a handy thing to look for when you want to follow the Boyscout Rule.

Unit Test Classes

The Xcode templates create these with a .h and a .m file. In almost all cases this isn't needed. Unit tests are self contained and having a .h file which can be shared just increased complexity. So, for unit test classes - put the interface and implementation in the same .m file for each test class.

Comments

One-line comments // are preferred to multiline /**/ comments. Keep comment's left margin following code's left margin. There should be one space between the comment marker and the comment text.

Comments should be used to document why something is happening and not what is happening and should be used to explain situations that are not trivial.

If you need to use a comment to explain what a variable does - then it's likely that you haven't used a descriptive enough name for it. If there is a comment to explain a section of code in a method, consider moving that section of code to a well named private method instead.

For writing inline documentation use AppleDoc.

If you can't be bothered to keep the comments updated don't have them at all. Bad or out of date comments are worse than none at all.

Horizontal Spacing

Use 2 spaces to indent code, not tabs. Set this on Xcode preferences. Use of 2 spaces helps developers using 13" screen on their Macs.

Long lines aren't a problem. Let Xcode autowrap them. If you do decide to wrap lines containing methods - make sure the colons are aligned.

Vertical Spacing

Use a single blank line between methods. There is no need to leave a blank line at the start of a method definition.

Use two blank spaces for organisation at a large scale, such as to separate static definitions from implementation blocks, or to separate the end of the @property declarations from the method declarations in a header file.

Group lines of codes together when they are related. Think of it like writing, where sentences that relate to a topic are grouped into paragraphs.

Braces

A brace is always opened on the same line after a space (the exceptions is for method and block definitions). The closing brace is on a separate line and indented to the same level as the line with the opening brace.

- (BOOL)someMethod
{
  return YES;
}

There should always be a space after the control structure (i.e. if, else, etc). The else and else if statements should be on the same line as the closing brace of the if statement.

Xcode is inconsistent about the opening brace, and some of your colleagues may be too. I wrote a small ruby gem called fixbraces which will correctly position the braces on the opening line.

Control Structures

if/else

if (button.enabled) {
  // do something
} else if (otherButton.enabled) {
  // do something else
} else {
  // do something by default
}

Always use a multiline if/else statement with the clauses in braces. This is not only clearer to read but easier to make changes to.

The Ternary Operator

A handy structure, but should be used sparingly where it aids readability.

// Acceptable
NSString *boolString = nil;

if (someBool) {
  boolString = @"YES";
} else {
  boolString = @"NO";
}

DLog(@"The BOOL value is %@", boolString);

// Better. Succinct and readable
DLog(@"The BOOL value is %@", someBool ? @"YES" : @"NO");

Switch

For safety reason, use brackets for each case of statement. In some cases, when using ARC and declaring varibles within a case block an error may be thrown "Switch case is in protected scope" and then surrounding the case statement with brackets should clear up the error.

The default case should always be the last case and should always be included.

switch (something.state) {
  case 0: {
    // braced block where required by the compiler
    break;
  }
  case 1: {
    // Something
    break;
  }
  case 2: {
    
  }
  case 3: {
    // Something
    break;
  }
  default: {
    // Something
  }
}

For

for (NSInteger idx = 0; idx < 10; idx++) {
  // Do something
}

for (NSString *key in dictionary) {
  // Do something
}

When iterating using integers, it is preferred to start at 0 and use < rather than starting at 1 and using <=. Also, use idx instead of i. Fast enumeration is generally preferred because it is easier on the eyes as well as faster. Also consider using block enumeration where applicable.

While

while (something < somethingElse) {
  // Do something
}

Multiple Returns

Purists say that there should be only one return point from a function. There are times where multiple returns are not a problem. I find that this makes it clearer when reading the code that the function will return at this point and I don't have to look through the method any more to see if the value of self is changed later on before returning.

Version Control

You have no excuse for not using version control.

Aim for all commits on the master branch to compile cleanly and work (for some definition of 'work').

Develop on branches. Commit early and commit often, don't be afraid to rebase commits. Unlike the master branch, these don't all have to compile cleanly. You'll be rebasing them onto master anyway so they'll be tidied up then.

When writing commit messages use the correct conventions

The Boyscout Rule

The Boyscouts say: "Always leave a campsite cleaner than you found it". A similar rule is said to apply to programming - "Always check in a module cleaner than you checked it out".

In these days of distributed version control, I'd say that means if you see a problem in code - fix it. Don't leave it for later, don't leave it for someone else. Version control, unit tests, comments are all structures that support making changes like this.

Source

If you feel like creating your own, you can grab the latest markdown for this page from Github.

Credits

The basis for these standards were taken from

Revision History

Last updated June 5, 2014

Full history available on GitHub.

Project Setup

Set Xcode project class prefix to "SM", stands for "Suitmedia Mobile".

View management

Use storyboard, nib, interface builder, and autolayout when ever possible. Strive to make screens & views on storyboard or nib looks closely with running app. Do NOT override -loadView, as it is will complicate view's frame & bound settings and will force us to use VFL for defining autolayout.

Define object's identifier in storyboard as same as its class name. Usually this is needed for view controllers, table view cells, collection view cells. And then useful for using methods, such as:

  [storyboard instantiateViewControllerWithIdentifier:NSStringFromClass([Class class])];
  [tableView registerNibWithIdentifier:NSStringFromClass([Class class])];

Comments

/*!
 *  Method comment.
 *  Start this with a command verb (i.e. “Initialize”, “Compare”).
 *  Use periods.
 *
 *  @param <name> <description>
 *
 *  @return <description>
 */

Public interface should contain comments for methods. Private methods that aren’t implementing protocol methods should have comments. Don’t declare private methods outside of the implementation.

// Private comment. Again, make sure to use periods.

Comment confusing or non-trivial code inside a method. Reference issues on GitHub if code fixes a specific bug.

// TODO: Fix this problem. #42

If a method is missing code pending a later fix or feature, reference the GitHub issue.

Constants

.h: extern NSString *const kSMClassNamePublicConstantString

.m: NSString *const kSMClassNamePublicConstantString = @”My Constant String”;

.m: NSString *const kSMClassNamePrivateConstantString = @”My Private String”;

Static variables

static UIInteger sMyStaticVar;

Imports

#import “MyClass.h”

#import “Alphabetical.h”
#import “Order.h”

#import <AngleBracketImports/AtEnd.h>

Alphabetize header imports by section. Start with the class definition, then internal imports, then external imports with angle brackets.

Delegates/protocols

Self should always be the first parameter in delegate methods.

- (void)className:(SMClassName *) didDoSomethingWithNumber:(NSNumber *)number;

Use @optional for methods that not need mandatory implementation.

In interface declarations implementing more than one delegate, put each delegate name on its own line, in alphabetical order i.e.

@interface MyInterface () <
ADelegate,
BDelegate
>

Event/action handlers

Noun describing view followed by past tense verb describing event. Optionally take sender or gesture recognizer if it’s used.

buttonTapped
cellSwipedRightWithGestureRecognizer:

Properties

Use lazy instantiation pattern (in the getter) as much as possible.

Try to use private properties always instead of raw ivars. This is for consistency - we should be using lazy instantiation even on private variables (for example, private subviews), so let’s just use private properties for all private variables so we’re not mentally toggling between _privateVar1 and self.privateVar2.* Major exception is in init methods, where you should access the ivar directly (and obviously in the getter and setter). Avoid synthesize statements - use the default synthesizer provide by XCode 4.4+ (underscore + property name).

If you declare a property as readonly in the public .h @interface, redeclare it as readwrite in the private @interface. This ensures that the backing store ivar is actually created, and allows code in the .m file to write to the property without accessing the ivar directly.

Modifier order (for consistency): atomic/nonatomic, strong/weak/assign, readonly/readwrite, getter, setter.

Method calling syntax

Always use the dot syntax for setters and getters. Never use it for other methods.

Initialization

Access ivars directly in initializers (this is the one place to do that).

All initialization must go through an explicitly-documented designated initializer. The comment should contain the string “This is the designated initializer.” If the designated initializer has a different method signature than its superclass's designated initializer, an initializer must be written that matches the method signature of the superclass's designated initializer (and calls the designated initializer, needless to say).

Controller communication

Rules of thumb:

  • Use delegates to communicate back to a controller some action/event that originated in the view (or whatever class).
  • Use block to communicate back to a controller the result of some action explicitly taken by the controller on the view (or whatever class)

File sections

Create hierarchy as follows:

#pragma mark - Public Class methods

+ Public Class Methods
+ Public Instance Methods
+ Public Properties / Custom Accessors
+ Object's Lifecycle Methods (Initialization & Deallocation)
    + Custom Initializers
    + Overrides Initializers
    + Dealloc (if-needed)
+ View lifecycle
+ Overrides Methods
+ Private Methods
+ Private properties / Custom Accessors
+ Event handlers
+ <Something> protocols (datasource then delegate)
+ <Something> helpers (e.g. View lifecycle helpers)

#Misc. naming conventions

  • “Count” variables should always end with the word “count” (fooCount, barCount, etc.)
  • Methods for updating core data from the server should be called “With<Dictionary/Array>:” and return the created object or array of objects.

#Brackets

  • Always use curly braces around code blocks, even one liners.
  • Put the opening curly brace on the same line except for methods.
  • Use parentheses around multi-line expressions to help the auto-indenter do it’s thing.

#Auto layout Use AutoLayoutShorthand and AutoLayoutHelpers to construct views. Construct initial view properties, like style, in a memoized property getter, i.e.:

- (UILabel *)topicLabel
{
  if (!_topicLabel) {
    _topicLabel = [UILabel new];
    _topicLabel.translatesAutoresizingMaskIntoConstraints = NO;
    _topicLabel.font = [PREFIXFont searchResultCellSecondaryFont];
    _topicLabel.textColor = [PREFIXColor secondaryTextColor];
  }
  return _topicLabel;
}

View construction should be done in the following stages, in loadView (only if-needed) for a view controller or init for a view:

self.view = [self mainView]; // View controllers
[self addSubviewTree]; // Add each view to the hierarchy
[self constrainViews]; // Individual methods to constrain each view
[self setupNavigationBar]; // Any navigation bar view/button/title customization

Based on Kenny Tang's iOS Coding Conventions

Objective-C Coding Convention and Best Practices

This document aims to set a standard way of doing things so everyone can do things the same way. If there is something you are not particularly fond of, it is encouraged to do it anyway to be consistent with everyone else.

Most of these guidelines are to match Apple's documentation and community-accepted best practices.

This document is mainly targeted towards iOS development, but applies to Mac as well.

Usage

These coding guidelines shall be applied for all new iOS and Mac projects, converting old projects is possible but not recommended in general.

Warnings

Warnings in your own code should be fixed in general. Sometimes it is not possible to remove a warning but it is clear that to the developer that there is no problem with the underlying code. In this cases it is possible to silence a warning with pragma comments. This should be used very rarely and should always be documented.

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"

// Code that generates a warning but really shouldn't
// I deactivated this warning because ...
// ...

#pragma clang diagnostic pop

Another option, which again should be rarely used is to silence warnings in a file completely. This should only be done on files of third-party-libraries which won't get changed. For this one has to specify the option -w as Compiler Flag for a file (Target - Build Phases - Compile Sources).

TODOs

Todos should be marked as follows as should contain a brief description of what there is to do:

 // TODO: Fix the Bug XYZ that happens when the user clicks on the second button while the App is loading

Here's a handy script which can be added as a Build Phase (Target - Build Phases - Add Build Phase - Run Script) to your project that automatically generates a warning out of each TODO-Comment, when in release build:

KEYWORDS="TODO:|FIXME:|\?\?\?:|\!\!\!:"

if [ $CONFIGURATION != "Debug" ]; then
find "${SRCROOT}" \( -name "*.h" -or -name "*.m" \) -print0 | xargs -0 egrep --with-filename --line-number --only-matching "($KEYWORDS).*\$" | perl -p -e "s/($KEYWORDS)/ warning: \$1/"
fi

Static Analyzer

The Clang static analyzer should be used early and often, as it can detect various code smells and helps finding those.

Comments

One-line comments // are preferred to multiline /**/ comments. Comments should be used to document why something is happening and not what is happening and should be used to explain situations that are not trivial.

For documentation AppleDoc is used: http://www.gentlebytes.com/home/appledocapp/

Operators

NSString *foo = @"bar";
NSInteger answer = 42;
answer += 9;
answer++;
answer = 40 + 2;

The ++, --, etc are preferred to be after the variable instead of before to be consistent with other operators. Operators separated should always be surrounded by spaces unless there is only one operand.

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. This future proofs code for 64-bit platforms.

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. This is considered best practice and makes for clearer code.

Spacing

Code shall be indented by 2 spaces not tabs, configure your Xcode's setting on this. If you want to wrap a long line of code then make sure it is colon aligned per Xcode’s formatting. Keywords such as if, while, do, switch, etc. should have a space after them. Wasted vertical space should be avoided, there should not be more than one empty line, except between big group of code, e.g: after @end of @interface or @protocol.

// long method name calling convention
color = [NSColor colorWithCalibratedHue:0.10
                             saturation:0.82
                             brightness:0.89
                                  alpha:1.00];

Methods

- (void)someMethod
{
  // Do stuff
}


- (NSString *)stringByReplacingOccurrencesOfString:(NSString *)target withString:(NSString *)replacement
{ // ok here too
  return nil;
}

There should always be a space between the - or + and the return type ((void) in this example). There should never be a space between the return type and the method name.

There should never be a space before or after colons. If the parameter type is a pointer, there should always be a space between the class and the *.

The return type should never be ommitted (defaults to id for methods and to int for functions).

There should always be a space between the end of the method and the opening bracket. The opening bracket can be either on the next line, or on the same line.

There should always be two new lines between methods. This matches some Xcode templates (although they change a lot) and increase readability.

Pragma Mark and Implementation Organization

An excerpt of a UIView:

#pragma mark - Lifecycle

- (void)dealloc
{
  // Unregister Notifications, clean up etc.
}

#pragma mark - UIView

- (id)layoutSubviews
{
  [super layoutSubviews];
  
  // Stuff
}

- (void)drawRect:(CGRect)rect
{
  // Drawing code
}

Control Structures

There should be a space after the control structure (i.e. if, else, etc).

If/Else

if (button.enabled) {
  // Stuff
} else if (otherButton.enabled) {
  // Other stuff
} else {
  // More stuf
}

If comments are desired around the if and else statement, they should be formatted like the example above with one empty line between the end of a block and the following comment.

Ternary Operator

label.frame = (inPseudoEditMode) ? kLabelIndentedRect : kLabelRect;

Use the ternary operator only for assignments with simple conditions that probably won't be expanded. (readability & expandability)

Switch

switch (something.state) {
  case 0: {
    // Something
    break;
  }
  case 1: {
    // Something
    break;
  }
  case 2: {
  }
  case 3: {
    // Something
    break;
  }
  default: {
    // Something
    break;
  }
}

Brackets are desired around each case. If multiple cases are used, they should be on separate lines. default should always be the last case and should always be included.

For

for (NSInteger idx = 0; idx < 10; idx++) {
  // Do something
}

for (NSString *key in dictionary) {
  // Do something
}

When iterating using integers, it is preferred to start at 0 and use < rather than starting at 1 and using <=. Use idx insted of i, except for sub-iterating loops (h, i, j or x, y, z). Fast enumeration is generally preferred because it is easier on the eyes as well as faster. Also consider using block enumeration where applicable.

While

while (something < somethingElse) {
  // Do something
}

Import

Always use @class whenever possible in header files instead of #import since it has a slight compile time performance boost.

From the Objective-C Programming Guide (Page 38):

The @class directive minimizes the amount of code seen by the compiler and linker, and is therefore the simplest way to give a forward declaration of a class name. Being simple, it avoids potential problems that may come with importing files that import still other files. For example, if one class declares a statically typed instance variable of another class, and their two interface files import each other, neither class may compile correctly.

Header Prefix

Adding frameworks that are used in the majority of a project to a header prefix is preferred. If these frameworks are in the header prefix, they should never be imported in source files in the project.

For example, if a header prefix looks like the following:

#ifdef __OBJC__
  #import <Foundation/Foundation.h>
  #import <UIKit/UIKit.h>
#endif

#import <Foundation/Foundation.h> should never occur in the project outside of the header prefix.

Properties

@property (nonatomic, strong) UIColor *topColor;
@property (nonatomic, assign) CGSize shadowOffset;
@property (nonatomic, unsafe_unretained, readonly) UIActivityIndicatorView *activityIndicator;
@property (nonatomic, assign, getter=isLoading) BOOL loading;

If the property is nonatomic, it should be first. The next option should always be strong, weak, or assign since if it is omitted, there is a warning. readonly should be the next option if it is specified. readwrite should never be specified in header file. readwrite should only be used in class extensions to overwrite a specified readonly in the headwer file. getter or setter should be last, setter should rarely be used, getter should mainly be used for BOOL properties.

See an example of readwrite in the Private Methods section.

Explicitely defined iVars for properties should be omitted, each @synthesize should be on a single line and should synthesize a member-variable with a leading _ . This is to not create a name clash with parameter names (eliminates the need for cryptic parameter names like aTableView instead of just tableView and there is no need to rename the parameternames generated by code completion) and because LLVM 4.0's autosynthesize generates iVars with leading underscores. When LLVM 4.0 is used in a non-beta Xcode (4.4), a manual synthesize should be ommitted and autosynthesization should be used.

@synthesize topColor = _topColor;

Encapsulation

Encapsulation should be used to restrict access to certain component of an object (private methods, variables, etc.) and to group related functionality. Dependencies should be kept as minimal as possible, especially global dependencies should be avoided.

The header file should only contain methods and properties, that belong to your interface. Private methods, properties etc. should be put in a private class extension at the top of your implementation file. Header files generally don't contain an iVar-section.

init

The designated initializer of a class shall be marked with a comment in the header file, the designated initializer of the base-class should always be overridden. e.g. always override initWithFrame in your UIView-subclass. Dot-Notation shall not be used in init and dealloc, only direct iVar-access.

dealloc

If -dealloc is implemented (ARC) it should be placed directly below the -init methods of any class, or at the end of the file. On non-ARC code dealloc must always be implemented and release all the iVars.

Private Methods and Properties

MyShoeTier.h

@interface MyShoeTier : NSObject 

// In general no iVar-section

@property (nonatomic, strong, readonly) MyShoe *shoe;

...

@end

MyShoeTier.m

#import "MyShoeTier.h"

@interface MyShoeTier () {
  // Here can go private iVars if there is the need to
}

@property (nonatomic, strong, readwrite) MyShoe *shoe;
@property (nonatomic, strong) NSMutableArray *laces;

- (void)crossLace:(MyLace *)firstLace withLace:(MyLace *)secondLace;

@end


@implementation MyShoeTier

// Here shall go the @synthesize-block
...

@end

Private methods should always be created in a class extension for simplicity since a named category can't be used if it adds or modifies any properties.

Note: The above example provides an example for an acceptable use of a readwrite property.

Naming

In general, everything should be prefixed with a 2 letter prefix. Longer prefixes are acceptable, but not recommended.

It is a good idea to prefix classes with an application specific, if it is application specific code. If there is code you plan on using in other applications or open sourcing, you should prefix it with the company-wide prefix SM.

Here are a few examples:

SMLoadingView     // Simple view that can be used in other applications
AppDelegate       // AppDelegate should ALWAYS be named AppDelegate
SCLoadingView     // `NGLoadingView` customized for the application SuitChamp

Classes, Categories, Functions and Protocols should always start with a capitalized letter, variables, properties and method names with a lowercase letter.

Naming Again

Variables, Methods or Classes should never be abbreviated, verbose naming, as heavily used by Apple, is preferred. Ambiguity shall be avoided, as well as connectives like and and with in method names.

SMBrowserViewController   // instead of SMBrowserVC
currentPageIndex          // instead of pageIdx or just idx

- (void)updateSegmentTextViewContentSize
- (void)updateBarButtonItemsForInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation

Methods that return (autoreleased) objects should follow the following naming scheme, get should not be used for marking getters like in other languages (e.g. Java):

[object thing+condition];
[object thing+input:input];
[object thing+identifer:input];

// Examples:
realPath    = [path stringByExpandingTildeInPath];
fullString  = [string stringByAppendingString:@"Extra Text"];
object      = [array objectAtIndex:3];

If you want a variation on a value the following format is possible too:

[object adjective+thing];
[object adjective+thing+condition];
[object adjective+thing+input:input];

// Examples:

capitalized = [name capitalizedString];
rate        = [number floatValue];
newString   = [string decomposedStringWithCanonicalMapping];
subarray    = [array subarrayWithRange:segment];

Methods

Any method that is empty or just calls super should be removed from the source code as they are redundant.

Categories

Categories should mainly be used for code where the implementation is not available (framework code) and should be avoided if possible, because they can create name clashes and weird bugs. Categories should always be named like follows:

@interface UITableView (SMLoading)
@interface UITableView (SCLoading)

The files shall be named accordingly: UITableView+NGLoading.h/.m, UITableView+TVLoading.h/.m

Global C Functions

Functions should start with a capitalized Prefix and should follow the same naming scheme as classes (get shall be used for functions that return values opposed to method-getters).

SMGetGlobalFormatOfString(@"test");

Extern, Const, and Static

extern NSString *const kSMMyConstant;
extern NSString *myExternString;
static NSString *const kSMMyStaticConstant;
static NSString *staticString;

Notifications

Notification names should always follow the following naming scheme:

Class of Affected Object + Did/Will + Action + "Notification"

Example:

UIApplicationWillResignActiveNotification

Booleans

Boolean variables shouldn't be compared against YES and NO, since they already represent YES or NO. YES and NO is to use instead of true/false, TRUE/FALSE, 0/1, etc. Each other datatype used in a condition must be explicitly converted to a BOOL by using an equation, e.g.

  • if (pointer != nil) instead of if (pointer)
  • if (intValue == 0) instead of if (!intValue)
  • if (strcmp("test", myCString) == 0) instead of if (!strcmp("test",myCString))
  • ...

Enums

Enum values should always be prefixed with the name of the enum, following Apple's guidelines. For Bitmask-Type enums the shift-left operator (<<) should be used.

enum {
  Foo,
  Bar
};

typedef enum {
  SMLoadingViewStyleLight,
  SMLoadingViewStyleDark
} SMLoadingViewStyle;

typedef enum {
  SMHUDViewStyleLight = 7,
  SMHUDViewStyleDark = 12
} SMHUDViewStyle;

typedef enum {
  SMHUDViewTypeSingle = 1 << 0,    // For bitmasks
  SMHUDViewTypeDouble = 1 << 1,
  SMHUDViewTypeTriple = 1 << 2
} SMHUDViewType;

Objetive C Literals (LLVM 4.0 and up)

As soon as LLVM 4.0 is ready for production (Xcode 4.4 non-beta) the new Objective C literals should be used to keep the code concise and easy to read.

NSMutableDictionary *status = {@"llvm4IsAwesome" : @YES, @"javaIsAwesome" : @NO};
@status[@"excited"] = @YES; // instead of [status setValue:[NSNumber numberWithBool:YES] forKey:@"excited"];

Dynamic Typing

Is it preferred to tell the compiler the type of a variable, if possible. That means the use of bare id should be avoided, instead a specified protocol or class name should be used.

Singletons

Singletons should be avoided whenever possible since they create a global dependency and are considered bad practise. However there are of course appropriate uses of Singletons, but there should not be a general ApplicationManager or something similar that handles a lot of functionality (see Encapsulation).

Blocks/GCD

Blocks shall be heavily used and are preferred to delegate-methods in a lot of cases (but not all). GCD shall be used as a lightweight mechanism for parallelism, whenever cancellation of operations is needed NSOperation/NSOperationQueue should be used instead.

Asserts

Asserts shall be used to fail early and often. Known assertions shall be expressed in code to not only crash as early as possible if the assertion is violated but to also guide the developer and whoever reads the code.

+ (NSArray *)indexPathsFromRow:(NSInteger)startRow toRow:(NSInteger)endRow inSection:(NSInteger)section {
  NSAssert(endRow >= startRow, @"endRow must be greater or equal than startRow");
  NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:endRow-startRow+1];
  for (NSInteger row = startRow; row <= endRow; row++) {
    [indexPaths addObject:[NSIndexPath indexPathForRow:row inSection:section]];
  }
  return [indexPaths copy];
}

Info.plist

The info.plist should always be named Info.plist and not Project_Info.plist or anything else.

App Delegate

The application delegate, if its a separate class, should be named AppDelegate and not Project_AppDelegate or anything else.

Submodules, Third-Party Code

When using Third-Party Code available at Github it is recommended to fork the repository to the Suitmedia Github account to have a better control over the code. If the code isn't available on Github it is recommended to create a repository with the Suitmedia account. Third-Party Code shall not be copied from project to project but should be integrated as git submodules or fake git submodules.

Other Sources to look at

Example Code

MSCanteenMenuParser.h

#import "MSJSONParser.h"

@class MSCanteen;    // class-directive instead of import

@interface MSCanteenMenuParser : MSJSONParser // no iVar-section

// the designated initializer
- (id)initWithJSON:(id)JSON canteen:(MSCanteen *)canteen;

@end

MSCanteenMenuParser.m

#import "MSCanteenMenuParser.h"

#import "FKIncludes.h"
#import "MSCanteen.h" // import here to make known in implementation-file


// Private Class Extension for private methods, properties etc.
@interface MSCanteenMenuParser ()

@property (nonatomic, strong) MSCanteen *canteen;

@end


@implementation MSCanteenMenuParser

@synthesize canteen = _canteen;    // synthesize with leading "_"

#pragma mark - Lifecycle

- (id)initWithJSON:(id)JSON canteen:(MSCanteen *)canteen
{
  if (self = [super initWithJSON:JSON]) {
    _canteen = canteen;
  }  
  return self;
}

- (id)initWithJSON:(id)JSON
{
  // Usage of Assert
  NSAssert(NO, @"Cannot parse menu without known canteen");
  return [self initWithJSON:JSON canteen:nil];
}

#pragma mark - MSJSONParser

- (void)parseWithCompletion:(ms_network_block_t)completion
{
  // all menus of the given canteen
  NSArray *menus = [self.JSON valueForKey:FKConstant.JSON.CanteenMenu.RootNode];
  
  if (menus.count == 0) {
    [self callCompletion:completion statusCode:MSStatusCodeJSONError];
  }
  
  // format of long method call
  [FKPersistenceAction persistData:menus
                        entityName:[MSCanteenMenu entityName]
                   dictionaryIDKey:FKConstant.JSON.CanteenMenu.Name
                     databaseIDKey:[MSCanteenMenu uniqueKey]
                       updateBlock:^(MSCanteenMenu *canteenMenu, NSDictionary *data, NSManagedObjectContext *localContext)
  {
    canteenMenu.canteen = [self.canteen inContext:localContext];
    
    // parse the canteen menu itself
    [canteenMenu setFromDictionary:data];
    // parse the menu items
    [canteenMenu setItemsFromArray:[data valueForKey:FKConstant.JSON.CanteenMenu.Items]];
    
    [localContext save];
  } completion: ^{
    [self callCompletion:completion statusCode:MSStatusCodeSuccess];
  }];

@end

Attribution

This document is heaviliy based on Sam Soffes' Objective-C Coding Convention and Best Practices, thanks for making publicly available.

Based on NOUSguide's Objective-C Coding Convention and Best Practices

These guidelines build on Apple's existing Coding Guidelines for Cocoa. Unless explicitly contradicted below, assume that all of Apple's guidelines apply as well.

Whitespace

  • 2 spaces, not 4 spaces, or even tabs.
  • End files with a newline.
  • Make use of vertical whitespace to divide code into logical chunks.

Documentation and Organization

  • All method declarations should be documented.
  • Comments should be hard-wrapped at 80 characters.
  • Comments should be Tomdoc-style.
  • Document whether object parameters allow nil as a value.
  • Use #pragma marks to categorize methods into functional groupings and protocol implementations, following this general structure:
#pragma mark Properties

@dynamic someProperty;

- (void)setCustomProperty:(id)value
{
}

#pragma mark Lifecycle

+ (instancetype)objectWithThing:(id)thing
{
}

- (instancetype)init
{
}

#pragma mark Drawing

- (void)drawRect:(CGRect)
{
}

#pragma mark Another functional grouping

#pragma mark GHSuperclass

- (void)someOverriddenMethod
{
}

#pragma mark NSCopying

- (id)copyWithZone:(NSZone *)zone
{
}

#pragma mark NSObject

- (NSString *)description
{
}

Declarations

  • Never declare an ivar unless you need to change its type from its declared property.
  • Don’t use line breaks in method declarations.
  • Prefer exposing an immutable type for a property if it being mutable is an implementation detail. This is a valid reason to declare an ivar for a property.
  • Always declare memory-management semantics even on readonly properties.
  • Declare properties readonly if they are only set once in -init.
  • Don't use @synthesize unless the compiler requires it. Note that optional properties in protocols must be explicitly synthesized in order to exist.
  • Declare properties copy if they return immutable objects and aren't ever mutated in the implementation. strong should only be used when exposing a mutable object, or an object that does not conform to <NSCopying>.
  • Instance variables should be prefixed with an underscore (just like when implicitly synthesized).
  • Don't put a space between an object type and the protocol it conforms to.
@property (attributes) id<Protocol> object;
@property (nonatomic, strong) NSObject<Protocol> *object;
  • C function declarations should have no space before the opening parenthesis, and should be namespaced just like a class.
void GHAwesomeFunction(BOOL hasSomeArgs);
  • Constructors should generally return instancetype rather than id.
  • Prefer C99 struct initialiser syntax to helper functions (such as CGRectMake()).
  CGRect rect = { .origin.x = 3.0, .origin.y = 12.0, .size.width = 15.0, .size.height = 80.0 };

Expressions

  • Don't access an ivar unless you're in -init, -dealloc or a custom accessor.
  • Use object literals, boxed expressions, and subscripting over the older, grosser alternatives.
  • Comparisons should be explicit for everything except BOOLs.
  • Prefer positive comparisons to negative.
  • Long form ternary operators should be wrapped in parentheses and only used for assignment and arguments.
Blah *a = (stuff == thing ? foo : bar);
  • Short form, nil coalescing ternary operators should avoid parentheses.
Blah *b = thingThatCouldBeNil ?: defaultValue;
  • Separate binary operands with a single space, but unary operands and casts with none:
void *ptr = &value + 10 * 3;
NewType a = (NewType)b;

for (int i = 0; i < 10; i++) {
  doCoolThings();
}

Control Structures

  • All curly braces should begin on the same line as their associated statement. They should end on a new line.
  • Put a single space after keywords and before their parentheses.
  • Return and break early.
  • No spaces between parentheses and their contents.
// if (somethingIsBad) return;

if (something == nil) {
	// do stuff
} else {
	// do other stuff
}

Exceptions and Error Handling

  • Don't use exceptions for flow control.
  • Use exceptions only to indicate programmer error.
  • To indicate errors, use an NSError ** argument or send an error on a ReactiveCocoa signal.

Blocks

  • Blocks should have a space between their return type and name.
  • Block definitions should omit their return type when possible.
  • Block definitions should omit their arguments if they are void.
  • Parameters in block types should be named unless the block is initialized immediately.
void (^blockName1)(void) = ^{
  // do some things
};

id (^blockName2)(id) = ^ id (id args) {
  // do some things
};

Literals

  • Avoid making numbers a specific type unless necessary (for example, prefer 5 to 5.0, and 5.3 to 5.3f).
  • The contents of array and dictionary literals should have a space on both sides.
  • Dictionary literals should have no space between the key and the colon, and a single space between colon and value.
NSArray *theStuff = @[ @1, @2, @3 ];

NSDictionary *keyedStuff = @{ GHDidCreateStyleGuide: @YES };
  • Longer or more complex literals should be split over multiple lines (optionally with a terminating comma):
NSArray *theStuff = @[
  @"Got some long string objects in here.",
  [AndSomeModelObjects too],
  @"More strings."
];

NSDictionary *keyedStuff = @{
  @"this.key": @"corresponds to this value",
  @"otherKey": @"remoteData.payload",
  @"some": @"more",
  @"JSON": @"keys",
  @"and": @"stuff",
};

Categories

  • Categories should be named for the sort of functionality they provide. Don't create umbrella categories.
  • Category methods should always be prefixed.
  • If you need to expose private methods for subclasses or unit testing, create a class extension named Class+Private.

Based on Github's Coding conventions for Objective-C projects.

Introduction

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:

Table of Contents

Dot-Notation Syntax

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;

Spacing

  • Indent using 2 spaces. Never indent with tabs. Be sure to set this preference in Xcode.
  • Method braces and other braces (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 {
  //Do something else
}
  • There should be exactly one blank line between methods to aid in visual clarity and organization. Whitespace within methods should separate functionality, but often there should probably be new methods.
  • @synthesize and @dynamic should each be declared on new lines in the implementation.

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.

For example:

if (!error) {
  return success;
}

Not:

if (!error)
  return success;

or

if (!error) 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.

For example:

result = a > b ? x : y;

Not:

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

Error handling

When methods return 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).

Methods

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;

Variables

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;
}

Naming

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

Long, descriptive method and variable names are good.

For example:

UIButton *settingsButton;

Not

UIButton *setBut;

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;

Comments

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.

Block comments should generally be avoided, as code should be as self-documenting as possible, with only the need for intermittent, few-line explanations. This does not apply to those comments used to generate documentation.

init and dealloc

init methods should be structured like this:

- (instancetype)init
{
  self = [super init]; // or call the designated initalizer
  if (self) {
    // Custom initialization
  }

  return self;
}

Literals

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

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];

CGRect Functions

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

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.

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

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()

Example:

typedef NS_ENUM(NSInteger, NYTAdRequestState) {
    NYTAdRequestStateInactive,
    NYTAdRequestStateLoading
};

Bitmasks

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

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 (nonatomic, strong) GADBannerView *googleAdView;
@property (nonatomic, strong) ADBannerView *iAdView;
@property (nonatomic, strong) UIWebView *adXWebView;

@end

Image Naming

Image names should be named consistently to preserve organization and developer sanity. They should be named as one camel case string with a description of their purpose, followed by the un-prefixed name of the class or property they are customizing (if there is one), followed by a further description of color and/or placement, and finally their state.

For example:

  • RefreshBarButtonItem / RefreshBarButtonItem@2x and RefreshBarButtonItemSelected / RefreshBarButtonItemSelected@2x
  • ArticleNavigationBarWhite / ArticleNavigationBarWhite@2x and ArticleNavigationBarBlackSelected / ArticleNavigationBarBlackSelected@2x.

Images that are used for a similar purpose should be grouped in respective groups in an Images folder.

Booleans

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.

For example:

if (!someObject) {
}

Not:

if (someObject == nil) {
}

For a BOOL, here are two examples:

if (isAwesome)
if (![someObject boolValue])

Not:

if ([someObject boolValue] == NO)
if (isAwesome == YES) // Never do this.

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;

Text and example taken from the Cocoa Naming Guidelines.

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;
}

This will prevent possible and sometimes prolific crashes.

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.

When possible, always turn on "Treat Warnings as Errors" in the target's Build Settings and enable as many additional warnings as possible. If you need to ignore a specific warning, use Clang's pragma feature.

Other Objective-C Style Guides

If ours doesn't fit your tastes, have a look at some other style guides:

Based on NYTimes's Objective-C Style Guide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment