【Swift】NSUserDefaultsを便利に使う方法を幾つか

  • 26
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

はじめに

  • NSUserDefaultsの扱いを簡単にしたい
  • NSUserDefaults.standardDefaults()って毎回書きたくない
  • キーを文字列リテラルで指定するのが嫌だ(タイポもあるし..)

などなどの不満から、NSUserDefaultsを扱う方法についていろいろ調べてみて試行錯誤した結果を残しておきます。
タイトルに「幾つか」と書いていますが、2つだけです :sweat_smile:

良い方法をご存知の方、ご教授ください。

1. enumでキーを定義する

enumでNSUserDefaultsを便利に

上記の記事を参考にさせていただきenumを使ってNSUserDefaultsを使う方法を考えてみました。

ほぼほぼ上記の記事通りですが、キーに対応する値の型を明確にしたかったので、型を分けるためのenumを入れ子にして入れてみました。

enum UserDefaults {

    // Bool型の値を持つキーをcaseで定義
    enum BoolValue: String {
        case SomeFlag

        func set(value: Bool) {
            NSUserDefaults.standardUserDefaults().setBool(value, forKey: self.rawValue)
            NSUserDefaults.standardUserDefaults().synchronize()
        }

        func get() -> Bool {
            return NSUserDefaults.standardUserDefaults().boolForKey(self.rawValue)
        }
    }

    // Int型の値を持つキーをcaseで定義
    enum IntValue: String {
        case UserID

        func set(value: Int) {
            NSUserDefaults.standardUserDefaults().setInteger(value, forKey: self.rawValue)
            NSUserDefaults.standardUserDefaults().synchronize()
        }

        func get() -> Int {
            return NSUserDefaults.standardUserDefaults().integerForKey(self.rawValue)
        }
    }

    // String型の値を持つキーをcaseで定義
    enum StringValue: String {
        // ...
    }

    // 略...

}

使用時

UserDefaults.IntValue.UserID.set(1)
let userId = UserDefaults.IntValue.UserID.get() // 1

キーに対してどの型の値を入れるかは明確になった一方、
定義の段階においては、型ごとにgetメソッドとsetメソッドを作っているので、少々面倒ではあります。
また、使用時でもenumのネストが深い分、「.」つなぎが多くなってしまうという点ではちょっと使いづらくなってしまった感じがあります。

2. structのプロパティとしてキーを定義する

2つ目ですが、こちらに関しては下記のライブラリを参考にしました。
https://github.com/radex/SwiftyUserDefaults

(そのままそのライブラリ使えよっていうのはいったん置いといて :grimacing:

struct UserDefaultsKeys {

    // ここにキーを定義していく
    static var hasLoginKey = DefaultsKey<Bool>("someFlag")
    static var userID = DefaultsKey<Int>("userID")

}

struct DefaultsKey<ValueType> {
    let key: String

    init(_ key: String) {
        self.key = key
    }
}

extension NSUserDefaults {

    subscript(key: DefaultsKey<Bool>) -> Bool {
        get {
            return boolForKey(key.key)
        }
        set {
            setBool(newValue, forKey: key.key)
            synchronize()
        }
    }

    subscript(key: DefaultsKey<Int>) -> Int {
        get {
            return integerForKey(key.key)
        }
        set {
            setInteger(newValue, forKey: key.key)
            synchronize()
        }
    }

    subscript(key: DefaultsKey<String>) -> String {
        // ...
    }

    // 略...
}

使用時

let UserDefaults = NSUserDefaults.standardUserDefaults()

UserDefaults[UserDefaultsKeys.userID] = 1
let userId = UserDefaults[UserDefaultsKeys.userID]

こちらも型ごとにsubscriptを書いてgetterとsetterを実装していますが、
subscriptでアクセスできる分、直感的に値のget, setを行いやすいというのはいいですね。

また、DefaultsKey構造体でジェネリクスを用いて様々な型を受けられるようにし、NSDefaultsの拡張でその型に対応するsubscriptを作るというのは勉強になります。

最後に

ほとんど自分で考えたというものではないので記事にするのもおこがましいのですが、、
enumsubscript, ジェネリクスの活用などは今回色々と考えてみて大変勉強になりました。