Most of this is inspired by (and shamelessly stolen from) The Official raywenderlich.com Swift Style Guide and The Sport Ngin Swift Style Guide. For anything this guide doesn't cover, please refer to Apple's Coding Guidelines for Cocoa and Apple's Swift Programming Guide
camelCase instead of snake_case
let MaximumWidgetCount = 100
class WidgetContainer {
var widgetButton: UIButton
let widgetHeightPercentage = 0.85
}
enum Directions {
case North, East, South, West
}
func dateFromString(dateString: NSString) -> NSDate
dateFromString("2014-03-14")
func convertPointAt(column: Int, row: Int) -> CGPoint
convertPointAt(column: 42, row: 13)
To keep code clear and concise, avoid using self to access properties or invoke methods. The exception to this is when inside a closure, or when differentiating between property names and arguments in initializers. The compiler will complain when self is not used inside of a closure.
class History {
var events: [Event]
init(events: [Event]) {
self.events = events
}
var whenVictorious: (() -> ()) {
return {
self.rewrite()
}
}
func rewrite() {
events = []
}
}//bad
let width: NSNumber = 120.0
//good
let width = 120.0
var submitButton: UIButton
let capitals = [Sweden: Stockholm]
//bad
let capitals: Dictionary<Country, City>
//good
let capitals: [Country: City]
Prefer constant declarations with let over variable declarations with var wherever possible. Only use var when you know the value might change.
It may help to define everything with let and change it to var when the compiler complains.
//This button needs to be a var because it is instantiated from the storyboard, and may change
@IBOutlet weak var submitButton: UIButton!
//The view height should be constant, so it is defined with let
let viewHeight = view.frame.size.height
//Because UILabel represents a class object, it's properties can be modified even if the object itself is defined with let
let label = UILabel()
label.text = "Hello World!"? where a nil value is acceptable
! only for instance variables that you know will be initialized later before use, such as subviews that will be set up in viewDidLoad, variables that will be setup in an init method, or @IBOutlets that will be initialized by the storyboard.
optionalString or maybeView because there optional-ness is already in the type declaration
Guard statements can also be used to unwrap optionals.
var textContainer: TextContainerView?
//Optional chaining
textContainer?.textLabel?.setNeedsDisplay()
//Optional binding
if let textContainer = textContainer {
// do many things with textContainer
}
//Optional binding with a guard
guard let textContainer = textContainer else { return }/**
Description of what the method does
- parameter paramaterName: Description of the parameter
- returns: Description of what the method returns
*/
func someMethod(parameterName: AnyType?) -> returnValueName: AnyType? {
...
}
///Description of some function (notice the three slashes)
func someFunction() {
}
Use //MARK:s to categorize similar methods into functional groupings and protocol implementations
Notice that there are 0 blank lines after a //MARK: declaration, 2 blank lines before the //MARK:, and 1 blank line between methods.
A hyphen after the MARK: will cause Xcode to draw a seperator line in the jump bar. Use this to seperate very different sections of code, like property declarations, from view methods.
Xcode also supports TODO: and FIXME landmarks to annotate your code in the jump bar.
class SomeViewController: UIViewController, UITableViewDatasource, UITableViewDelegate {
//MARK: - Properties
@IBOutlet weak var tableView: UITableView!
let cells = ["first cell", "second cell"]
//MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
//TODO: Finish this method
}
//MARK: Table View Datasource
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cells.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell
//FIXME
cell.textLabel?.text = cell[indexPath.row]
return cell
}
//MARK: Table View Delegate
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("selected cell at index: \(indexPath.row)")
}
}if/else/for/switch/while condition
if/else/for/switch/while conditions. Only add them for complicated conditions when it adds clarity. Long conditions can also be split on to multiple lines for clarity.
if/else/for/switch/while etc. always open on the same line as the statement, and close on a new line
//MARK: or a /** */ or /// documentation comment
if someMethod.isTrue {
}
if ((a + b + c) < (d + e + f)) {
}
for i in 0..<maxValue {
}
if let a = a,
b = b,
c = c,
where c != 0 {
}
//MARK: Functions (2 spaces)
func myFunction() {
}
func anotherFunction() { //(1 space)
//The label initialization functions are grouped together
let label = UILabel()
label.frame = CGRect(x: 0, y: 0, width: 100, height: 30)
label.text = "Text"
//The process of adding the label to the view is seperated by a blank line
let view = UIView
view.addSubview(label)
}
///Documented Function (2 spaces)
func documentedFunction() {
}Pods/ (if using CocoaPods)ProjectName/ProjectNameTests/ProjectName.xcodeproj/ProjectName.xcodeworkspace/ (if using CocoaPods)ProjectName/
Models/ (Contains all model classes)
ProjectName.xcdatamodeld (if using Core Data)RLMSupport.swift (if using Realm)Views/ (Contains .xib's and UI subclasses within a folder structure that mirrors the app navigation)Storyboards/ (Contains .storyboard's. For larger projects, the interface should be split up into multiple storyboards, eg. one for each tab on a tab bar. This will help organize the storyboard, and improve compile time.)Controllers/ (Contains view controllers within a folder structure that mirrors the app navigation)Base.lproj/ (if using localized strings)Utilities/ (Contains utility classes and singletons)Resources/
Fonts/Images/ (Contains .xcassets to group images for similar purposes. The bundle should be named like Home.xcassets and all internal images should be prefixed with Home_ and should be in camel case with an initial capital, eg. Home_Background. Assets should be split up into multiple bundles to improve compile time, and ease in using assets.Strings/ (Contains .plist's for localized stringsSupporting Files/ (AppDelegate, InfoPlist, ProjectName-Info.plist, ProjectName-Prefix.pch, Bridging-headers, etcFrameworks/ (Any added frameworks and 3rd party classes added to the project including all git submodules)Tests appended. eg: UtilsTests.swift
test, and the rest of the name should describe the method that is being tested, and what is being tested in camel case, eg. testValidateBlankUsername
import XCTest
class SomeClassTests: XCTestCase {
func testButtonPressed() {
class MockData: DataObject {
func overriddenFunc() {
XCTAssert(1 == 1, "1 does not equal 1")
}
}
}
}