Skip to content

Latest commit

 

History

History
212 lines (173 loc) · 7.84 KB

README.md

File metadata and controls

212 lines (173 loc) · 7.84 KB

iOS Guidelines

Project Structure

  • Organized .xcodeproj: files are grouped by layers (Presentation, Domain/Model, Infrastructure, etc), responsibilities (UI, Model, Logic, Vendor, etc), roles, etc. No flat structure allowed
  • Organized project folder: desirable but not required to keep file structure in sync with .xcodeproj
  • One .m / .swift == One class. No extra classes allowed
  • Localization is required even for the case, when you have only one language. Therefore adding extra language won't cause any difficulty in future
  • Images and other resources (.plist) should be grouped as well (i.e. $SRCROOT/Resources/Images/Common/, $SRCROOT/Resource/Assets/)

File Structure (.m)

  • Structure
imports

related constants

class extension

@implementation
- dealloc
- init methods
- other
@end
  • Method sections should be separated by #pragma mark - $sectionName

Braces, Asterisk

  • Curly braces are opened on the same line with code, closed - on a new line. For short blocks / closures a single line can be used
  • Asterisk placed near the name Type *var = ...;
  • Every code block should be wrapped by {}:
if (statement) {
    NSLog(@"%@", var);
}

The only exception is a short statement and an immediate return in the beginning:

- (void)performSomeTask {
    if (!user.hasToken) return;
    ...
}

Spaces, Formatting

General

  • Tab consists of 4 spaces
  • Max symbols in a row: 120
  • Use space after if, while, for and similar:
if (pointer != someOtherPointer)

CGFloat result = width * height * 2.f;

BOOL contentExists = self.content.length > 0;

for (int i = 0; i < x; i++) { ... }
  • Alignment: do not align code as ASCII-art.
  • Separate logical code blocks with 1 line wrap. Random number of \n is not allowed

Variable declaration

  • Between Type & Protocol there are no spaces: id<NSObject> object = ...;
  • Between var and casting parentheses there are no spaces: ClassType *a = (ClassType *)b;, ScalarType a = (ScalarType)b;

Class Declaration

  • If listing protocols goes beyond 120 characters, place each on a separate line:
@interface SCHCategoryViewController : UIViewController <UITableViewDelegate> 

@interface SCHCategoryViewController : UIViewController <
    UITableViewDelegate, 
    UITableViewDataSource, 
    SCHSyncronizationDelegate
>

Method Declaration

  • Use a single space between +/- and a returned type. Any additional spaces in arguments list are not allowed: - (void)doSomethingWithString:(NSString *)string flag:(BOOL)flag;
  • If the method goes beyond 120+10 characters, place each argument on a separate line, align by colon:
- (void)doSomethingWithString:(NSString *)string
                         rect:(CGRect)rectangle
                       length:(NSUInteger)length;

Method Invocation

  • Method invocation should use the same formatting as for declaration

Function Declaration

  • No space symbol between name and arguments parentheses
  • If the method goes beyond 120+10 characters, parentheses should be placed the same way, as opening/closing braces. Place each argument on a separate line aligned by 1 tab:
void * LongLongLongFunction(
    id firstArgument, 
    id secondArgument,
    id *outArgument,
    int other
)

NSAssert(
    [controller conformsToProtocol:@protocol(EParticipantSelection)],
    @"Controller %@ should confrom to protocol",
    NSStringFromProtocol(@protocol(EParticipantSelection))
);

Access specifiers & ivars

  • @public, @private, @protected should be aligned by 1 tab:
@interface MyClass : NSObject {
    @private
    id _privateIvar;

    @protected
    id _protectedIvar;
}
  • Access specifiers should be declared explicitly

Property Declaration

  • Between @property and opening parentheses 1 space symbol should be used
  • Parameters ordering: atomic/nonatomic, memory policy, access specified (readonly/readwrite), setter declaration, getter declaration: @property (nonatomic, copy, readonly, getter=customGetter) NSString *value;

Protocol Declaration

  • @required and @optional aligned to the line beginning

Blocks / Closures Declaration

  • Code inside block / closure aligned by 1 tab
  • Opening and closing braces should be aligned by the first symbol of declaration:
dipatch_async(dispatch_get_main_queue(), ^{
    // your code here
});

NSArray *contentArray = nil;
[contentArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
     // your code here
}];

[UIView animateWithDuration:0.3 animations:^{
    // your code here
} completion:^(BOOL finished){
     // your code here
}];
  • There is no space between ^ and return type / arguments: ^(id response){} ^NSUInteger * (id response){}

Naming

In general we're using Apple Coding Guidelines for Cocoa with several highlights:

  • Three-letters prefix is REQUIRED
  • Methods for the non-project class categories should be prefixed by lowercasePrefix_: - (void)sch_performTask:(id)arg

Constants

  • Magical numbers are not allowed - constants should be used instead. Exception: constant value, used in local one place with descriptive name
  • No "raw" strings. Exception: the same as for the magical numbers
  • Desirable to use extern keyword:
// SCHNotifier.h
OBJC_EXTERN NSString * const SCHNotifierDidChangeStateNotification;
OBJC_EXTERN const CGFloat SCHDefaultAnimationDuration;

// SCHNotifier.m

NSString * const SCHNotifierDidChangeStateNotification = @"com.project.notifier.stateDidChange";
const CGFloat SCHDefaultAnimationDuration = 0.33;

Required / recommended best practices

Types Declaration / Usage

  • Use typedef for blocks declaration and custom enums / scalar types
  • Use NSInteger, NSTimeInterval, CGFloat, etc over plain types. Therefore it is easier to migrate to the 64-bits

Forward Declaration

Use forward declaration whenever possible

Ivars

@public ivars not allowed

Properties

  • Use copy specifier in case mutability leads to unexpected behaviour (i.e. when NSMutableString passed as NSString and mutated outside of the class)
  • Use readonly for public properties whenever possible to prevent unexpected class usage (i.e. outlet assigned from client code, etc)
  • (iOS only) Use nonatomic whenever possible to speedup code execution
  • Desirable to access underlying property's ivar only in init/setter/getter
  • Use of @property solely to create _ivar is not recommended and should be prohibited

Exceptions handling

Use exceptions only where it is required. Desirable to use NSError ** and / or return result status (i.e. Success, Fail)

Protocols

  • Delegate methods should always pass sender argument:
@protocol CustomClassDelegate <NSObject>

- (NSInteger)someDelegateMethod:(CustomClass *)customClass;
- (void)customClass:(CustomClass *)customClass didSelectRowAtIndexPath:(NSIndexPath *)indexPath;

@end
  • Protocol support for properties / vars should be declared explicitly: id<Protocol> instance = ...;, @property (nonatomic, weak) id<Protocol> delegate;

Boolean statements

  • Desirable to use implicit equality check: if (bar && baz && quux)

Preprocessor usage

  • Prefer c-functions / constants / methods over #define

AppDelegate

Keep your AppDelegate as clean as possible. Any logic, not related to AppDelegate (database seeding, networking, etc) is not allowed

Clean .pch

Group required #defines, constants to a separate header (SCHConstants.h, SCHDefines.h). Garbage in .pch is not allowed