Swiftの列挙型(=enum)のassociated valuesの仕組みを使ってコードの保守性を向上させよう、という記事です。
※この記事が前提にしている開発環境はXcode9.3 / Swift4.1です。
String
型やData
型を列挙型のassociated valuesにして意味を与える
文字列やバイナリデータって、変数名に気をつけないと可読性・保守性が凄く落ちますよね。
例えば以下のような「暗号化された画像をWEBからロードしてきて表示する」コードの場合↓
func load() -> Data? {
let imageUrl = URL(string: "https://www.example.com/some/secret/image")!
let data = try? Data(contentsOf: imageUrl) // dataは暗号化されている
return data
}
if let data = load() {
imageView.image = UIImage(data: data) // ❌dataはまだ複合されていないので表示できない
}
この例ではdata
が復号化されないままUIImage
のイニシャライザに渡されているので画像が表示されなくなります。
この例はとても単純な実装なのでこういったミスをすることは想像しにくいかもしれませんが、暗号化されたデータが複数のfunctionを跨る設計になっていると、こういった不具合が起きやすくなると思います。
※余談ですが、先日Twitterさんが誤ってハッシュ化する前のパスワードをログに書き込んでしまっていた件はそういった実装が原因で「ハッシュ化前のパスワード」と「ハッシュ化後のパスワード」が区別できないような実装になっていて起きたのではないか?と想像しています。
この問題をSwiftの列挙型を使って解消する一例を挙げると、以下のようになります。
enum ImageData {
case encrypted(Data)
case decrypted(Data)
}
func load() -> ImageData? {
let imageUrl = URL(string: "https://www.example.com/some/secret/image")!
if let data = try? Data(contentsOf: imageUrl) {// dataは暗号化されている
return .encrypted(data) // 暗号化されているDataだということを明示した値を返却する
}
return nil
}
if let imageData = load(), case .decrypted(let data) = imageData {
imageView.image = UIImage(data: data) // ⭕️imageDataが複合されているときだけこの行が実行される
}
この実装でもData
の復号化処理は書いていないので結局画像は表示されないのですが、ImageData.decrypted
であること(=復号化済みであること)を判定するif
文が書けるおかげでUIImage
に暗号化されたままのData
を渡すことがなくなり、不意の事故を防いで適切なエラーハンドリングを実装する事ができるようになりました。
このような形で、Data
型やString
型の値をenumでラップしてあげることでそれらの値に意味を持たせる事ができます。
今回は暗号化済データ/復号化済データの区別に用いましたが、実行ステップを経ていく中でデータの形が変わる類の値であれば、何にでも活用できるTipsだと思います。
チーム開発では分かりやすい変数名をつけたりすることも大切ですが、こういうアプローチで保守性を上げることもできますよ、というご紹介でした。
どなたかのお役に立てば幸いです。