Swift ifとguardのエトセトラ - Re.Ra.Ku アドベントカレンダー day 11
Re.Ra.Ku アドベントカレンダー 11日目です。
こんにちは、神場です。 前回の記事 に引き続き、今回もSwift関連の記事となります。
概要
今回はif文とguard文に関するtipsです。 if文は一般の条件分岐に、guard文は想定外の値が来た場合などの早期リターンに使われるという違いはありますが、使い方が似ているためこの記事ではまとめて紹介したいと思います。
ifとguard両方で使えるもの
今回紹介するのはこの3つです。いずれもif
とguard
の両方で使えます。
if let, guard let
によるOptional Bindingif A, B, ..., guard A, ,B ...
のような複数の条件if case, guard case
によるパターンマッチ
順番に見て行きましょう。
if let, guard let
によるOptional Binding
まずは基本的なところですが、if let, guard let
でオプショナルな変数のバインディングを行うことが出来ます。
if let a = a { print(a) } else { print("a is nil") }
これはオプショナルな変数のUnwrapに使う基本的な方法なので、特に疑問はないかと思います。
if A, B, ..., guard A, ,B ...
のような複数の条件
Swiftをやり始めの頃だと(自分も含めて)よくやってしまっていたと思うのですが、
if let a = a { if let b = b { ... } }
のような文はまとめて
if let a = a, let b = b { ... }
と書くことが出来ます。上記の例のように二つぐらいの条件であればまだ良いのですが、条件が多い時は基本的にカンマ区切りで書いたほうがコードがすっきりします。guard文を使う例を以下に挙げます。
indexが存在し、かつステータスがactiveなユーザーの名前を取得するサンプルコード
enum Status { case active, inactive } struct User { let name: String let status: Status } func getActiveUserName(users: [User], index: Int) -> String? { guard index < users.count, users[index].status == .active else { return nil } return users[index].name }
if case, guard case
によるパターンマッチ
上記二つに比べて日本語ではあまり情報がない印象がありますが、実はif
やguard
でもswitch
で使うようなパターンマッチを使うことが出来ます。
enum
のAssociated Valueの中身をパターンマッチで取り出すサンプルコード
enum Result<T> { case success(value: T) case failure(error: Error) } func getValue<T>(result: Result<T>) -> T? { guard case let .success(value) = result else { return nil } return value }
上記のように、switch
に書くcase
がほぼそのままguard
の中に
入ります。
なおこれを応用(?)することによって、Optionalでない変数のバインドを行うことも出来ます。
indexが存在し、かつステータスがactiveなユーザーの名前を取得するサンプルコード(userを一旦変数に入れておくバージョン)
func getActiveUserName2(users: [User], index: Int) -> String? { guard index < users.count, case let user = users[index], user.status == .active else { return nil } return user.name }
if case, guard case
のさらに詳しい情報については、こちらの方の記事
が非常に参考になるかと思います。
より実践的なサンプル
以上のいくつかを組み合わせて、より具体的なサンプルを見てみましょう。
guard文を使ったAPIレスポンスのハンドリングのサンプル
func parseResponse(result: Result<Any>) -> DTO? { guard case let .success(value) = result else { print("通信に失敗したよ") return nil } guard let json = data as? [String: Any] else { print("データがJSONじゃないみたいだよ") return nil } guard (200..<300) ~= statusCode else { print("ステータスコードが200番台以外だよ") return nil } guard let dto = parseJson(json) else { print("JSONが期待しているものと違うよ") return nil } print("正常なレスポンスが返ってきたよ") return dto }
ただしエラー内容も取得したい場合はsuccess
とfailure
の2つにパターンマッチを使いたいため、成功か失敗かの分岐部分をswitch
で書くことになるかと思います。
まとめ
いかがでしょうか。以上の3つ程度があればif
やguard
だけでも比較的読みやすい書き方が出来るのではないでしょうか。若干まとまりのない感じになってしまいましたが、もしご参考になれば幸いです。