はじめに
Swiftで開発していてif let
やguard let 〜 else
を使ったOptional Bindingをすることがよくあると思います
私が普段、よく使うif let
やguard let 〜 else
のOptional BindingのTipsを紹介してみたいと思います
(誰もが知っている当たり前のことしか書いてないかもしれませんが。。)
Optional Bindingの基本
まずは基本として、if let
やguard let
を使った下記のような書き方はお馴染みだと思います
let optionalString: String? = "aaa"
if let unwrapString = optionalString {
print(unwrapString) //aaa
}
これは、Optional型の変数optionalString
を非Optional型として扱うこと(アンラップ)ができる場合、if let
で宣言しているunwrapString
にOptional型が外れたString型の値が代入され、処理がifのスコープ内に入り、unwrapString
変数を使うことができます
逆にoptionalString
の値がnilの場合、非Optional型として扱うことができないため、ifのスコープ内には入りません(すなわちunwrapString
も使えません)
if let
だとネストされるので下記のようにguard
を使って早期リターンで書くケースも多いと思います
let optionalString: String? = "aaa"
guard let unwrapString = optionalString else {
return
}
print(unwrapString) //aaa
また、optionalString
の値が空でない場合という条件を満たしたい場合はwhere
を使い下記のように表現できます
let optionalString: String? = "aaa"
if let unwrapString = optionalString where !unwrapString.isEmpty {
print(unwrapString) //aaa
}
optionalString
変数を非Optional型に変換したunwrapString
変数が空かどうかという条件をwhere
に書いています
仮にoptionalString
がnil
だった場合は、非Optional型にすることができないため、where
に書いた条件文に入ってくることはありません
Optional Binding Tips
さてここからがTipsとなります
- 例題としてある画像のURL文字列からUIImageを作成し、UIImageViewで表示する場合を考えてみましょう
処理の流れとしては下記とします
1.URL文字であるString型をNSURL型に変換
2.NSURL型をNSData型に変換
3.NSData型からUIImage型にしてUIImageViewを生成する
1,2,3の各型変換処理は全てOptional型が返り値になる処理になります
Tips1
Optional型を全て羅列
下記のようにそれぞれのOptional変換を全てif let
で羅列して、各処理の返り値の値が非Optional型として扱うことができる場合、最終的にimage
変数にUIImage型の値が生成されUIImageViewに表示することができます
let urlString = "https://pbs.twimg.com/profile_images/3734255592/0b417aa61da0196cfed35889928ee895.png"
if let url = NSURL(string: urlString), data = NSData(contentsOfURL: url), image = UIImage(data: data) {
UIImageView(image: image)
}
仮にNSURL(string: urlString)
やNSData(contentsOfURL: url)
で非Optional型として扱えないとなった場合、そこで処理が終了しifのスコープ内には入りません
置き換えると下記と同様です
if let url = NSURL(string: urlString) {
if let data = NSData(contentsOfURL: url) {
if let image = UIImage(data: data) {
UIImageView(image: image)
}
}
}
Tips2
先の例題で、画像URLに記載されている画像の拡張子がpngである場合のみUIImageViewに表示するといった要件に変わった場合、下記のようにすることで要件を満たせます
let urlString = "https://pbs.twimg.com/profile_images/3734255592/0b417aa61da0196cfed35889928ee895.png"
if let url = NSURL(string: urlString) where url.pathExtension == "png", let data = NSData(contentsOfURL: url), image = UIImage(data: data) {
UIImageView(image: image)
}
まずif let url = NSURL(string: urlString)
で非Optional型であるNSURL型として扱える値かどうかを判定し、NSURL型が返ってきた場合、url
変数に代入しwhere
でNSURL型の拡張子を調べるメソッドを呼び出し拡張子がpngかチェックしています
その条件が通った場合、さらにurl
変数からNSData型を生成し、UIImage型を生成するといった先ほどと同じ流れになります
where
の条件の後にOptional Bindingを使うことができるということです
ちなみにwhere
の後に、letをつけないと下記のようにエラーになります
//Binding ended by previous where clause: use let to introduce a new one
if let url = NSURL(string: urlString) where url.pathExtension == "png", data = NSData(contentsOfURL: url), image = UIImage(data: data) {
UIImageView(image: image)
}
Tips3
Optional Bindingと、非Optionalな値をif let
内で共存させたいケースってないでしょうか?
例題として、あるURL文字列を保持するurl
というOptional型の変数があり、そのファイル名の文字数が5文字より大きければ表示したいとします
処理としては下記とします
1.url
変数が非Optionalにできるか
2.urlを/
文字で分割
3./
文字で分割した最後の部分であるファイル名を取得
4.ファイル名が5文字より大きければ表示
これを直感的に下記のように書いてみます
let url: String? = "http://qiita.com/hachinobu/items/aedac203f42b215e6df4"
if let unwrapUrl = url, elements = unwrapUrl.characters.split("/"), last = elements.last where last.count > 5 {
print(String(last))
}
しかしこれは、コンパイルエラーになります
Initializer for conditional binding must have Optional type, not [SubSequence]
if let
やguard let
で非Optional型と分かっているものを変数に代入することはできません
(unwrapUrl.characters.split("/")
の返り値は非Optional型)
だからといって下記のように多重ネストしたり、分けて書いたりしたくないですよね?(guard
を使っても2回return書いたり分けて書くことになる)
let url: String? = "http://qiita.com/hachinobu/items/aedac203f42b215e6df4"
if let unwrapUrl = url {
let elements = unwrapUrl.characters.split("/")
if let last = elements.last where last.count > 5 {
print(String(last))
}
}
さて、これを解決するには2つ方法があります
まず1つめはif let
で宣言した変数に明示的に型を書いてあげれば通るようになります
let url: String? = "http://qiita.com/hachinobu/items/aedac203f42b215e6df4"
if let unwrapUrl = url, elements: [String.CharacterView] = unwrapUrl.characters.split("/"), last = elements.last where last.count > 5 {
print(String(last)) //aedac203f42b215e6df4
}
unwrapUrl.characters.split("/")
の返り値を格納するelements
変数に[String.CharacterView]
と型を明示的に書くことでコンパイルが通るようになります
2つめのやり方は、Optional Bindingの特性を利用して、非Optional型と分かっているものをOptional.Some()
で包んでOptional型にしてしまうやり方です
let url: String? = "http://qiita.com/hachinobu/items/aedac203f42b215e6df4"
if let unwrapUrl = url, elements = Optional.Some(unwrapUrl.characters.split("/")), last = elements.last where last.count > 5 {
print(String(last))
}
unwrapUrl.characters.split("/")
の結果は非Optional型だと分かっているので、Optional.Some
で包むことで強引に結果をOptional型にしてOptional Bindingのエラーをパスするやり方です
まとめ
いかがでしょうか
Tipsというほどのものではなかったかもしれませんが、この情報が何かしら役に立てれば幸いです