バックストーリー
問題
ある日、あなたのチームに、iOSアプリがクラッシュする!というバグ報告が押し寄せました。
どうやら新規実装された「武内P」というキャラクターが問題のようです。
ソースを確認してみます。
{
id: 346,
name: "武内P",
type: 3
}
class Idol {
enum Type: Int {
case Cute
case Cool
case Passion
}
let id: Int
let name: String
let type: Type
init(json: JSON) {
id = json["id"].stringValue
name = json["name"].stringValue
type = Type(rawValue: json["type"].intValue)!
}
}
原因がわかりました。 Idol.Type
のrawValueは0〜2の3パターンを想定しているのに、実際のresponseには 3
が入ってきています。
サーバチームに確認したところ、この「武内P」は、新しいtypeを持つキャラクターなのだそうです。
Typeは3パターンで決めうちだと聞いてたのに増えるなんて! と愚痴りつつも、あなたは取り急ぎPull Requestを出しました。
enum Type: Int {
case Cute
case Cool
case Passion
+ case Producer
}
Enumのcaseをひとつ増やして、これで一安心…と思ったら、チームメンバーからレビューがつきました。
ふたつの解答
Oさんのレビュー。
O:
これだとダメ。また新しいTypeが実装された途端、再びクラッシュするから。type = Type(rawValue: json["type"].intValue)!
のforced unwrapをなくして、このクラスの
type
プロパティはOptionalに変えよう。
解釈できない状態はnilで表現すべき。
そこに、チームメンバーのFさんから横レスがつきました。
F:
えー?
外からtypeを参照するときいちいちif let
でunwrapするの面倒じゃない?
それよりさー、新しいcaseType.None
を追加して、type = Type(rawValue: json["type"].intValue) ?? .None
って書けば一番楽だと思うな
ふたつの立場の立脚点
ここからは二人の論戦が続きます。
O「 Type.None
? 解釈できないだけで存在してるのに?」
F「ネーミングの問題ー? じゃあ Type.Unrecognized
にしよう」
O「命名の問題じゃない。
Type.Unrecognized
のrawValueは? 5だよね。
レスポンスの値がたとえば7でもそのcaseになると…値が乖離しちゃう」
F「それ、気にするとこー?
…まーぶっちゃけstringで返してほしかった的なとこはあるよねー…って話はおいといて。
rawValueが問題なら、enum Type: Int
の Int
を削ってから init(id:Int)
みたくイニシャライザを用意すればOK?」
O「そういうことじゃなくて。
実際は Unrecognized
なんてTypeはないんでしょ。
存在する状態と存在しない状態が別れる場合、Swiftに言語仕様としてOptionalが用意されてるのだから、Optionalを使うべきってこと」
F「うーん…
実運用上、解釈できないTypeなんて異常値でしょー?
そのためにプロパティをOptionalにしたら、アプリ全体でunwrapの手間が増えちゃうじゃん。めんどくさくなーい?
Typeが解釈できないってのも、列挙できるひとつの状態だよ。そもそもOptional型だって所詮 Some
と None
のEnumなんだし。
言葉を返すけど、列挙できる場合、Swiftに言語仕様として列挙子が用意されてるのだから、列挙子を使うべきじゃないかなー」
OさんとFさんは、視点が違うだけで、どちらももっともらしいことを言っているように見えます。
さて、あなたはどう考えますか?
このエントリに結論はありません。
コメント等、お待ちしています。