Edited at
SwiftDay 3

Swiftを書く時に気をつける小さな違い

More than 3 years have passed since last update.


はじめに

Githubが公開しているSwiftコーディング規約をベースにコードを書いています。

この規約と方針が合わない場合は、参考にならないかもしれません。


Access Control

外部から値の取得・保存をするプロパティはpublicに、外部から単に情報を取得したい場合はpublic read onlyに、それ以外は出来るだけprivateにするのが良い設計だと思います。

【追記】ライブラリを作る方はpublicが必要となりますが、モジュール内で完結する際にはデフォルトのinternalで十分です。



  • gettersetterも公開するプロパティ

public var option: Option



  • getterのみ公開するプロパティ

public private(set) var view: CustomView


  • 非公開のプロパティ

@IBOutlet weak private var button: UIButton!


Class

継承させないクラスにはfinalを付けます。 逆にfinalが付いていないクラスは、継承出来てカスタマイズ可能だとみなします。

// 継承不可

final class Option {
}

// 継承可能
class MenuView: UIView {
}


Protocol

Protocol名はSwiftの標準ライブラリに合わせるようにしています。 状態を表すときは語尾にTypeを付けて、状態が変位可能な場合や、ある挙動を実行可能な場合は語尾にableを付けます。

public protocol IntegerType

public protocol CustomStringConvertible
public protocol Equatable


@available

有効なプラットフォームやOSを指定できます。 クラスやメソッドが廃止になった場合や、名前が変わった時に使用します。

@available(*, unavailable)

@available(iOS, introduced=7.0, deprecated=8.0, message="foo")
@available(iOS, obsoleted=8.0, renamed="bar")


Associated Values

switch文の中でassociated valuesを取り出すときは、inner letもしくはouter letどちらでも使えます。

associated valuesが単数ならinner let、複数ならouter letを使うようにしています。

// inner let

switch productBarcode {
case .UPCA(let numberSystem, let manufacturer, let product, let check):
print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")
case .QRCode(let productCode):
print("QR code: \(productCode).")
}

// outer let

switch productBarcode {
case let .UPCA(numberSystem, manufacturer, product, check):
print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")
case let .QRCode(productCode):
print("QR code: \(productCode).")
}


switch vs if case

switchはそれぞれの状態に対して適切な処理を行うときに使います。 if caseは特定の状態に対して処理を行うときに使います。

switch gender {

case .Male: break
case .Female: break
case .Other: break
}

if case .Other = gender {}

余談ですが下記はコンパイルエラーとなります。

if case .Other != gender {} // error

if case .Male = gender && case .Female = gender {} // error

【追記】上のコンパイルエラーの例で一部間違いがありました。

if case .Male = gender, .Female = gender {} // ok

上記のように書けば問題ないようです。


if vs guard

ifは条件によって処理を分岐させたいときに使います。 guardは特定の状態において処理を終了させたいときに使います。

if success {

print("success")
} else {
print("failure")
}

guard HKHealthStore.isHealthDataAvailable() else { return }
...


map vs forEach

元の配列を変形して返り値を使用する場合はmapを使います。 そうでなければforEachを使います。

let result = numbers.map { $0 * 2 }

views.forEach { $0.hidden = true }


for in vs forEach

for inは特定の状態の時に処理をスキップしたりループから抜けたい時や、インデックスを取得したい時に使用します。 forEachは単純なループ処理をしたい時に使います。

for (index, view) in views.enumerate() where index < 3 {

view.tag = index
views.append(view)
}

views.forEach { view in
// cannot use break or continue
// only exit from the current call
guard !view.hidden else { return }

view.backgroundColor = UIColor.whileColor()
}


まとめ

こういった細かい使い分けは、チーム内で共有してコーディング規約にするのも良いかもしれません。 人それぞれ自由な書き方で開発すると、収拾つかなくなる可能性があるからです。 ただし、目的や状況によっては上記の内容が必ずしも良いとは限らないので、都度修正していくのが良いと思いました。