iOS
Swift

Swift の guard は正しく使いましょう


概要

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) {
省略...


guard節としての利用

メソッド開始直後の条件判定

private func deleteDownloadTask() {

guard let realm = self.realm() else {
return
}

try! realm.write {
// realmを使ったなんらかの処理
}
}

複数条件の判定

カンマで区切って複数条件をチェックしつつアンラップできます。長い処理の中での中断を判定する処理としてもguardを利用すべきです。

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節として利用すべし


参考

Swift_Programming_Language Statements