Permalink
Find file
cbfda77 Feb 9, 2017
342 lines (251 sloc) 8.48 KB

Swiftコーディング規約

はじめに

本文書は、CookpadにおけるSwiftコードのスタイル基準を定めるものである。

また、本文書はSwift3.0の言語仕様に準拠している。

目的

このスタイルガイドは以下のような目標を達成するために策定されている。

  1. 意図を明確にする
  2. 冗長さを排除し、可読性を高める
  3. 一貫性を持たせる
  4. 副作用を減らす

コードスタイル

  • [MUST] インデントにはソフトタブを使い、幅は4スペースにすること
  • [MUST] ファイル終端は改行すること
  • [MUST] オープンブラケットは同じ行に記述すること
// Bad
func update() 
{
}

// Good
func update() {
}
  • [MUST] 配列、辞書を複数行で定義する場合、最後の要素にも,を付けること
    • 修正時の差分を最小にするため
// Bad
let ingredients = [
    "bacon": "100g",
    "lettuce": "1/4",
    "tomato": "half"
]

// Good
let ingredients = [
    "bacon": "100g",
    "lettuce": "1/4",
    "tomato": "half",
]
  • [MUST] 条件式全体を囲う()は使わないこと
// Bad
if (a == 0) {
}

// Good
if a == 0 {
}
  • [MUST] 行末に;を付けないこと

クラス・構造体

  • [MUST] Objective-Cからの参照が必要な場合を除きNSObjectを継承しないこと
  • [SHOULD] mutatingな構造体は利用を避けること
    • 一般的に構造体は不変であることが期待されるため

メソッド

  • [MUST] スコープが最も狭くなるように適切なアクセスレベルを設定すること
  • [MUST] 戻り値がVoidの場合、記述を省略すること
// Bad
func update() -> Void {
}

// Good
func update() {
}

クロージャー

  • [MUST] 引数のクロージャーが1つだけの場合、Trailing Closureを利用すること
// Bad
client.search(for: "Sushi", completion: { recipes in
})

// Good
client.search(for: "Sushi") { recipes in
}
  • [MUST] 引数がクロージャー1つだけの場合、()を省略すること
// Bad
let titles = recipes.map() { $0.title }

// Good
let titles = recipes.map { $0.title }
  • [MUST] クロージャーの定義が1行だけの場合はshorthand argumentを利用すること。逆に複数行にわたる場合は利用を避けること
// Bad
let titles = recipes.map { recipe in recipe.title }

// Good
let titles = recipes.map { $0.title }
// Bad
client.search(for: "Sushi") {
    if $1 != nil {
        recipes = $0
    }
}

// Good
client.search(for: "Sushi") { results, error in
    if error != nil {
        recipes = results
    }
}
  • [MUST] 引数としてブロックを受け取るとき、@escapingは必要な場合のみ使用すること
  • [MUST] unownedによる変数キャプチャは避け、weakを使うこと
    • 適切に使用した場合はパフォーマンス改善に繋がるが、判断が難しくリスクを伴うため

プロパティ、変数

  • [MUST] 変更が不要な値はletを用いて定数にすること
  • [SHOULD] 可能な場合は型宣言を省略すること。ただし、型推論がコンパイル速度に影響を及ぼす場合はその限りではない

プロパティ

  • [MUST] 最もスコープが狭くなるようにアクセスレベルを設定すること
  • [MUST] 外部から読み込めるが、書き込ませたくないプロパティにはprivate(set)を明示すること
  • [MUST] プロパティの属性としてweakを使える場所では使うこと
    • @IBOutletやデリゲートなど、循環参照が発生しうる箇所に付与すること
  • [MUST] プロパティの属性としてunownedの使用は避けること
    • 適切に使用した場合はパフォーマンス改善に繋がるが、判断が難しくリスクを伴うため
// Bad
var delegate: FooDelegate
@IBOutlet var textLabel: UILabel!

// Good
weak var delegate: FooDelegate
@IBOutlet weak var textLabel: UILabel!
  • [MUST] Computed Propertyについて、省略可能なときgetを省略すること
// Bad
var foodCount: Int {
    get { return foods.count }
}

// Good
var foodCount: Int { 
    return foods.count 
}
  • [SHOULD] 副作用を伴う場合、willSet, didSetに記述すること

変数

  • [MUST] グローバル変数として定義しないこと

  • [MUST] Array<T>, Dictionary<K, V>の表記は避け、シンタックスシュガーを使うこと
// Bad
let seasonings: Array<String> = ["sugar", "salt", "vinegar", "soy sauce", "miso"]
let ingredients: Dictionary<String, String> = [
    "bacon": "100g",
    "lettuce": "1/4",
    "tomato": "half",
]

// Good
let seasonings: [String] = ["sugar", "salt", "vinegar", "soy sauce", "miso"]
let ingredients: [String: String] = [
    "bacon": "100g",
    "lettuce": "1/4",
    "tomato": "half",
]
  • [SHOULD] コールバックなど、複雑なブロック型を扱う場合にはtypealiasを使うこと
// Good
typealias RecipeClientCompletionBlock = (Result<[Recipe], APIError>) -> Void
  • [SHOULD] NSプレフィックスのないブリッジングが提供されている際はそちらを用いること
    • ただし、提供されていないメソッドを用いる必要がある場合などはその限りではない
// Bad
let url = NSURL(string: "https://cookpad.com/")

// Good
let url = URL(string: "https://cookpad.com/")

Enum

  • [MUST] 値はlowerCamelCaseで命名すること
  • [MUST] 型名が省略可能なときは省略すること
// Bad
user.status = UserStatus.guest

// Good
user.status = .guest

オプショナル

  • [MUST] Optional<T>の表記は避け、シンタックスシュガー(T?)を使うこと
  • [MUST] 非オプショナル型を利用できる場合はそちらを使うこと
  • [MUST] guardはオプショナル・バインディングを伴う場合のみに使い、条件式として利用しないこと
// Bad
guard isLoggedIn else {
    return
}

// Good
if !isLoggedIn {
    return
}
// Good
guard let user = manager.currentUser {
    return
}
label.text = user.name

例外

  • [MUST] NSExceptionを使用しないこと
  • [SHOULD] do ~ catchを用いて、try!の使用は避けること

命名

  • [MUST] Swift API Design Guidelineに従うこと
  • [MUST] 利用しない戻り値、引数は_にすること
  • [MUST] ベンダープレフィックスを付与しないこと
    • ただし、NSObjectの継承や既存クラスのextensionなどを用いてObjective-Cからも利用可能になる場合は付けること
  • [MUST] 定数の命名も変数に従うこと
    • UpperCamelCase、SNAKE_CASEを用いたり、kプレフィックスやベンダープレフィックスを付与してはならない
  • [MUST] 非ASCII文字を名前に使用しないこと
  • [SHOULD] 階層構造を示す命名は避け、ネストで表現すること
// Bad
enum RecipeType {
    case none
}

class Recipe {
}

// Good
class Recipe {
    enum Type {
        case none
    }
}

構文

  • [MUST] selfは常に省略すること。ただし、同名の変数名から割り当てるなど、selfを明示する必要がある場合はその限りではない

  • [MUST] 将来的に廃止が予定されている構文を使わないこと

Cocoa

  • [MUST] ファクトリメソッドではなく、コンストラクタを使うこと
// Bad
let rect = CGRectMake(10.0, 20.0, 30.0, 40.0)

// Good
let rect = CGRect(x: 10.0, y: 20.0, width: 30.0, height: 40.0)
  • [MUST] 代替可能な場合は、クラスメソッドではなく、コンストラクタを使うこと
// Bad
let url = NSURL.fileURL(withPath: "/foo/bar")

// Good
let url = URL(fileURLWithPath: "/foo/bar")
  • [MUST] 関数ではなく、プロパティやメソッドを使うこと
// Bad
let width = CGRectGetWidth(rect)

// Good
let width = rect.width