Stripe
Stripe

Objective-C Style Guide

Unify your team mobile development with Stripe's syle guide.

Objective-C Style Guide

Ground Rules

NSpacing

  1. Indent using 4 spaces. No tabs.
  2. Avoid starting methods with an empty line
  3. NThere should not be a need to use multiple consecutive empty lines
  4. Asterisks should be attached to the variable name NSString *text unless it's NSString * const Text

Variable Naming

  1. Lean towards clarity over compactness
  2. Avoid single letter variables. Try using idx / jdx instead of i / j in for loops.
  3. Acronyms should be all lowercase as a method prefix (ex:url or urlString). Otherwise, they should be all caps when occurring elsewhere in the method name, or as a class name (ex: handleStripeURLCallbackWithURL or STPAPIClient)
  4. Internal or private methods and ivars should begin with an _, e.g. - (void)_doPrivateStuff and id _internalVariable. This is not required for private properties which should not include an underscore (this is to distinguish them from their underlying variable which automatically has an _ prefix).

Control Flow

  1. Place else if and else on the same line as the preceding closing curly brace:
if (condition) {
// A
} else if (condition) {
// B
} else {
// C
}
  1. Always wrap conditional bodies with curly braces
  2. Each return statement should be on a separate line for ease of debugging. i.e. do NOT write if (condition) return YES;
  3. Use ternary operators sparingly and for simple conditions only:
type = isCard ? @"card" : @"unknown";

type = dictionary[@"type"] ?: @"default";
  1. switch statements for enums should contain an entry for each value and avoid using default

Documentation

  1. Document using the multi-line syntax in all cases with the content aligned with the first asterisk:
/** This is a one line description for a simple method */
- (void)title;

/** This is a multi-line description for a complicated method @param @see https://... */
- (void)title;
  1. Header documentation should wrap lines to 80 characters

Literals

  1. Use literals to create immutable instances of NSStringNSDictionaryNSArrayNSNumber:
NSArray *brands = @[@"visa", @"mastercard", @"discover"];

NSDictionary *parameters = @{
@"currency": @"usd",
@"amount": @1000,
};
  1. Dictionary colons should be attached to the key
  2. Align multi-line literals using default Xcode indentation

Constants

  1. Use static constants whenever appropriate. Names should start with a capital letter:
static NSString * const HTTPMethodGET = @"GET";

static const CGFloat ButtonHeight = 100.0;
  1. Any public static constants should be prefixed with STP:
static NSString * const STPSDKVersion = @"11.0.0";

Folders

  1. We use flat folder structure on disk with some exceptions
  2. Save files to the appropriate root level folder. Typical folders include:
  3. stripe-ios/Stripe/
  4. stripe-ios/Tests/Tests/
  5. stripe-ios/Example/Basic Integration/Basic Integration/
  6. stripe-ios/Example/Non-Card Payment Examples/Non-Card Payment Examples/
  7. Save public header files in stripe-ios/Stripe/PublicHeaders/ for Cocoapods and Swift Package Manager compatibility

Design Patterns

Imports

  1. Ordering for imports in headers
  2. Import system frameworks
  3. Import superclasses and protocols sorted alphabetically
  4. Use @class for everything else
#import <Foundation/Foundation.h>

#import "STPAPIResponseDecodable.h"
#import "STPBankAccountParams.h"@class STPAddress, @STPToken;
  1. Ordering for imports in implementations
  2. Import system frameworks
  3. Import corresponding headers
  4. Import everything else sorted alphabetically
#import <PassKit/PassKit.h>

#import "STPSource.h"
#import "STPSource+Private.h"

#import "NSDictionary+Stripe.h"
#import "STPSourceOwner.h"
#import "STPSourceReceiver.h"
#import "STPSourceRedirect.h"
#import "STPSourceVerification.h"

Interfaces and Protocols

  1. Stick to Xcode default spacing for interfaces, categories, and protocols
  2. Always define NS_ASSUME_NON_NULL_BEGIN / NS_ASSUME_NON_NULL_END in headers. NS_ASSUME_NON_NULL_BEGIN / NS_ASSUME_NON_NULL_END should also be used in implementation (.m) files
NS_ASSUME_NON_NULL_BEGIN

@protocol STPSourceProtocol <NSObject>

// ...@end// ...@interface STPSource : NSObject<STPAPIResponseDecodable, STPSourceProtocol>

// ...@end// ...@interface STPSource () <STPInternalAPIResponseDecodable>

// ...@end

NS_ASSUME_NON_NULL_END
  1. Category methods on certain classes need to be prefixed with stp_ to avoid collision:
// NSDictionary+Stripe.h

@interface NSDictionary (Stripe)

- (NSDictionary *)stp_jsonDictionary;

@end
  1. Define private properties and methods as class extensions inside the implementation. Ex: STPSource.m.
  2. Define internal properties and methods as class extensions inside a +Private.h file. Ex: STPSource+Private.h.
  3. Access private properties and methods from test classes by defining a class extension inside the test implementation:
// STPBankAccountTest.m

@interface STPBankAccount ()

+ (STPBankAccountStatus)statusFromString:(NSString *)string;
+ (NSString *)stringFromStatus:(STPBankAccountStatus)status;

@end

@interface STPBankAccountTest : XCTestCase

@end

@implementation STPBankAccountTest

// ...

@end

Properties

  1. Properties should be defined using this syntax:
@property (<nonatomic / atomic>, <weak / copy / _>, <nullable / _>, <readonly / _>) <class> *<name>;

@property (<nonatomic / atomic>, <readonly / _>) <type> <name>;
  1. Omit default properties (assignreadwritestrong)
  2. Use copy for classes with mutable counterparts such as NSStringNSArrayNSDictionary
  3. Leverage auto property synthesis whenever possible
  4. Declare @synthesize and @dynamic on separate lines for shorter diffs
  5. Use properties (self.foo) instead of their corresponding instance variables (_foo). Instance variables should only be accessed directly in initializer methods (initinitWithCoder:, etc…), dealloc methods, and within custom getters and setters. For more information, see Apple’s docs on using accessor methods in initializer methods and dealloc..

Init

- (instancetype)init {
self = [super init];
if (self) {
// ...
}
return self;
}

Methods

  1. If a method takes more than three arguments, each argument should be on a separate line.
  2. See Coding Guidelines for Cocoa - Naming Methods

Implementation

  1. Do not use #define to define a block of code -- #define code is very difficult to debug
  2. Use #pragma mark - <text> and #pragma mark <text> to group methods In large implementation files:
#pragma mark - Button Handlers

#pragma mark - UITableViewDataSource

#pragma mark - UITableViewDelegate
Unify your team mobile development with Stripe's syle guide.
Related examples
Google
Google
Code Review Developer Guide
Slack
Slack
How About Code Reviews?