iOS コーディングガイドライン

当記事は、[Colorful Board Advent Calendar 2016](http://qiita.com/advent-calendar/2016/colorful-board)の18日目の記事になります。

今回は Swift のコーディングをする上でのガイドラインになります。
現プロジェクトの環境に合わせてガイドラインを設定しているので、Swift のバージョンは 2.3 を前提としています。
(また、現時点で定義できていないガイドラインもあるので、決まり次第随時更新していきたいと思います)

主に参考にしているガイドラインは以下になります。

* [Swift.org - API Design Guidelines](https://swift.org/documentation/api-design-guidelines/)
* [github/swift-style-guide](https://github.com/github/swift-style-guide)
* [SmartTechVentures/swift-style-guide](https://github.com/SmartTechVentures/swift-style-guide)
* [eure/swift-style-guide](https://github.com/eure/swift-style-guide)
* [Swiftコーディング規約@Wantedly  - Qiita](http://qiita.com/susieyy/items/f71435cc962e70d81b37)

## ホワイトスペース

* インデントは **スペース4つ** でインデント。タブは使用しない。
* ブラケットの前後に半角スペースを入れる。
* コンマの後ろに半角スペースを入れる。
* メソッドの戻り値の `->` の前後に半角スペースを入れる。
* 演算子の前後に半角スペースを入れる。

## 行

* 1行は、 **80文字以内** にするようにする。折り返す際は関連する行のインデントに合わせる。(何文字が妥当かはプロジェクト毎に決めてください)
* ブロックの間は1行空ける。

```swift
import UIKit

final class SampleViewController: UIViewController {

    override func viewDidLoad() {}

    override func viewWillAppear(animated: Bool) {}
}
```

### セミコロン

行末のセミコロンは使用しない。

## マジック[ナンバー|ワード]の禁止

記述した本人にしか意味が分からない数値や文字列の利用を避け、他の人にもわかりやすい命名や記述を心がける。

Preferred

```swift
let viewRectPadding = 10.0
let viewRect = CGRect(
    x: viewRectPadding,
    y: viewRectPadding,
    width: view.frame.width - (viewRectPadding * 2),
    height: view.frame.height - (viewRectPadding * 2)
)
```

Not Preferred

```swift
let viewRect = CGRect(
    x: 10.0,
    y: 10.0,
    width: view.frame.width - 10.0,
    height: view.frame.height - 10.0
)
```

### メソッド分割

`viewDidLoad` 等のメソッド内で設定等を直接記述せずに専用のメソッドを用意する。

Preferred

```swift
override func viewDidLoad() {
    super.viewDidLoad()

    configureAppearance()
    configureTableView()
}

func configureAppearance() {
    backgroundColor = .whiteColor()
}

func configureTableView() {
    let contentInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 20.0, right: 0.0)
    tableView.backgroundColor = .blackColor()
    tableView.contentInset = contentInset
}
```

Not Preferred

```swift
override func viewDidLoad() {
    super.viewDidLoad()

    backgroundColor = .whiteColor()
    let contentInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 20.0, right: 0.0)
    tableView.backgroundColor = .blackColor()
    tableView.contentInset = contentInset
}
```

## 命名規則

### クラス・構造体・列挙体・プロトコル・ジェネリクスの型パラメータ

プレフィックスなしの `UpperCamelCase` とする

```swift
class SampleClass {
}

enum SampleEnum {
}

struct SampleStruct {
}

protocol SampleProtocol {
}
```

### 変数・メソッド・enumのcase

`lowerCamelCase` とする

```swift
let sample: String = ""

func sample() {}

enum Sample {
    case hoge
}
```

### UIKitを継承する時の命名

```swift
class SampleViewController: UIViewController {
}

class SampleButton: UIButton {
}

class SampleCell: UITableViewCell {
}
```

## 変数, 定数定義

### 変数, 定数共通

型を省略して、特定の場合だけ型を記述するよりも、一律記述するほうが迷わなくてすむため、**型は省略せずに記述する**。
また、基本 `let` で宣言し、変更される可能性があるときのみ `var` で宣言する。

Preferred

```swift
let text: String = "Hoge Fuga"

let frame: CGRect = .zero

let device: UIDevice = UIDevice.currentDevice()
let device: UIDevice = .currentDevice() // こういう書き方も可
```

Not Preferred

```swift
let text = "Hoge Fuga"

let frame = CGRectZero

let device = UIDevice.currentDevice()
```

### 変数定義

空の配列、連想配列も上記と同様に型を記述する。

Preferred

```swift
var elements: [Int] = []

var namesOfIntegers: [Int: String] = []
```

Not Preferred

```swift
var elements = [Int]()

var namesOfIntegers = [Int: String]()
```

### 定数定義

グローバルスコープ、クラススコープでの定数はprefixに小文字の **k** を記述する。

```swift
let kAnimationDuration = 0.3
```

## final

定義をオーバーライドしないときは `final` を宣言する。

```swift
final class SampleViewController: UIViewController {
}

class Button: UIButton {
}

final class PrimaryButton: Button {
}
```

## 演算子

### インクリメント、デクリメント

Swift 3.0 への移行を想定して、使用しない。

## ファイル内の宣言順

プライベートメソッドやプロトコル等の実装はファイル内に `extension` を用いて記述する。
なお、宣言する順序は以下の通りとする。

```swift
import UIKit

// MARK: - Selector

private extension Selector {
}

// MARK: - Protocol

protocol SampleViewControllerDelegate {
}

// MARK: Class

final class SampleViewController: UIViewController {

    override func viewDidLoad() {}
}

// MARK: IBActions methods

extension SampleViewController {

    @IBAction func onCancelButton(sender: UIButton) {}
}

// MARK: UITextFieldDelegate methods

extension SampleViewController: UITextFieldDelegate {
}

// MARK: Private methods

private extension SampleViewController {
}
```

## リテラル

ソースコード内で極力リテラルを記述しない。
また、そのように記述できるような実装を心がける。
(定数を宣言する専用のファイル内にまとめる. 例: Constants.swift等)

Preferred

```swift
view.backgroundColor = .backgroundColor(.black)

let params: ApiParameters = ApiParameters().set(.Limit(10))
SampleApi.configure(.Get, params).request()
// => GET /sample?limit=10
```

Not Preferred

```swift
view.backgroundColor = UIColor(hex: 0xf1f1f1)

SampleApi.request("GET", {limit: 10})
```

## IBOutlet

Interface Builder で storyboard や xib とリンクした変数は必要ない限りは `private` にする。

Preferred

```swift
@IBOutlet private weak var view: UIView!
```

Not Preferred

```swift
@IBOutlet weak var view: UIView!
```

## コメント

```swift
//
//  Filename.swift
//  Project Name
//
//  Created by XXXX XXXX on YYYY/MM/DD.
//  Copyright © YYYY Organization Name. All rights reserved.
//

import UIKit

/**
 * SampleViewの説明
 */
final class SampleView: UIView {

    override func awakeFromNib() {
        super.awakeFromNib()

        foo("bar")
    }
}

// MARK: Private methods

private extension SampleView {

    /**
     * foo()の説明
     *
     * - parameter bar: barの説明
     */
    func foo(bar: String) {}
}
```

#### 参考

* [Does Swift have documentation comments or tools? - Stack Overflow](http://stackoverflow.com/questions/24047991/does-swift-have-documentation-comments-or-tools)
* [Swift Documentation - NSHipster](http://nshipster.com/swift-documentation/)

### 不要なコメントは残さない

ソースファイル内にメモ用途の記述は残さずに Github の Issues やその他チケット等に記載する。

## その他

* ベータ版や実績のないライブラリや、UIライブラリ等は検討してから導入する。
* デバッグ用の print() は残さない。
* ビルド時の警告は放置しない。
* Gitのコミットメッセージは、ネイティブ言語を使って丁寧に記載する。 (良くない例: modify, fix bug)