Skip to content

Latest commit

 

History

History
525 lines (392 loc) · 10.5 KB

swift-guideline.md

File metadata and controls

525 lines (392 loc) · 10.5 KB

Mobillium Swift Style Guide

Requirements

  1. Install SwiftLint by using CocoaPods.
  2. Add .swiftlint.yml files to under Project folder. .swiftlint.yml file located inside of repo.

Naming

Variable name and function name should start with a lowercase character. SwiftLint: identifier_name

Preferred:

var abc = true
func abc() {}

Not Preferred:

var Abc = true
func Abc() {}

Type name and protocol name should start with an uppercase character. SwiftLint: type_name

Preferred:

class Abc {}
protocol Abc {}

Not Preferred:

class abc {}
protocol abc {}

Use compiler inferred context to write shorter, clear code.

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)

Include a hint about type in a name if it would otherwise be ambiguous.

Preferred:

@IBOutlet private weak var nameTextField: UITextField!
@IBOutlet private weak var confirmButton: UITextField!
let titleText: String
let cancelButton: UIButton

Not Preferred:

@IBOutlet private weak var name: UITextField!
@IBOutlet private weak var confirm: UITextField!
let title: String
let cancel: UIButton

Name booleans like isSpaceship, hasSpacesuit, etc. This makes it clear that they are booleans and not other types.

Preferred:

var isPassed = true
var hasPhoneNumber = false

Not Preferred:

var passed = true
var phoneNumber = false

Use US English spelling to match Apple's API.

Preferred:

let color = "red"

Not Preferred:

let colour = "red"

Avoid Objective-C-style acronym prefixes. This is no longer needed to avoid naming conflicts in Swift.

Preferred:

class Account {
 // ...
}

Not Preferred:

class MyAccount {
 // ...
}

Patterns

Prefer using guard at the beginning of a scope.

Why?

It's easier to reason about a block of code when all guard statements are grouped together at the top rather than intermixed with business logic.

Prefer immutable values whenever possible. Use map and compactMap instead of appending to a new collection. Use filter instead of removing elements from a mutable collection.˜

Access control should be at the strictest level possible. Prefer public to open and private to fileprivate unless you need that behavior.

Avoid global functions whenever possible. Prefer methods within type definitions.

Preferred:

class Person {
  var bornAt: TimeInterval

  var age: Int {
    // ...
  }

  func jump() {
    // ...
  }
}

Not Preferred:

func age(of person, bornAt timeInterval) -> Int {
  // ...
}

func jump(person: Person) {
  // ...
}

Use Swift's automatic enum values unless they map to an external source. Add a comment explaining why explicit values are defined. SwiftLint: type_name

Preferred:

enum ErrorType: String {
  case error
  case warning
}

enum Planet: Int {
  case mercury
  case venus
  case earth
  case mars
  case jupiter
  case saturn
  case uranus
  case neptune
}

Not Preferred:

enum ErrorType: String {
  case error = "error"
  case warning = "warning"
}

enum Planet: Int {
  case mercury = 0
  case venus = 1
  case earth = 2
  case mars = 3
  case jupiter = 4
  case saturn = 5
  case uranus = 6
  case neptune = 7
}

Default type methods to static.

Why?

If a method needs to be overridden, the author should opt into that functionality by using the class keyword instead.

Preferred:

class Fruit {
  static func eatFruits(_ fruits: [Fruit]) { ... }
}

Not Preferred:

class Fruit {
  class func eatFruits(_ fruits: [Fruit]) { ... }
}

Prefer immutable values whenever possible. Use map and compactMap instead of appending to a new collection. Use filter instead of removing elements from a mutable collection.

Why?

Mutable variables increase complexity, so try to keep them in as narrow a scope as possible.

Preferred:

var results = [SomeType]()
let results = input.map { transform($0) }
let results = input.compactMap { transformThatReturnsAnOptional($0) }

Not Preferred:

var results = [SomeType]()

for element in input {
  let result = transform(element)
  results.append(result)
}

for element in input {
  if let result = transformThatReturnsAnOptional(element) {
    results.append(result)
  }
}

Never use the default case when switching over an enum.

Why?

Enumerating every case requires developers and reviewers have to consider the correctness of every switch statement when new cases are added.

Preferred:

switch anEnum {
case .a:
  // ...
case .b, .c:
  // ...
}

Not Preferred:

switch anEnum {
case .a:
  // ...
default:
  // ...
}

Default classes to final.

Why?

If a class needs to be overridden, the author should opt into that functionality by omitting the final keyword.

Preferred:

final class SettingsRepository {
  // ...
}

Not Preferred:

class SettingsRepository {
  // ...
}

Check for nil rather than using optional binding if you don't need to use the value. SwiftLint: type_name

Why?

Checking for nil makes it immediately clear what the intent of the statement is. Optional binding is less explicit.

Preferred:

var thing: String?

if thing != nil {
  // ...
}

Not Preferred:

var thing: String?

// WRONG
if let _ = thing {
  // ...
}

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, didSelect name: String)
func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool

Not Preferred:

func didSelectName(namePicker: NamePickerViewController, name: String)
func namePickerShouldReload() -> Bool

Protocol

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 ViewController: UIViewController {
  // class stuff here
}

// MARK: - UITableViewDelegate
extension UIViewController: UITableViewDelegate {
  // UITableViewDelegate methods
}

// MARK: - UICollectionViewDataSource
extension UIViewController: UICollectionViewDataSource {
  // UICollectionViewDataSource methods
}

Not Preferred:

class ViewController: UIViewController, UITableViewDelegate, UICollectionViewDataSource {
  // all methods
}

Spacing

if/else/switch/while etc. SwiftLint: opening_brace SwiftLint: statement_position

Preferred:

if passed {
  // ...
} else {
  // ...
}

Not Preferred:

if passed
{
  // ...
}
else {
  // ...
}

Colons should be next to the identifier when specifying a type and next to the key in dictionary literals. SwiftLint: colon

Preferred:

var abc: Bool = true
func abc(string: String) {}

Not Preferred:

var abc : Bool = true
var abc :Bool = true
func abc(string : String) {}
func abc(string :String) {}

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

Operators

Infix operators should have a single space on either side. Prefer parenthesis to visually group statements with many operators rather than varying widths of whitespace. This rule does not apply to range operators (e.g. 1...3) and postfix or prefix operators (e.g. guest? or -1). SwiftLint: colon

Preferred:

let foo = 1 + 2
let foo = 1 > 2
let foo = !false
let range = 1...3
let range = 1..<3

Not Preferred:

let foo = 1+2
let foo = 1   + 2
let foo = 1   +    2
let foo=bar
let range = 1 ..<  3

References