This style guide describes the coding conventions suggested by EPAM Mobile Competency Center for any Objective-C based project developed by EPAM.
Most of these guidelines match Apple's documentation and community-accepted best practices, however some guidelines are derived from personal preferences. This document aims to set a standard way of doing things so that everyone can do things the same way. If there is some rule you are not particularly fond of, it is encouraged to follow this rule anyway to stay consistent with everyone else.
This document might be used in the next two ways:
- Use this document "as is" to define coding guidelines on your project
- Use this document as a template or a checklist to define your own coding guidelines by overriding/removing/adding rules in the most suitable and appropriate way that fits your project needs and your personal preferences
Some rules in this style guide have reasoning to explain why we need a rule and why is it defined this way. But there are a lot of rules that are contractual by their nature, we just need to define these rules to keep our code consistent. The authors of this document are more concerned whether you have some coding guidelines on your project, than whether you use this particular document as your coding guidelines.
- Naming
- Project
- Files
- Classes
- Categories
- Methods
- Properties
- Variables
- Blocks
- Resources
- C Language
- Objective-C++
- Apple's Coding Guidelines for Cocoa covers most of the naming rules that need to be followed.
- Naming conventions related to Memory Management Rules should also be followed.
- Project prefix should be defined and always be used for class names and constants.
For example:
SFprefix stands for "Spark Framework".
- 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.
- 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.
- It is recommended to turn on "Documentation Comments" setting in your Xcode project settings, this will warn when the documentation’s variable names or return types don’t match the method signature.
- It is recommended to put 3rd party source files into a separate folder with a distinct name like "ThirdParty", so that 3rd party code do not mix up with the rest of the source files.
- It is forbidden to delete or modify any license related comments from 3rd party code.
- It is recommended to avoid any modifications in 3rd party code and keep it intact. If you absolutely need to modify some 3rd party code then add a comment to explain your changes. Comply with existing formatting in 3rd party code when you modify it.
- If you insert some third-party code snippet or method into you source file then add THIRDPARTY_START, THIRDPARTY_END or THIRDPARTY comments (see Comment Types section).
- There is no restriction on code line length. Xcode built-in support for wrapping lines works best for all resolutions. So you should not manually wrap long lines.
- Indent using 4 spaces instead of tabs. This is the default Xcode setting.
- Method braces and other braces (
if/else/switch/whileetc.) always open on the same line as the statement but close on a new line.
For example:
- (void)someMethod { //Do something }
- There should be exactly one blank line between methods in implementation file to aid in visual clarity and organization.
- Methods in implementation file should appear in the same order they are declared in header file. Private methods should be placed below all of the public methods.
- Whitespace should be avoided in blank lines. Blank line should only have new line symbol and no other invisible symbols.
- Avoid adding blank lines at the end of files.
- Implementation files should be no longer than 2000 lines. If a source file becomes very long it is hard to understand. Therefore long classes should usually be refactored into several individual classes that focus on a specific task.
- Imports follow the below pattern: own header import is the first followed by system framework imports. All other imports are separated by a newline, grouped together and ordered alphabetically:
#import "ThisClass.h" #import <dispatch/dispatch.h> #import "AnotherClass.h" #import "OtherClass.h"
- Use class/protocol forward declarations instead of imports in header files whenever possible to reduce compilation time.
#importObjective-C/Objective-C++ headers, and#includeC/C++ headers. Objective-C headers should not have#defineguards as they are already encapsulated in#import. Standard C and C++ headers without any Objective-C in them can expect to be included by ordinary C and C++ files. Since there is no#importin standard C or C++, such files will be included by#includein those cases. Using#includefor them in Objective-C source files as well means that these headers will always be included with the same semantics.
#pragma markdeclarations should be used in implementation file to distinguish protocol implementation methods. For example:#pragma mark - UITableViewDelegate methods#pragma markdeclarations should be used in implementation file to distinguish private methods. For example:#pragma mark - Private methods- Every
#pragma markdeclaration that describes a group of methods should have corresponding closing mark after the last method in the group. Closing mark should only have one dash and no other text:#pragma mark -. - There should be one newline before and after pragma marks.
The format that is used for code comments can be found here: appledoc, however there are a couple of guidelines that developers should follow to make code commenting consistent.
- All class, protocol, method and property declarations should be documented using appledoc's format. Only header files must be commented using appledoc's format. The reason behind this is that the documentation generator script - which issues build warning for undocumented members - checks only the header files.
- Any implementation file should start with copyright comment.
- Leave an empty line before a comment block. Leave one space after
//. - Always write comments before the line that is commented, do not add a comment to the end of the line.
- Xcode uses text-wrapping by default. Code comments look best on different monitors when they do not have line breaks. Please consider writing comments that are not broke into a fix width column but is allowed to flow in the entire width of the IDE's (eg. when the window is resized Xcode automatically wraps the long lines).
- Use neutral language. Do not write
this simple codeorthis small component- but instead write a neutral sentence likethe component is used in ...and do not speak unfavorably about development tools, competitors, employers or working conditions - Do explain the code, but don't repeat what it does.
- Do answer the why of the code rather than the how.
- Add information that the best comment to a workaround or some strange bug-fix is an issue to link in issue tracking system or some place where this issue were found (stack overflow etc).
- Your comment should never be shorter than a sentence, so two or three words will not do. Comments should start with a capital letter, and end the sentence with a dot. It would be even better for class or method comments to be a full-blown paragraph.
- Use only English for code comments.
- Use 3rd person (declarative descriptive) not 2nd person (imperative prescriptive):
Formats the paragraph, notFormat the paragraph - Method descriptions begin with a verb phrase:
Formats the text of this paragraph, notThis method is formatting the text of this paragraph - Use
thisinstead ofthewhen referring to an object created from the current class:Uses the toolkit for this component to …, notUses the toolkit for the component to …. - Source repository capabilities should be leveraged for storing source-code branches, so commented-out code is not allowed. If there is a real need to keep code as comments it should be clearly stated.
Every comment that you add should be either appledoc in header files or should start with a comment type description mark. Here is the list of possible comment type description marks:
- TODO(date_added,author) - todo item, describes something that needs to be done.
- DESNOTE(date_added,author) - designers note, explain workaround, unexpected code, design decisions, etc.
- THIRDPARTY_START(date_added,who_added,license_name_or_link,taken_from_link) and THIRDPARTY_END - delimits copy-pasted code snippet. Or use THIRDPARTY(date_added,who_added,license_name_or_link,taken_from_link) - to mark copy-pasted function.
-
Interface declaration follows the below example:
@interface NSString : NSObject <NSCopying, NSMutableCopying, NSCoding>
-
Omit the empty set of curly braces on interfaces that do not declare any instance variables.
- Category file name should follow the next pattern:
ClassName+CategoryName.handClassName+CategoryName.m. - If you add a category to a class without project prefix then the category name should have project prefix. If the class name already has project prefix the category name should not have project prefix. For example
NSObject+SFAttributes, butSFServiceProvider+Logging. - If you add a category to a class without project prefix then the category methods should have project prefix with underscore like
SF_to avoid method name collisions. - Categories should be named for the sort of functionality they provide. Don't create umbrella categories.
-
In method signatures, there should be a space after the scope (-/+) symbol. There should also be space between the method segments. For Example:
- (void)setExampleText:(NSString *)text image:(UIImage *)image;
-
Interface methods could only have maximum four parameters. Long parameter list are hard to understand and difficult to use. Therefore long list of parameters should usually be replaced with one parameter object with appropriate properties.
-
It is recommended for interface methods to only have one block type parameter (the last one). Invocation of a method with more than one block parameter may look very complex.
-
There should be one newline before and after block statements (if, for, while, etc.) and also method and function calls that ends with a block parameter type.
-
There should be one space around arithmetic operators, boolean operators, comparison operators and assignments.
-
There should be no spaces after "(" and "[", and before ")" and "]".
-
Use exactly one blank line within a method to separate functionality where necessary. However usually it is better to create another method for this purpose.
-
It is recommended to avoid multiple
returnstatements in one method. Multiple return statements might make it hard to understand execution flow of a method. However you may use guard conditions at the beginning of a method to return early. -
Prefer to use
instancetypeas method return type instead of genericidwhenever possible, that is whenever a method returns an instance of its own class. Here is a good article that explains why you should use instancetype instead of id. -
Method length should be no longer than 40 lines. It is preferable to keep method length below 30 lines. If a method becomes very long it is hard to understand. Therefore long methods should usually be refactored into several individual methods that focus on a specific task.
-
Do not declare private methods in class extension (anonymous category) or anywhere else. Only provide private method definition without any separate declaration.
- Comment and clearly identify your designated initializer. It is important for those who might be subclassing your class that the designated initializer be clearly identified. That way, they only need to subclass a single initializer (of potentially several) to guarantee their subclass' initializer is called. It also helps those debugging your class in the future understand the flow of initialization code if they need to step through it.
initmethods should be placed at the top of an implementation file before any other method implementation.- Don't initialize variables to
0ornilin the init method, it's redundant. All memory for a newly allocated object is initialized to0(except for isa), so don't clutter up the init method by re-initializing variables to0ornil. - Do not invoke the
NSObjectclass methodnew, nor override it in a subclass. Instead, useallocandinitmethods to instantiate retained objects. Modern Objective-C code explicitly callsallocandinitmethods to create and retain an object.newis a legacy method that comes from NeXT days and it is kept only for backward compatibility. As thenewclass method is rarely used, it makes searching for object allocation points and reviewing code for correct memory management more difficult. deallocmethod should be placed directly below theinitmethods of a class.deallocshould process instance variables in the same order the they were declared, so it is easier for a reviewer to verify.initmethods should be structured like this:
- (id)init { self = [super init]; // or call the designated initializer if (self) { // Custom initialization } return self; }
- Follow the official guide from Apple about Using Autorelease Pool Blocks.
- Conditional bodies should always use braces even when a conditional body could be written without braces (e.g., it is one line only), because absence of brackets may cause different types of programming 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;
elseandelse ifshould appear in the same line as closing brace. For example:
if (user.isHappy) { //Do something } else { //Do something else }
- There should be no assignment operator in if conditions.
- Do not use
nilchecks for crash prevention, only use it for logic flow of the application. Sending a message to anilobject is handled by the Objective-C runtime.
- Never compare something directly to
nil, sincenilresolves toNOit is unnecessary to compare it in conditions. For example:
if (!someObject) {
}Not:
if (someObject == nil) { }
- Never compare something directly to
YES, becauseYESis defined to 1 and aBOOLcan be up to 8 bits. This allows for more consistency across files and greater visual clarity. For example:
if (isAwesome) if (![someObject boolValue])
Not:
if ([someObject boolValue] == NO) if (isAwesome == YES) // Never do this.
- Prefer positive comparisons to negative since it improves code clarity.
- The Ternary operator, ? , should 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 boolean variables.
For example:
result = a > b ? x : y;Not:result = a > b ? x = c > d ? c : d : y;
- If there is a logging solution integrated an appropriate logging level (
info,debug,warning,error) should be chosen to reflect the severity of the log message. - Specify a distinct log message type appropriate for your component (for instance
SparkCore), so that your messages can be easily filtered out from a user's logs.
- To indicate errors, use an
NSError **method argument. - Don't use exceptions for flow control, use exceptions only to indicate programmer error such as out-of-bounds collection access, attempts to mutate immutable objects, sending an invalid message, etc. These sorts of errors should be taken care of when an application is being developed, rather than in production.
- Use NSAssert, not assert(), for consistency and since it does not require to include any header file.
- Always provide error message parameter describing the failure condition.
- Do not call functions with side effects in the condition parameter of NSAssert because the condition parameter is not evaluated when assertions are disabled.
- Assertions should be used to verify internal implementation invariants, like internal state before or after execution of some method, etc. If assertion fails it really means the logic of the program is broken and you can't recover from this.
- Properties should be camel-case with the leading word being lowercase. Order of property specifiers follow the below example:
@property (strong, nonatomic, readonly) NSArray *array;- Always declare memory-management semantics even on readonly properties.
- Declare properties readonly if they are only set once in
initmethod. - Declare properties
copyif they return immutable objects and aren't ever mutated in the implementation. - Synthesize should not be used if it is not redefining default synthesize behavior.
@synthesizeand@dynamicshould each be declared on new lines in the implementation. - Prefer exposing an immutable type for a property to promote encapsulation.
- Use
self.to access class' own public properties. If you directly access instance variable for a public property, this will break KVO.
- Dot-notation cannot be used with variables types as
id, so use bracket notation in this case. - Dot-notation should 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;
- Prefer to create and use instance variable directly instead of private property if you are not implementing custom setter/getter.
- Private properties should be declared in class extensions (anonymous categories) in the implementation file of a class. Named categories (such as
SFTPrivateorprivate) should never be used unless extending another class.
For example:
@interface ESDXMLParser () @property (strong, nonatomic) NSMutableArray *parserStack; @property (strong, nonatomic) ESDXMLElement *rootElement; @end
- Variables should be named as descriptively as possible. This will result in self-documented easy to understand code. Single letter variable names should be avoided except for counters in loops.
- The variable that you return from a method should also be named descriptively, its name should explain what the method return. Generic variable names like
result,retValshould be avoided. - There should be no instance variable declarations in header files. Instance variables belong to implementation details and should therefore be declared in class extension (anonymous categories) in implementation file.
- Instance variable name should be camel-case with the leading word being lowercase and a leading underscore.
- Asterisks indicating pointers belong with the variable, e.g.,
NSString *textnotNSString* textorNSString * text. - A local variable should be created as close as possible to the place where this variable is actually used.
- Avoid making numbers a specific type unless necessary (for example, prefer
5to5.0, and5.3to5.3f). NSString,NSDictionary,NSArray, andNSNumberliterals should be used whenever creating immutable instances of those objects. Pay special care thatnilvalues not be passed intoNSArrayandNSDictionaryliterals, 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];
- 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
staticconstants and not#defines, since it may appear that defined macro does not represent a constant (you never know until you look at macro itself). For example:
static NSString *const SFNetLogServiceType = @"_appalocalnetwork._tcp.";
Not:
#define SFNetLogServiceType @"_appalocalnetwork._tcp."
- Scope / lifetime specifiers should always stand before const specifier.
static const MyClass *const
- When you have several related constants, you should prefer to use enumeration type to define all this constants instead of defining all these constants separately.
- If the block is large, e.g. more than 15 lines, it is recommended to move it out-of-line into a local variable.
- Avoid strong reference cycles when capturing self.
- It is recommended to put all resources in asset catalogs.
- 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.
- C function declarations should start with a capital letter.
- C function declarations in header files should be prefixed with a project prefix For example:
void SFAwesomeFunction(BOOL hasSomeArgs);
- Objective-C++ implementation file should have
.mmextension
Style guides that were used to create this document: