Objective-C Coding Standards

When In Doubt

If something isn't explicitly covered here you should generally refer to the NYTimes Objective-C Style Guide. If you can't find a definitive answer there check with your colleagues and codify the answer here.


Musts

Three-letter Prefix

All Objective-C projects should have a designated three-letter prefix, used on all classes (and any symbols exposed at the global level).

Code Styling

Header Styling

Implementation Styling

Method Styling

Categories

Categories are a fun way to glom methods onto existing classes. To avoid naming collisions always prefix your category methods with a lowercase three letter prefix. You should use the same prefix used throughout your project. For example:

- (nonnull UIFont *)rdc_boldFontOfSize:(CGFloat)pointSize

Note: This is optional when the category is on a completely internal class since you have control over method names. (If a category method has the same selector as a method in the class, it may not be clear which implementation is called.)

Blocks

Shoulds

Code Organization

Constants

Constants accessible outside an implementation file should be declared using FOUNDATION_EXPORT.

Example header:

FOUNDATION_EXPORT NSString *const XXXBaseURL;

Example implementation:

NSString *const XXXBaseURL = @"http://someapp-staging.herokuapp.com/api/";

Variable Qualifiers

When it comes to the variable qualifiers introduced with ARC, the qualifier (__strong, __weak, __unsafe_unretained, __autoreleasing, __block) should be placed between the asterisks and the variable name, e.g., NSString *__weak text.

Dot Syntax

Using dot syntax for properties is a must. Dot syntax should also be used for parameter-less getter-like methods, both instance and class. For example:

[NSUserDefaults.standardUserDefaults setBool:YES forKey:@"CacheDataAggressively"];

NSManagedObjectContext *backgroundContext = VOKCoreDataManager.sharedInstance.temporaryContext;

Do not use dot notation to invoke methods that return void.

// Do NOT do this.
VOKCoreDataManager.sharedInstance.managedObjectContext.saveMainContextAndWait;

Tips & Tricks

DRYer String Constants

C99 compilers, including Objective-C compilers, combine a run of adjacent string literals into a single string literal. This can be combined with judicious use of #define'd strings to reduce repetition in sets of string constants, such as API paths.

Consider:

static NSString *const APIPathCurrentUserFetch = @"v1/user/";
static NSString *const APIPathFacebookLoginRegister = @"v1/user/facebook";
static NSString *const APIPathSignOut = @"v1/user/logout";
static NSString *const APIPathTerms = @"v1/text/terms";
static NSString *const APIPathPrivacy = @"v1/text/privacy";
static NSString *const APIPathNotificationRegister = @"v1/notifications/token";
static NSString *const APIPathNotificationsPreferences = @"v1/notifications/preferences";

If the v1 at the beginning of those paths ever needed to change, it'd have to be changed in every single line.

Using #define to declare the building blocks, then put them together to make the static NSString *const values:

#define APIPathComponentBase @"v1/"
#define APIPathComponentUser APIPathComponentBase @"user/"
static NSString *const APIPathCurrentUserFetch = APIPathComponentUser;
static NSString *const APIPathFacebookLoginRegister = APIPathComponentUser @"facebook";
static NSString *const APIPathSignOut = APIPathComponentUser @"logout";
#define APIPathComponentText APIPathComponentBase @"text/"
static NSString *const APIPathTerms = APIPathComponentText @"terms";
static NSString *const APIPathPrivacy = APIPathComponentText @"privacy";
#define APIPathComponentNotifications APIPathComponentBase @"notifications/"
static NSString *const APIPathNotificationRegister = APIPathComponentNotifications @"token";
static NSString *const APIPathNotificationsPreferences = APIPathComponentNotifications @"preferences";

Now, v1 is only in one place. Changing it there changes it for every API path.

When the preprocessor runs, the #define'd constants are replaced with their string values so that, for example, APIPathNotificationsPreferences becomes:

static NSString *const APIPathNotificationsPreferences = @"v1/" @"notifications/" @"preferences";

The compiler then combines the adjacent string literals, so that APIPathNotificationsPreferences is equal to @"v1/notifications/preferences".

NSNull, NULL, Nil, and nil

Checking if something is nothing can be complicated. Remember that NSNull objects are not nil. NSNull is a class implementing the singleton pattern; [NSNull null] returns a singleton instance of that class. It should only be used to represent nil objects in collections. Essentially:

NSNull *null = [NSNull null];  // An actual object, not a 0 pointer.
Class Nil = (Class)0;
id nil = (id)0;
void *NULL = (void *)0;

When comparing NULL, Nil, nil, and NSNull remember the following statements are all YES:

[NSNull null] isEqual:[NSNull null]
[NSNull null] == [NSNull null]
[NSNull null] != nil
[NSNull null] != Nil
[NSNull null] != NULL

nil == Nil == NULL