iOS
Swift

Swift 2.2 で導入された#selectorをViewControllerごとに一元管理して可読性を上げよう

More than 1 year has passed since last update.

Swift2.1以前と2.2以降の比較

Swift2.1以前の書き方

ViewController.swift
let button = UIButton(type: .System)
button.addTarget(self, action: Selector("buttonTapped:"), forControlEvents: .TouchUpInside)

func buttonTapped(sender: UIButton) {
    //Button Tapped
}

セレクターは文字列を指定して生成していたのでtypoしていてもコンパイル時に気づくことができなかった

Swift2.2の書き方

#selectorが導入されたことで下記のような書き方に変わった

ViewController.swift
let button = UIButton(type: .System)
button.addTarget(self, action: #selector(ViewController.buttonTapped(_:)), forControlEvents: .TouchUpInside)

func buttonTapped(sender: UIButton) {
    //Button Tapped
}

これによりtypoしてもエラーが出るので安全になった

課題

  • Swift2.1以前の時よりも1行のコード量が増える
//Swift2.1以前
button.addTarget(self, action: Selector(buttonTapped:), forControlEvents: .TouchUpInside)

//Swift2.2以降
button.addTarget(self, action: #selector(ViewController.buttonTapped(_:)), forControlEvents: .TouchUpInside)
  • 大きなViewControlerになると色々なイベントのアクションとしてセレクターを使うけれど、セレクターがViewControllerの様々な箇所に散らばりがち
  • 複数から同じセレクターの処理を使う時にどうやってまとめるべきか

要は可読性をもっと良くしたいよね

解決方法

ViewControllerごとにSelector構造体をprivate拡張してセレクターのタイププロパティを生やしてあげる

ViewController.swift
private extension Selector {
    static let buttonTapped = #selector(ViewController.buttonTapped(_:))
}

呼び出し側は下記のようになる

ViewController.swift
let button = UIButton(type: .System)
button.addTarget(self, action: .buttonTapped, forControlEvents: .TouchUpInside)
  • button.addTarget(self, action: #selector(ViewController.buttonTapped(_:)), forControlEvents: .TouchUpInside)と書いている場合よりずっと簡潔に書ける
  • ViewControllerが大きくなってセレクターを使ったイベント処理が増えた時も、セレクターの生成はSelector構造体にタイププロパティを拡張して増やすことにより、セレクター生成処理のコードが散らばらないし、セレクターの修正とかしたい時も1箇所にまとまっているから簡単にできるし可読性が上がる気がする

まとめ

ViewControllerごとにセレクターを一元管理することで可読性があがるよね

というようなことが ここ に書いてあり、良いと思ったので共有です