概要
guardの使い方を整理していた時にhttp://radex.io/swift/guard/ の記事を見つけてしまったので超訳しつつリビルドしてみました。
はじめに
Swiftのguard構文はエラーが発生したら終了
やこの値は来てほしくない
,などのguard節として利用と、アンラップした変数を以降の処理でも使いたい
などの場面で使われるのが一般的かと思います。
実際の案件でのコードレビューを見てみると、ifで良い場面でguardを使ったり、guardを使うことで逆にわかりにくくなったりするケースがありました。リファレンスに書かれている使い方をもう一度明確にするために利用方法を整理してみます。
一般的な使い方
アンラップした変数をスコープ外で利用する
変数をアンラップしてguardブロック以降のスコープでも利用する
guard let url = NSURL(string: coverPath) else { // NSURL.init?(string URLString: String)
abort()
}
if imageManager.cachedImageExistsForURL(url) {
省略...
早期リターンとしての利用
メソッド開始直後に条件を満たしていなければ早期にリターンする
private func deleteDownloadTask() {
guard let realm = self.realm() else {
return
}
try! realm.write {
// realmを使ったなんらかの処理
}
}
複数条件の判定
カンマで区切って複数条件をチェックしつつアンラップできます。
長い処理の中での中断を判定する処理としてもguardは有効利用できます。
※Xcode12からは面倒だった複数行のインテントが揃うようになったとのことです
func onUpdateSubscriptionLevel(notification: NSNotification) {
if self.isTopmost(true) {
guard let channel = self.viewModel.channel,
let episodes = self.viewModel.episodes else {
return
}
// 何らかの処理
guard let subscribe = notification.parameter().data as? Subscribe else {
return
}
// 何らかの処理
}
}
アサーションとしての利用
※単純なチェックならassertでもいいと思います。
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
guard let story = self.viewModel.stories?[indexPath.row] else {
abort()
}
// 何らかの処理
}
NGな使い方
どちらかを返すようなメソッドの中で利用する
func isLogin() -> Bool {
guard let _ = SettingService.sharedInstance.currentUserId else {
return false
}
return true
}
// 判定結果を返せばいいので != で良い。
func isLogin() -> Bool {
return SettingService.sharedInstance.currentUserId != nil
}
Rubyのunlessの様に利用する
Rubyには条件式が偽の場合の処理を記述する「unless」という構文があります。
Swiftのguardは名前の通りguard節としての利用方法が正しい使い方ですので、unlessのように使うと可読性が悪くなります。
// 悪い例
guard SettingService.sharedInstance.isUserLogined(userId) == true else {
// ユーザーIDをセットする処理
}
// 改善例
// guard 構文内にロジックを書くのは分かりにくいので if を利用する
if !SettingService.sharedInstance.isUserLogined(userId) {
// ユーザーIDをセットする処理
}
まとめ
guardはアンラップ 兼 guard節として利用しましょう