はじめに
Swiftでメッセージを表示するには UIAlertController
というクラスを使います。
使い方が若干難しいので、Swift特有の書き方も交えつつ、リファクタリングしてみます。
環境
- Swift:4.2
処理の内容
ViewControllerにあるButtonをタップすると以下のメッセージを表示します。
リファクタリング前
リファクタリング前のViewControllerです。
おそらくSwiftに慣れている方からすれば、明らかに冗長だとわかると思います。
import Foundation
import UIKit
class ViewController: UIViewController {
// MARK: IBActions
@IBAction func didTapButton(_ sender: UIButton) {
let alert: UIAlertController = UIAlertController.init(title: "タイトル", message: "メッセージ",
preferredStyle: UIAlertController.Style.alert)
let cancelAction: UIAlertAction = UIAlertAction.init(title: "キャンセル", style: UIAlertAction.Style.cancel,
handler: { (UIAlertAction) in
print("キャンセルが選択されました。")
})
alert.addAction(cancelAction)
let okAction: UIAlertAction = UIAlertAction.init(title: "OK", style: UIAlertAction.Style.default,
handler: { (UIAlertAction) in
print("OKが選択されました。")
})
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
}
リファクタリングをしてみよう
さっそく1つずつリファクタリングしていきます。
①不要なインポートを削除する
UIKitをインポートする場合、Foundationをインポートする必要はありません。
- import Foundation
import UIKit
class ViewController: UIViewController {
// MARK: IBActions
@IBAction func didTapButton(_ sender: UIButton) {
let alert: UIAlertController = UIAlertController.init(title: "タイトル", message: "メッセージ",
preferredStyle: UIAlertController.Style.alert)
let cancelAction: UIAlertAction = UIAlertAction.init(title: "キャンセル", style: UIAlertAction.Style.cancel,
handler: { (UIAlertAction) in
print("キャンセルが選択されました。")
})
alert.addAction(cancelAction)
let okAction: UIAlertAction = UIAlertAction.init(title: "OK", style: UIAlertAction.Style.default,
handler: { (UIAlertAction) in
print("OKが選択されました。")
})
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
}
逆にAPIコールやDBアクセスの実装など、UIを伴わないクラスでUIKitをインポートするのは冗長なので、Foundationのみをインポートするといいです。
②イニシャライザをクラス名のみで呼び出す
Swiftではイニシャライザを {クラス名}.init()
でなく {クラス名}()
のみで呼び出せます。
import UIKit
class ViewController: UIViewController {
// MARK: IBActions
@IBAction func didTapButton(_ sender: UIButton) {
- let alert: UIAlertController = UIAlertController.init(title: "タイトル", message: "メッセージ",
+ let alert: UIAlertController = UIAlertController(title: "タイトル", message: "メッセージ",
preferredStyle: UIAlertController.Style.alert)
- let cancelAction: UIAlertAction = UIAlertAction.init(title: "キャンセル", style: UIAlertAction.Style.cancel,
+ let cancelAction: UIAlertAction = UIAlertAction(title: "キャンセル", style: UIAlertAction.Style.cancel,
handler: { (UIAlertAction) in
print("キャンセルが選択されました。")
})
alert.addAction(cancelAction)
- let okAction: UIAlertAction = UIAlertAction.init(title: "OK", style: UIAlertAction.Style.default,
+ let okAction: UIAlertAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default,
handler: { (UIAlertAction) in
print("OKが選択されました。")
})
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
}
③できる限り型推論する
右辺から型を推論できる場合、型アノテーションを省略することでコードがすっきりして可読性が上がります。
今回だと UIAlertController
と UIAlertAction
のインスタンス生成時にイニシャライザを呼び出していて型が明確になっているので、わざわざ型を指定する必要がありません。
import UIKit
class ViewController: UIViewController {
// MARK: IBActions
@IBAction func didTapButton(_ sender: UIButton) {
- let alert: UIAlertController = UIAlertController(title: "タイトル", message: "メッセージ",
+ let alert = UIAlertController(title: "タイトル", message: "メッセージ",
preferredStyle: UIAlertController.Style.alert)
- let cancelAction: UIAlertAction = UIAlertAction(title: "キャンセル", style: UIAlertAction.Style.cancel,
+ let cancelAction = UIAlertAction(title: "キャンセル", style: UIAlertAction.Style.cancel,
handler: { (UIAlertAction) in
print("キャンセルが選択されました。")
})
alert.addAction(cancelAction)
- let okAction: UIAlertAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default,
+ let okAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default,
handler: { (UIAlertAction) in
print("OKが選択されました。")
})
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
}
④列挙型は .
から書き始める
Swiftでは列挙型の型が明確な場合、型名を省略して .
から書き始めることができます。
今回だと UIAlertController
と UIAlertAction
のstyleは型が明確なので、型名を省略できます。
import UIKit
class ViewController: UIViewController {
// MARK: IBActions
@IBAction func didTapButton(_ sender: UIButton) {
let alert = UIAlertController(title: "タイトル", message: "メッセージ",
- preferredStyle: UIAlertController.Style.alert)
+ preferredStyle: .alert)
- let cancelAction = UIAlertAction(title: "キャンセル", style: UIAlertAction.Style.cancel,
+ let cancelAction = UIAlertAction(title: "キャンセル", style: .cancel,
handler: { (UIAlertAction) in
print("キャンセルが選択されました。")
})
alert.addAction(cancelAction)
- let okAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default,
+ let okAction = UIAlertAction(title: "OK", style: .default,
handler: { (UIAlertAction) in
print("OKが選択されました。")
})
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
}
⑤トレイリングクロージャを使う
メソッドの最後の引数がクロージャの場合、引数名を省略してカッコの外に出すことができます。
これを トレイリングクロージャ といいます。
可読性が上がるので積極的に取り入れるべきです。
今回だと UIAlertAction
のイニシャライザの最後の引数である handler
はクロージャを引数に取るので外に出せます。
import UIKit
class ViewController: UIViewController {
// MARK: IBActions
@IBAction func didTapButton(_ sender: UIButton) {
let alert = UIAlertController(title: "タイトル", message: "メッセージ", preferredStyle: .alert)
- let cancelAction = UIAlertAction(title: "キャンセル", style: .cancel,
- handler: { (UIAlertAction) in
- print("キャンセルが選択されました。")
- })
+ let cancelAction = UIAlertAction(title: "キャンセル", style: .cancel) { (UIAlertAction) in
+ print("キャンセルが選択されました。")
+ }
alert.addAction(cancelAction)
- let okAction = UIAlertAction(title: "OK", style: .default,
- handler: { (UIAlertAction) in
- print("OKが選択されました。")
- })
+ let okAction = UIAlertAction(title: "OK", style: .default) { (UIAlertAction) in
+ print("OKが選択されました。")
+ }
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
}
⑥クロージャで未使用の引数名を省略する
クロージャで使っていない引数は _
にして省略します。
省略しない場合、SwiftLintでは unused_closure_parameter
の警告となります。
今回だと (UIAlertAction)
は使っていないので省略します。
import UIKit
class ViewController: UIViewController {
// MARK: IBActions
@IBAction func didTapButton(_ sender: UIButton) {
let alert = UIAlertController(title: "タイトル", message: "メッセージ", preferredStyle: .alert)
- let cancelAction = UIAlertAction(title: "キャンセル", style: .cancel) { (UIAlertAction) in
+ let cancelAction = UIAlertAction(title: "キャンセル", style: .cancel) { _ in
print("キャンセルが選択されました。")
}
alert.addAction(cancelAction)
- let okAction = UIAlertAction(title: "OK", style: .default) { (UIAlertAction) in
+ let okAction = UIAlertAction(title: "OK", style: .default) { _ in
print("OKが選択されました。")
}
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
}
⑦引数が少ないオーバーロードメソッドを使う
オーバーロードされているメソッドの場合、できる限り引数が少ないメソッドを使うとコードがすっきりします。
今回だと present
メソッドの completion
に nil
を渡していますが、そもそも completion
の引数を取らないメソッドが用意されているので、そちらを使います。
import UIKit
class ViewController: UIViewController {
// MARK: IBActions
@IBAction func didTapButton(_ sender: UIButton) {
let alert = UIAlertController(title: "タイトル", message: "メッセージ", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "キャンセル", style: .cancel) { _ in
print("キャンセルが選択されました。")
}
alert.addAction(cancelAction)
let okAction = UIAlertAction(title: "OK", style: .default) { (UIAlertAction) in
print("OKが選択されました。")
}
alert.addAction(okAction)
- present(alert, animated: true, completion: nil)
+ present(alert, animated: true)
}
}
⑧一連の処理をメソッド化する
見てわかる通り、メッセージを表示するだけでかなりの行数を使います。
メッセージを多数呼び出す場合は可読性が著しく落ちるので、メソッド化して簡単に呼び出せるようにします。
import UIKit
class ViewController: UIViewController {
// MARK: IBActions
@IBAction func didTapButton(_ sender: UIButton) {
- let alert = UIAlertController(title: "タイトル", message: "メッセージ", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "キャンセル", style: .cancel) { _ in
print("キャンセルが選択されました。")
}
- alert.addAction(cancelAction)
let okAction = UIAlertAction(title: "OK", style: .default) { _ in
print("OKが選択されました。")
}
- alert.addAction(okAction)
- present(alert, animated: true)
+ showAlert(title: "タイトル", message: "メッセージ", actions: [cancelAction, okAction])
}
+ // MARK: Private Methods
+
+ private func showAlert(title: String, message: String, actions: [UIAlertAction]) {
+ let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
+ actions.forEach { alert.addAction($0) }
+ present(alert, animated: true)
+ }
+
}
UIAlertAction
を配列で渡しているので、メッセージの選択肢を3つ以上にすることもできます。
⑨メソッド化した処理を他からも呼び出せるようにする
アラートを表示する処理は他のViewControllerでも使いたいため、 UIViewController
を拡張してメソッド化した処理を移します。
import UIKit
class ViewController: UIViewController {
// MARK: IBActions
@IBAction func didTapButton(_ sender: UIButton) {
let cancelAction = UIAlertAction(title: "キャンセル", style: .cancel) { _ in
print("キャンセルが選択されました。")
}
let okAction = UIAlertAction(title: "OK", style: .default) { _ in
print("OKが選択されました。")
}
showAlert(title: "タイトル", message: "メッセージ", actions: [cancelAction, okAction])
}
- // MARK: Private Methods
-
- private func showAlert(title: String, message: String, actions: [UIAlertAction]) {
- let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
- actions.forEach { alert.addAction($0) }
- present(alert, animated: true)
- }
-
}
+ import UIKit
+
+ /// UIViewController拡張(アラート)
+ public extension UIViewController {
+
+ // MARK: Public Methods
+
+ func showAlert(title: String, message: String, actions: [UIAlertAction]) {
+ let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
+ actions.forEach { alert.addAction($0) }
+ present(alert, animated: true)
+ }
+
+ }
リファクタリング後
これで完成です!
リファクタリング前と比べてコード量が減り、読みやすくなっていることがわかります。
import UIKit
class ViewController: UIViewController {
// MARK: IBActions
@IBAction func didTapButton(_ sender: UIButton) {
let cancelAction = UIAlertAction(title: "キャンセル", style: .cancel) { _ in
print("キャンセルが選択されました。")
}
let okAction = UIAlertAction(title: "OK", style: .default) { _ in
print("OKが選択されました。")
}
showAlert(title: "タイトル", message: "メッセージ", actions: [cancelAction, okAction])
}
import UIKit
/// UIViewController拡張(アラート)
public extension UIViewController {
// MARK: Public Methods
func showAlert(title: String, message: String, actions: [UIAlertAction]) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
actions.forEach { alert.addAction($0) }
present(alert, animated: true)
}
}
おわりに
いかがでしたでしょうか?
みなさんも他にいいリファクタリング案がありましたら、どんどんコメントなどでご指摘ください!