3. Objective-C Style Guide
Dot Notation Syntax
Dot notation is RECOMMENDED over bracket notation for getting and setting properties.
For example:
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;
Not:
[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;
Spacing
Method braces and other braces (if/else/switch/while etc.) MUST open on the same line as the statement. Braces MUST close on a new line.
For example:
if (user.isHappy) {
// Do something
} else {
// Do something else
}
Not:
if (user.isHappy)
{
// Do something
}
else
{
// Do something else
}
Conditionals
For example:
if (!error) {
return success;
}
Not:
if (!error)
return success;
or
if (!error) return success;
4. Objective-C Style Guide
Ternary Operator
The intent of the ternary operator, ? , is to increase clarity or code neatness. The ternary SHOULD only evaluate a single condition per
expression. Evaluating multiple conditions is usually more understandable as an if statement or refactored into named variables.
For example:
result = a > b ? x : y;
Not:
result = a > b ? x = c > d ? c : d : y;
Methods
In method signatures, there SHOULD be a space after the scope (- or + symbol). There SHOULD be a space between the method segments.
For example:
- (void)setExampleText:(NSString *)text image:(UIImage *)image;
Not:
+(void)openSignInVC;
Variables
Variables SHOULD be named descriptively, with the variable’s name clearly communicating what the variable is and pertinent information a
programmer needs to use that value properly.
For example:
• NSString *title: It is reasonable to assume a “title” is a string.
• NSString *titleHTML: This indicates a title that may contain HTML which needs parsing for display. “HTML” is needed for a
programmer to use this variable effectively.
Asterisks indicating a type is a pointer MUST be “attached to” the variable name. For example, NSString *text not NSString* text or
NSString * text, except in the case of constants (NSString * const NYTConstantString).
Variable Qualifiers
When it comes to the variable qualifiers introduced with ARC, the qualifier (__strong, __weak, __unsafe_unretained, __autoreleasing)
SHOULD be placed between the asterisks and the variable name, e.g., NSString * __weak text.
5. Objective-C Style Guide
Naming
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;
For example:
static const NSTimeInterval NYTArticleViewControllerNavigationFadeAnimationDuration = 0.3;
Not:
static const NSTimeInterval fadetime = 1.7;
Categories
Categories are RECOMMENDED to concisely segment functionality and should be named to describe that functionality.
For example:
@interface UIViewController (NYTMediaPlaying)
@interface NSString (NSStringEncodingDetection)
Not:
@interface NYTAdvertisement (private)
@interface NSString (NYTAdditions)
Methods and properties added in categories MUST be named with an app- or organization-specific prefix. This avoids unintentionally overriding an
existing method, and it reduces the chance of two categories from different libraries adding a method of the same name. (The Objective-C runtime
doesn’t specify which method will be called in the latter case, which can lead to unintended effects.)
For example:
@interface NSArray (NYTAccessors)
- (id)nyt_objectOrNilAtIndex:(NSUInteger)index;
@end
Not:
@interface NSArray (NYTAccessors)
- (id)objectOrNilAtIndex:(NSUInteger)index;
@end
6. Objective-C Style Guide
Comments
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.
No comment
Not:
// Start monitoring network connection
[Reachability defaultReachability];
Literals
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];
Constants
Constants are RECOMMENDED 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 MUST be declared as static constants. Constants MAY be declared as #define when 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
fileLogger.rollingFrequency = 60 * 60 * 24; // 24 hour rolling
7. Objective-C Style Guide
Booleans
Values MUST NOT be compared directly to YES, because YES is defined as 1, and a BOOL in Objective-C is a CHAR type that is 8 bits long
(so a value of 11111110 will return NO if compared to YES).
For a BOOL value:
if (isAwesome)
if (!someNumber.boolValue)
if (someNumber.boolValue == NO)
Not:
if (isAwesome == YES) // Never do this.
Imports
If there is more than one import statement, statements MUST be grouped together. Groups MAY be commented.
// Frameworks
@import QuartzCore;
// Models
#import "NYTUser.h"
// Views
#import "NYTButton.h"
#import “NYTUserView.h"
https://github.com/NYTimes/objective-c-style-guide
9. Swift Style Guide
Spacing
Preferred:
if user.isHappy {
// Do something
} else {
// Do something else
}
Not Preferred:
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 having too many sections in a method often means you should refactor into several methods.
override func viewDidLoad() {
super.viewDidLoad()
setup()
validate()
}
• Colons always have no space on the left and one space on the right. Exceptions are the ternary operator ? :, empty dictionary [:] and #selector
syntax for unnamed parameters (_:).
Preferred:
class TestDatabase: Database {
var data: [String: CGFloat] = ["A": 1.2, "B": 3.2]
}
Not Preferred:
class TestDatabase : Database {
var data :[String:CGFloat] = ["A" : 1.2, "B":3.2]
}
10. Swift Style Guide
Comments
When they are needed, use comments to explain why a particular piece of code does something. Comments must be kept up-to-date or
deleted.
Avoid block comments inline with code, as the code should be as self-documenting as possible. Exception: This does not apply to those
comments used to generate documentation.
Delegates
When creating custom delegate methods, an unnamed first parameter should be the delegate source. (UIKit contains numerous examples
of this.)
Preferred:
func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String)
func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool
Not Preferred:
func didSelectName(namePicker: NamePickerViewController, name: String)
func namePickerShouldReload() -> Bool
Use Type Inferred Context
Use compiler inferred context to write shorter, clear code. (Also see Type Inference.)
Preferred:
let selector = #selector(viewDidLoad)
view.backgroundColor = .red
let toView = context.view(forKey: .to)
let view = UIView(frame: .zero)
Not Preferred:
let selector = #selector(ViewController.viewDidLoad)
view.backgroundColor = UIColor.red
let toView = context.view(forKey: UITransitionContextViewKey.to)
let view = UIView(frame: CGRect.zero)
11. Swift Style Guide
Code Organization
Use extensions to organize your code into logical blocks of functionality. Each extension should be set off with a // MARK: - comment to
keep things well-organized.
Protocol Conformance
In particular, when adding protocol conformance to a model, prefer adding a separate extension for the protocol methods. This keeps the
related methods grouped together with the protocol and can simplify instructions to add a protocol to a class with its associated
methods.
Preferred:
class MyViewController: UIViewController {
// class stuff here
}
// MARK: - UITableViewDataSource
extension MyViewController: UITableViewDataSource {
// table view data source methods
}
// MARK: - UIScrollViewDelegate
extension MyViewController: UIScrollViewDelegate {
// scroll view delegate methods
}
Not Preferred:
class MyViewController: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
// all methods
}
12. Swift Style Guide
Unused Code
Unused (dead) code, including Xcode template code and placeholder comments should be removed. An exception is when your tutorial
or book instructs the user to use the commented code.
Aspirational methods not directly associated with the tutorial whose implementation simply calls the superclass should also be removed.
This includes any empty/unused UIApplicationDelegate methods.
Preferred:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Database.contacts.count
}
Not Preferred:
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return Database.contacts.count
}
Minimal Imports
Keep imports minimal. For example, don't import UIKit when importing Foundation will suffice.
13. Swift Style Guide
Classes and Structures
Remember, structs have value semantics.
Classes have reference semantics.
NOTE
Structures are always copied when they are passed around in your code, and do not use reference counting.
NOTE
However, Swift only performs an actual copy behind the scenes when it is absolutely necessary to do so. Swift manages all value copying
to ensure optimal performance, and you should not avoid assignment to try to preempt this optimization.
Use of Self
For conciseness, avoid using self since Swift does not require it to access an object's properties or invoke its methods.
Use self only when required by the compiler (in @escaping closures, or in initializers to disambiguate properties from arguments). In other
words, if it compiles without self then omit it.
Computed Properties
For conciseness, if a computed property is read-only, omit the get clause. The get clause is required only when a set clause is provided.
Preferred:
var diameter: Double {
return radius * 2
}
Not Preferred:
var diameter: Double {
get {
return radius * 2
}
}
14. Swift Style Guide
Final
Marking classes or members as final in tutorials can distract from the main topic and is not required. Nevertheless, use of final can sometimes clarify your intent and is worth the
cost. In the below example, Box has a particular purpose and customization in a derived class is not intended. Marking it final makes that clear.
// Turn any generic type into a reference type using this Box class.
final class Box<T> {
let value: T
init(_ value: T) {
self.value = value
}
}
Function Declarations
Keep short function declarations on one line including the opening brace:
func reticulateSplines(spline: [Double]) -> Bool {
// reticulate code goes here
}
For functions with long signatures, add line breaks at appropriate points and add an extra indent on subsequent lines:
func reticulateSplines(spline: [Double], adjustmentFactor: Double,
translateConstant: Int, comment: String) -> Bool {
// reticulate code goes here
}
Closure Expressions
Use trailing closure syntax only if there's a single closure expression parameter at the end of the argument list. Give the closure parameters descriptive names.
Preferred:
UIView.animate(withDuration: 1.0) {
self.myView.alpha = 0
}
UIView.animate(withDuration: 1.0, animations: {
self.myView.alpha = 0
}, completion: { finished in
self.myView.removeFromSuperview()
})
Not Preferred:
UIView.animate(withDuration: 1.0, animations: {
self.myView.alpha = 0
})
UIView.animate(withDuration: 1.0, animations: {
self.myView.alpha = 0
}) { f in
self.myView.removeFromSuperview()
}
15. Swift Style Guide
Constants
Type properties declared in this way are generally preferred over global constants because they are easier to distinguish from instance
properties. Example:
Preferred:
enum Math {
static let e = 2.718281828459045235360287
static let root2 = 1.41421356237309504880168872
}
let hypotenuse = side * Math.root2
Optionals
Preferred:
var subview: UIView?
var volume: Double?
// later on...
if let subview = subview, let volume = volume {
// do something with unwrapped subview and volume
}
Not Preferred:
var optionalSubview: UIView?
var volume: Double?
if let unwrappedSubview = optionalSubview {
if let realVolume = volume {
// do something with unwrappedSubview and realVolume
}
}
16. Swift Style Guide
Lazy Initialization
Consider using lazy initialization for finer grain control over object lifetime. This is especially true for UIViewController that loads views lazily. You can either use a
closure that is immediately called { }() or call a private factory method. Example:
lazy var locationManager: CLLocationManager = self.makeLocationManager()
private func makeLocationManager() -> CLLocationManager {
let manager = CLLocationManager()
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
manager.requestAlwaysAuthorization()
return manager
}
Type Inference
Prefer compact code and let the compiler infer the type for constants or variables of single instances. Type inference is also appropriate for small (non-empty) arrays
and dictionaries. When required, specify the specific type such as CGFloat or Int16.
Preferred:
let message = "Click the button"
let currentBounds = computeViewBounds()
var names = ["Mic", "Sam", "Christine"]
let maximumWidth: CGFloat = 106.5
Not Preferred:
let message: String = "Click the button"
let currentBounds: CGRect = computeViewBounds()
let names = [String]()
Syntactic Sugar
Prefer the shortcut versions of type declarations over the full generics syntax.
Preferred:
var deviceModels: [String]
var employees: [Int: String]
var faxNumber: Int?
Not Preferred:
var deviceModels: Array<String>
var employees: Dictionary<Int, String>
var faxNumber: Optional<Int>
17. Swift Style Guide
Memory Management
Code (even non-production, tutorial demo code) should not create reference cycles. Analyze your object graph and prevent strong cycles
with weak and unowned references. Alternatively, use value types (struct, enum) to prevent cycles altogether.
Extending object lifetime
Extend object lifetime using the [weak self] and guard let strongSelf = self else { return } idiom. [weak self] is preferred to
[unowned self] where it is not immediately obvious that self outlives the closure. Explicitly extending lifetime is preferred to optional
unwrapping.
Preferred
resource.request().onComplete { [weak self] response in
guard let strongSelf = self else {
return
}
let model = strongSelf.updateModel(response)
strongSelf.updateUI(model)
}
Not Preferred
// might crash if self is released before response returns
resource.request().onComplete { [unowned self] response in
let model = self.updateModel(response)
self.updateUI(model)
}
Not Preferred
// deallocate could happen between updating the model and updating UI
resource.request().onComplete { [weak self] response in
let model = self?.updateModel(response)
self?.updateUI(model)
}
18. Swift Style Guide
Golden Path
When coding with conditionals, the left-hand margin of the code should be the "golden" or "happy" path. That is, don't nest if statements. Multiple return statements are OK. The guard statement is built for this.
Preferred:
func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies {
guard let context = context else {
throw FFTError.noContext
}
guard let inputData = inputData else {
throw FFTError.noInputData
}
// use context and input to compute the frequencies
return frequencies
}
Not Preferred:
func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies {
if let context = context {
if let inputData = inputData {
// use context and input to compute the frequencies
return frequencies
} else {
throw FFTError.noInputData
}
} else {
throw FFTError.noContext
}
}
When multiple optionals are unwrapped either with guard or if let, minimize nesting by using the compound version when possible. Example:
Preferred:
guard let number1 = number1,
let number2 = number2,
let number3 = number3 else {
fatalError("impossible")
}
// do something with numbers
Not Preferred:
if let number1 = number1 {
if let number2 = number2 {
if let number3 = number3 {
// do something with numbers
} else {
fatalError("impossible")
}
} else {
fatalError("impossible")
}
} else {
fatalError("impossible")
}
Failing Guards
Guard statements are required to exit in some way. Generally, this should be simple one line statement such as return, throw, break, continue, and fatalError(). Large code blocks should be avoided. If cleanup code is required for
multiple exit points, consider using a defer block to avoid cleanup code duplication.
19. Swift Style Guide
Semicolons
Swift does not require a semicolon after each statement in your code. They are only required if you wish to combine multiple statements
on a single line.
Do not write multiple statements on a single line separated with semicolons.
Preferred:
let swift = "not a scripting language"
Not Preferred:
let swift = "not a scripting language”;
Parentheses
Parentheses around conditionals are not required and should be omitted.
Preferred:
if name == "Hello" {
print("World")
}
Not Preferred:
if (name == "Hello") {
print("World")
}
20. Swift Style Guide
Organization and Bundle Identifier
Where an Xcode project is involved, the organization should be set to CactusSoft/CactusSoft OOO and the Bundle Identifier set to
com.cactussoft.projectName where projectName is the name of the project.
Never use .dev, .qa, .etc suffixes in Bundle Identifier. Control it in. Build Settings for each configuration.
Swift 4
https://developer.apple.com/library/content/documentation/Swift
27. Patterns
xCode Templates
https://github.com/rambler-digital-solutions/Generamba
Generamba is a code generator made for working with Xcode. Primarily it is designed to generate VIPER modules but it is quite easy to
customize it for generation of any other classes (both in Objective-C and Swift).
https://github.com/ochococo/Design-Patterns-In-Swift
Swift 3.0 Design Patterns.
29. The Single Responsibility
Principle
class Handler {
func handle() {
let data = requestDataToAPI()
let array = parse(data: data)
saveToDB(array: array)
}
private func requestDataToAPI() -> Data {
// send API request and wait the response
}
private func parse(data: Data) -> [String] {
// parse the data and create the array
}
private func saveToDB(array: [String]) {
// save the array in a database
(CoreData/Realm/...)
}
}
class Handler {
let apiHandler: APIHandlerProtocol
let parseHandler: ParseHandlerProtocol
let dbHandler: DBHandlerProtocol
init(apiHandler: APIHandlerProtocol, parseHandler: ParseHandlerProtocol,
dbHandler: DBHandlerProtocol) {
self.apiHandler = apiHandler
self.parseHandler = parseHandler
self.dbHandler = dbHandler
}
func handle() {
let data = apiHandler.requestDataToAPI()
let array = parseHandler.parse(data: data)
dbHandler.saveToDB(array: array)
}
}
protocol APIHandlerProtocol {
func requestDataToAPI() -> Data
}
protocol ParseHandlerProtocol {
func parse(data: Data) -> [String]
}
protocol DBHandlerProtocol {
func saveToDB(array: [String])
}
30. The Open-Closed Principle
(OCP)
protocol CanShoot {
func shoot() -> String
}
// I'm a laser beam. I can shoot.
final class LaserBeam: CanShoot {
func shoot() -> String {
return "Ziiiiiip!"
}
}
// I have weapons and trust me I can fire them all at once. Boom! Boom! Boom!
final class WeaponsComposite {
let weapons: [CanShoot]
init(weapons: [CanShoot]) {
self.weapons = weapons
}
func shoot() -> [String] {
return weapons.map { $0.shoot() }
}
}
let laser = LaserBeam()
var weapons = WeaponsComposite(weapons: [laser])
weapons.shoot()
final class RocketLauncher: CanShoot {
func shoot() -> String {
return "Whoosh!"
}
}
let rocket = RocketLauncher()
weapons = WeaponsComposite(weapons: [laser, rocket])
weapons.shoot()
31. The Liskov Substitution
Principle (LSP)
protocol Polygon {
var area: Float { get }
}
class Rectangle: Polygon {
private let width: Float
private let length: Float
init(width: Float, length: Float) {
self.width = width
self.length = length
}
var area: Float {
return width * length
}
}
class Square: Polygon {
private let side: Float
init(side: Float) {
self.side = side
}
var area: Float {
return pow(side, 2)
}
}
// Client Method
func printArea(of polygon: Polygon) {
print(polygon.area)
}
// Usage
let rectangle = Rectangle(width: 2, length: 5)
printArea(of: rectangle) // 10
let square = Square(side: 2)
printArea(of: square) // 4
let requestKey: String = "NSURLRequestKey"
// I'm a NSError subclass. I provide additional functionality but don't mess with
original ones.
class RequestError: NSError {
var request: NSURLRequest? {
return self.userInfo[requestKey] as? NSURLRequest
}
}
// I fail to fetch data and will return RequestError.
func fetchData(request: NSURLRequest) -> (data: NSData?, error: RequestError?) {
let userInfo: [String:Any] = [requestKey : request]
return (nil, RequestError(domain:"DOMAIN", code:0, userInfo: userInfo))
}
// I don't know what RequestError is and will fail and return a NSError.
func willReturnObjectOrError() -> (object: AnyObject?, error: NSError?) {
let request = NSURLRequest()
let result = fetchData(request: request)
return (result.data, result.error)
}
let result = willReturnObjectOrError()
// Ok. This is a perfect NSError instance from my perspective.
let error: Int? = result.error?.code
// But hey! What's that? It's also a RequestError! Nice!
if let requestError = result.error as? RequestError {
requestError.request
}
FUNCTIONS THAT USE POINTERS OR REFERENCES TO BASE CLASSES MUST BE ABLE TO USE OBJECTS OF DERIVED CLASSES WITHOUT
KNOWING IT.
32. The Interface Segregation
Principle (ISP)
protocol TapProtocol {
func didTap()
}
protocol DoubleTapProtocol {
func didDoubleTap()
}
protocol LongPressProtocol {
func didLongPress()
}
class SuperButton: TapProtocol, DoubleTapProtocol, LongPressProtocol {
func didTap() {
// send tap action
}
func didDoubleTap() {
// send double tap action
}
func didLongPress() {
// send long press action
}
}
class PoorButton: TapProtocol {
func didTap() {
// send tap action
}
}
CLIENTS SHOULD NOT BE FORCED TO DEPEND UPON INTERFACES THAT THEY DO NOT USE.
33. The Dependency Inversion
Principle (DIP)
class Handler {
let storage: Storage
init(storage: Storage) {
self.storage = storage
}
func handle(string: String) {
storage.save(string: string)
}
}
protocol Storage {
func save(string: String)
}
class FilesystemManager: Storage {
func save(string: String) {
// Open a file in read-mode
// Save the string in this file
// Close the file
}
}
class DatabaseManager: Storage {
func save(string: String) {
// Connect to the database
// Execute the query to save the string in a table
// Close the connection
}
}
A. HIGH LEVEL MODULES SHOULD NOT DEPEND UPON LOW LEVEL MODULES. BOTH SHOULD DEPEND UPON ABSTRACTIONS.
B. ABSTRACTIONS SHOULD NOT DEPEND UPON DETAILS. DETAILS SHOULD DEPEND UPON ABSTRACTIONS.
35. Project Setup
Project Structure
To keep all those hundreds of source files from ending up in the same directory, it's a good idea to set up some folder structure depending
on your architecture. For instance, you can use the following:
├─ Models
├─ Views
├─ Controllers (or ViewModels, if your architecture is MVVM)
├─ Stores
├─ Extensions
├─ Utils
├─ Helpers
├─ etc…
Localization
Keep all user strings in localization files right from the beginning. This is good not only for translations, but also for finding user-facing text
quickly. You can add a launch argument to your build scheme to launch the app in a certain language, e.g.
-AppleLanguages (Finnish)
For more complex translations such as plural forms that depending on a number of items (e.g. "1 person" vs. "3 people"), you should use
the .stringsdict format instead of a regular localizable.strings file.
Constants
Keep your constants' scope as small as possible. For instance, when you only need it inside a class, it should live in that class. Those
constants that need to be truly app-wide should be kept in one place. In Swift, you can use structs defined in a Constants.swift file to
group, store and access your app-wide constants in a clean way:
struct Config {
static let baseURL = NSURL(string: "http://www.example.org/")!
static let splineReticulatorName = "foobar"
}
struct Color {
static let primaryColor = UIColor(red: 0.22, green: 0.58, blue: 0.29, alpha: 1.0)
static let secondaryColor = UIColor.lightGrayColor()
}
36. Project Setup
Project Structure
To keep all those hundreds of source files from ending up in the same directory, it's a good idea to set up some folder structure depending
on your architecture. For instance, you can use the following:
├─ Models
├─ Views
├─ Controllers (or ViewModels, if your architecture is MVVM)
├─ Stores
├─ Extensions
├─ Utils
├─ Helpers
├─ etc…
Localization
Keep all user strings in localization files right from the beginning. This is good not only for translations, but also for finding user-facing text
quickly. You can add a launch argument to your build scheme to launch the app in a certain language, e.g.
-AppleLanguages (Finnish)
For more complex translations such as plural forms that depending on a number of items (e.g. "1 person" vs. "3 people"), you should use
the .stringsdict format instead of a regular localizable.strings file.
Constants
Keep your constants' scope as small as possible. For instance, when you only need it inside a class, it should live in that class. Those
constants that need to be truly app-wide should be kept in one place. In Swift, you can use structs defined in a Constants.swift file to
group, store and access your app-wide constants in a clean way:
struct Config {
static let baseURL = NSURL(string: "http://www.example.org/")!
static let splineReticulatorName = "foobar"
}
struct Color {
static let primaryColor = UIColor(red: 0.22, green: 0.58, blue: 0.29, alpha: 1.0)
static let secondaryColor = UIColor.lightGrayColor()
}
37. Project Setup
Minimum iOS Version Requirement
It’s useful to make an early decision on the minimum iOS version you want to support in your project: knowing which OS versions you need to develop
and test against, and which system APIs you can rely on, helps you estimate your workload, and enables you to determine what’s possible and what’s
not.
Use these resources to gather the data necessary for making this choice:
• Official “first-party” resources:
◦ Apple’s world-wide iOS version penetration statistics: The primary public source for version penetration stats. Prefer more localized and
domain-specific statistics, if available.
Common Libraries
Generally speaking, make it a conscious decision to add an external dependency to your project. Sure, this one neat library solves your problem now, but
maybe later gets stuck in maintenance limbo, with the next OS version that breaks everything being just around the corner. Another scenario is that a
feature only achievable with external libraries suddenly becomes part of the official APIs. In a well-designed codebase, switching out the implementation
is a small effort that pays off quickly. Always consider solving the problem using Apple's extensive (and mostly excellent) frameworks first!
# UI Components
pod ‘JTCalendar’
pod 'DZNEmptyDataSet'
pod 'ActionSheetPicker-3.0'
pod 'MBProgressHUD'
pod 'PasswordTextField'
pod 'MaterialControls'
pod 'MGSwipeTableCell'
pod 'Floaty'
pod 'JDStatusBarNotification'
pod 'Magnetic'
38. Common Libraries
# UI Customization
pod 'PureLayout'
pod 'SnapKit'
pod ‘UIColor_Hex_Swift'
pod ‘SwiftHEXColors’
# Rate App
pod 'Armchair'
pod ‘iRate'
# Camera / Photo
pod 'MHVideoPhotoGallery'
pod 'LLSimpleCamera'
# Bug Tracking
pod 'Fabric'
pod 'Crashlytics'
# Analytics
pod 'GoogleAnalytics'
pod 'GoogleSignIn'
pod 'Appsee'
pod 'Taplytics', '2.17.6'
pod 'FBSDKCoreKit'
pod 'Localytics'
pod 'NewRelicAgent'
pod 'KochavaTrackeriOS'
pod 'Mixpanel-swift'
pod 'Branch'
# Reactive
pod 'RxSwift'
pod 'RxCocoa'
# Charts
pod 'Charts'
pod 'ChartsRealm'
39. Common Libraries
# Network
pod 'Moya'
pod 'Alamofire'
pod 'SwiftyJSON'
pod ‘SDWebImage'
pod ‘KFSwiftImageLoader’
# Cloud
pod 'Dropbox-iOS-Dropins-SDK'
pod 'box-ios-browse-sdk'
pod 'GoogleAPIClientForREST/Drive'
pod 'GTMOAuth2'
# Address Book Data
pod 'APAddressBook'
pod 'libPhoneNumber-iOS'
# Communication With Customers
pod 'Intercom'
# AB Testing
pod 'Firebase/Core'
pod 'Firebase/DynamicLinks'
pod 'Firebase/Performance'
pod 'GoogleTagManager'
# Debug
pod 'Dotzu'
# Data Storage
pod ‘RealmSwift'
pod ‘KeychainAccess'
# Common
pod ‘Localize-Swift’
pod ‘DateTools’
# Billing
pod ‘SwiftyStoreKit'
41. CI and CD
Setting up a continuous integration and delivery process has become critical nowadays as it helps you to
squash out bugs early in the development cycle and saves a lot of developer time.
Continuous Integration (CI) is a development practice that requires developers to integrate code into a
shared repository several times a day. Each check-in is then verified by an automated build, allowing
teams to detect problems early.
There are a lot of tools available that can help you with continuous integration of iOS apps like Xcode
Server, Jenkins and Travis CI.
Continuous Delivery (CD) is a software engineering approach in which teams produce software in short
cycles, ensuring that the software can be reliably released at any time. It aims at building, testing, and
releasing software faster and more frequently.
Why use Continuous Delivery?
1.Save days of preparing app submission, uploading screenshots and releasing the app
2.Colleague on vacation and a critical bugfix needs to be released? Don’t rely on one person releasing
updates
3. Increase software quality and reaction time with more frequent and smaller releases