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")
}
}
}
}