16
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

enumをUserDefaultsに保存したい!

Last updated at Posted at 2018-03-31

背景

タイトルにもある通り、UserDefaultsにenumを保存したいシーンに遭遇しました。
enumを保存する際によくやる方法は次のようなコードではないでしょうか?

enum State: String {
    case hoge, fuga, piyo
}

extension UserDefaults {

    func setState(_ value: State?, forKey key: String) {
        if let value = value {
            set(value.rawValue, forKey: key)
        } else {
            removeSuite(named: key)
        }
    }

    func getState(forKey key: String) -> State? {
        if let string = string(forKey: key) {
            return State(rawValue: string)
        }
        return nil
    }
}

呼び出しは次のような感じです。

let key = "key"
var state: State? = UserDefaults.standard.getState(forKey: key) // nil
UserDefaults.standard.setState(State.hoge, forKey: key)
state = UserDefaults.standard.getState(forKey: key) // hoge

保存したいenumを値型enumにして、そのrawValueをUserDefaultsで読み書きする方法です。
これで正しくenumをUserDefaultsに保存できました。動かすだけならこれで十分です。

ですが、別の型のenumを保存する場合を考えましょう。そうなると、また上のコードのようにUserDefaultsにメソッドを追加する必要があります。保存したいenumの種類が増えるたびメソッドを追加するのは面倒です。

解決方法

複数種類のenumを愚直に保存する場合、基本的には上のコードの型が違うだけのコードが増えます。
ということでジェネリクスを利用していきます。

値型enumの正体はRawRepresentableというプロトコルです。


public protocol RawRepresentable {

    associatedtype RawValue

    public init?(rawValue: Self.RawValue)
    public var rawValue: Self.RawValue { get }
}

このプロトコルを元にUserDefaultsにメソッドを追加します。

extension UserDefaults {

    func setEnum<T: RawRepresentable>(_ value: T?, forKey key: String) where T.RawValue == String {
        if let value = value {
            set(value.rawValue, forKey: key)
        } else {
            removeObject(forKey: key)
        }
    }

    func getEnum<T: RawRepresentable>(forKey key: String) -> T? where T.RawValue == String {
        if let string = string(forKey: key) {
            return T(rawValue: string)
        }
        return nil
    }
}

今回はRawValueがStringの場合のメソッドのみを追加していますが、基本的にはこれで十分だと思います。
呼び出し側のコードは次の通りです。

let key = "key"
var state: State? = UserDefaults.standard.getEnum(forKey: key) // nil
UserDefaults.standard.setEnum(State.hoge, forKey: key)
state = UserDefaults.standard.getEnum(forKey: key) // hoge

もちろんState以外のenumでもRawValueがStringであれば、UserDefaultsにメソッドを追加することなく読み書きすることができます。

以上、ちょっとした小ネタでした。
「もっとこうした方がいい」などありましたらコメントお願いします。

16
11
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?