LoginSignup
1

More than 3 years have passed since last update.

Property Wrappersで値の検査をしてProperty Wrappersを学んでみた

Posted at

Swift5.1(?)の新機能Property Wrappersを学ぶために、Property Wrappersを使った値の検査を書いてみました。

まず検査を担当する、Wrapperすると言ったら良いのかな?の型です。

@propertyWrapper
struct Condition<T> {
    let inCond: (T) -> Bool
    let outCond: (T) -> Bool
    var _value: T!

    init(in inCond: @escaping (T) -> Bool, out outCond: @escaping (T) -> Bool) {
        self.inCond = inCond
        self.outCond = outCond
    }

    var value: T {
        get {
            precondition(outCond(_value))
            return _value
        }
        set {
            precondition(inCond(newValue))
            _value = newValue
        }
    }
}

@propertyWrapperを付けて記述します。inCond・outCondは代入時・読み出し時の検査です。@propertyWrapperではvalueプロパティの宣言が必要です。

次にConditionを利用するコードを書いてみます。

class A {
    @Condition(in: { !$0.string.isEmpty }, out: { $0.string == $0.string2 })
    var b: B

    func copy() {
        $b._value.copy()
    }
}

struct B {
    var string: String
    var string2: String = ""

    mutating func copy() {
        string2 = string
    }
}

var a = A()
a.b = B(string: "test")
a.copy()
print(a.b)

Aには@Conditionを付けて宣言したvar b:Bがあります。このプロパティbがConditionでラップされていることになります。
a.b = B(string: "test")で代入するときに@Conditionのinが実行されてpreconditionに渡されます。
print(a.b)で読み出すときにoutが実行されます。
もしB(string: "")(空文字列)を代入するとinがfalseになってpreconditionでクラッシュします。a.copy()を行ってB.stringをB.string2に代入しておかないとoutがfalseになってクラッシュします。
func copy()では、$b._value.copy()を実行しています。もしb.copy()を行うと@Conditionの読み出しが発生してoutがfalseになるのでクラッシュしてしまいます。ですので、@Conditionを回避するにはCondition._valueを直接操作する必要がありました。

これでだいたいの動作が把握できたように思います。

この検査、ちょっとDbCっぽい記述かなと思いました。クロージャをラップしてDbCの様な記述ができると面白いと思いましたが、

@Condition(...)
var f: (String) -> String

のfの型のConditionでの処理が思いつきませんでした。

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
1