Compilation of Objective-C Style Guides
-
-
Save SiriusDely/ef4d4136cff478c1c248 to your computer and use it in GitHub Desktop.
- Purpose
- Xcode Project Settings
- Code Style
- Code Layout
- Version Control
- The Boyscout Rule
- Source
- Credits
- Footnote
- Revision History
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.
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.
All new projects use ARC. The compiler is better placed to insert boilerplate code.
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.
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);
}
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.
Use the most up to date compiler possible and all the associated benefits, Literals, ARC, auto-synthesis, the static analyser, auto-layout, storyboards, etc.
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.
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.
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.
Don't fear long descriptive names, Xcode has auto-completion.
Follow the conventions in Apple's Coding Guidelines for Cocoa
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.
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;
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.
If there is a method that has no implementation, or is just a call to super
then it should be removed.
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
.
Use YES
and NO
not true, false, 1, -1 or anything like that.
Don't compare BOOL
returns against BOOL
s:
// GOOD
if ([self aMethodReturningBOOL]) {
// THE DEVIL'S WORK
if ([self aMethodReturningBOOL] == YES) {
Use CGFloat
instead of float
.
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.
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;
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
};
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.
Group methods into related groups and section them up with #pragma descriptions.
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.
- 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.
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.
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.
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.
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.
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.
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.
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");
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 (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 (something < somethingElse) {
// Do something
}
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.
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 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.
If you feel like creating your own, you can grab the latest markdown for this page from Github.
The basis for these standards were taken from
- Zarra Studios' ZDS Code Style Guide
- NOUSguide's Coding-Conventions
Last updated June 5, 2014
Full history available on GitHub.
Set Xcode project class prefix to "SM", stands for "Suitmedia Mobile".
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])];
/*!
* 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.
.h: extern NSString *const kSMClassNamePublicConstantString
.m: NSString *const kSMClassNamePublicConstantString = @”My Constant String”;
.m: NSString *const kSMClassNamePrivateConstantString = @”My Private String”;
static UIInteger sMyStaticVar;
#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.
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
>
Noun describing view followed by past tense verb describing event. Optionally take sender or gesture recognizer if it’s used.
buttonTapped
cellSwipedRightWithGestureRecognizer:
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.
Always use the dot syntax for setters and getters. Never use it for other methods.
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).
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)
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
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.
These coding guidelines shall be applied for all new iOS and Mac projects, converting old projects is possible but not recommended in general.
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 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
The Clang static analyzer should be used early and often, as it can detect various code smells and helps finding those.
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/
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.
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.
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];
- (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.
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
}
There should be a space after the control structure (i.e. if
, else
, etc).
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.
label.frame = (inPseudoEditMode) ? kLabelIndentedRect : kLabelRect;
Use the ternary operator only for assignments with simple conditions that probably won't be expanded. (readability & expandability)
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 (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 (something < somethingElse) {
// Do something
}
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.
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.
@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 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.
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.
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.
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.
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.
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];
Any method that is empty or just calls super should be removed from the source code as they are redundant.
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
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 NSString *const kSMMyConstant;
extern NSString *myExternString;
static NSString *const kSMMyStaticConstant;
static NSString *staticString;
Notification names should always follow the following naming scheme:
Class of Affected Object + Did/Will + Action + "Notification"
Example:
UIApplicationWillResignActiveNotification
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))
- ...
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;
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"];
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 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 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 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];
}
The info.plist should always be named Info.plist and not Project_Info.plist or anything else.
The application delegate, if its a separate class, should be named AppDelegate and not Project_AppDelegate or anything else.
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.
- Apple HIG: http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/MobileHIG/Introduction/Introduction.html
- The Objective-C programming language: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Introduction/introObjectiveC.html
- http://stackoverflow.com/questions/155964/what-are-best-practices-that-you-use-when-writing-objective-c-and-cocoa
- http://cocoadevcentral.com/articles/000082.php
- http://cocoadevcentral.com/articles/000083.php
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
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.
- 2 spaces, not 4 spaces, or even tabs.
- End files with a newline.
- Make use of vertical whitespace to divide code into logical chunks.
- 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 mark
s 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
{
}
- 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 thanid
. - 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 };
- 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
BOOL
s. - 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();
}
- 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
}
- 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 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
};
- Avoid making numbers a specific type unless necessary (for example, prefer
5
to5.0
, and5.3
to5.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 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.
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:
- The Objective-C Programming Language
- Cocoa Fundamentals Guide
- Coding Guidelines for Cocoa
- iOS App Programming Guide
- Dot-Notation Syntax
- Spacing
- Conditionals
- Error handling
- Methods
- Variables
- Naming
- Comments
- Init & Dealloc
- Literals
- CGRect Functions
- Constants
- Enumerated Types
- Bitmasks
- Private Properties
- Image Naming
- Booleans
- Singletons
- Xcode Project
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;
- 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.
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;
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;
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).
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 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;
}
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;
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
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. 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];
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
When using enum
s, it is recommended to use the new fixed underlying type specification because it has stronger type checking and code completion. The SDK now includes a macro to facilitate and encourage use of fixed underlying types — NS_ENUM()
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 (nonatomic, strong) GADBannerView *googleAdView;
@property (nonatomic, strong) ADBannerView *iAdView;
@property (nonatomic, strong) UIWebView *adXWebView;
@end
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
andRefreshBarButtonItemSelected
/RefreshBarButtonItemSelected@2x
ArticleNavigationBarWhite
/ArticleNavigationBarWhite@2x
andArticleNavigationBarBlackSelected
/ArticleNavigationBarBlackSelected@2x
.
Images that are used for a similar purpose should be grouped in respective groups in an Images folder.
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.
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.
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.
If ours doesn't fit your tastes, have a look at some other style guides:
Based on NYTimes's Objective-C Style Guide.