LoginSignup
33
21

More than 3 years have passed since last update.

Swift5.1 PropetyWrappersを使ったUserDefaultsの例

Last updated at Posted at 2019-10-11

はじめに

Swift 5.1から新しく追加されたPropertyWrapper
@State@Bindingなどでお馴染みですが、カスタム属性を作成して使っているでしょうか?

今回は、WWDC2019セッションUserDefaultsを使用したカスタム属性の内容が紹介されていたので、その例を備忘録兼ねて書こうと思います。

PropertyWrappersとは?

Swift5.1で導入されている機能で、プロパティをラップしてアクセスパターンを指定することができる機能。
すごくざっくり言うと、「プロパティにアクセスする際の面倒な処理をまとめておいて、使う時に@なんちゃらで呼び出すことができるよ」というものです。

ちなみに@State@PublishedなどもPropertyWrappersです。

独自に定義する場合は@propertyWrapperを宣言して使います。

今まで

通常はこんな処理はしないと思いますが、例えばこのような処理があったとします。

struct Person {
    var name: String {
        get {
            UserDefaults.standard.object(forKey: "person_name") as? String ?? ""
        }
        set {
            UserDefaults.standard.set(newValue, forKey: "person_name")
        }
    }
}

Person以外にも、このようなコンピューテッドプロパティを使用してUserDefaultsにアクセスするような処理が複数あったらとても冗長です。

PropertyWrappersを使う

ここでPropertyWrappersの出番です。
PropertyWrappersを使用してUserDefaultsのアクセスパターンを定義してみます。
(ちなみにこのUserDefaultsのアクセスパターンは冒頭で書いたように、WWDC2019セッションで紹介されている例です。)

@propertyWrapper
struct UserDefault<T> {
    let key: String
    let defaultValue: T

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

    var wrappedValue: T {
        get {
            return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
        }
        set {
            UserDefaults.standard.set(newValue, forKey: key)
        }
    }
}

こう定義することで、外部からUserDefaultでプロパティを宣言することが可能になりました。

今回の例だと、実際に@UserDefaultで定義されたプロパティにアクセスした際はこのwrappedValueの中が呼ばれます。

@UserDefaultを使ってみる

実際に使ってみます。

先ほどPersonクラスで定義していた処理がこんなにすっきり書くことができました。

struct Person {
    @UserDefault("person_name", defaultValue: "")
    var name: String
}

通常通り以下のように使用できます。

let a = Person()
a.name = "Taro"

ただ、nameプロパティも同時に初期化する場合、このままでは型がUserDefault<String>となっているので、明示的にイニシャライザを追加してあげる必要があります。

struct Person {
    @UserDefault("person_name", defaultValue: "")
    var name: String

    init(name: String) {
        self.name = name
    }
}

これで引数付きで初期化できるようになりました。

let a = Person(name: "Taro")

上の例は実用的でないので、実際に使う場合は以下のようにStaticで宣言して使う感じになるかと思います。

struct UserDefaultsConfig {
    @UserDefault("isLogin", defaultValue: false)
    static var isLogin: Bool

    @UserDefault("username", defaultValue: "")
    static var username: String

    @UserDefault("age", defaultValue: 1990)
    static var age: Int
}

すっきり。

    UserDefaultsConfig.isLogin = true
    UserDefaultsConfig.username = "Yamada Taro"
    UserDefaultsConfig.age = 24

さいごに

どうでしょう。思ったより簡単でした。
PropertyWrappersを使用することで、プロパティのアクセスパターンを繰り返し書く必要がなくなります。
そして宣言的にカスタム属性を足すだけで、その定義をプロパティに当てはめられるようになるので、よりシンプルに、完結的な記述をすることができるようになりますね。

良かったらみなさんも使ってみてください。

以上、UserDefaultsを使った例でしたが、まだまだ、理解できていないところもありますので、ご指摘やアドバイス等ありましたら、コメントしていただければと思います。

それではお読みいただきありがとうございました。

33
21
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
33
21