【Swift】プロパティ・ラッパーの基本を理解する

# この投稿は何か？
Apple Swift Language Guideにある[Property Wrappers]()に基づき、プロパティ・ラッパーの基本を理解します。

## 実行環境
macOS Catalina 10.15.2
Xcode11.3
Swift5.1

# ハンズオン

# 小さな長方形
コードで、各辺の長さが`12`以下である小さな長方形を表現します。
2つのプロパティ`height`と`width`を持つ構造体です。


````Swift:
struct SmallRectangle {
    private var _height = 0
    private var _width  = 0
    var height: Int {
        get { return _height }
        set { _height = min(newValue, 12) }
    }
    var width: Int {
        get { return _width }
        set { _width = min(newValue, 12) }
    }
}
````

高さも幅も、計算プロパティを利用して、プロパティに`12`より大きい値が割り当てられた場合だけ、`12`を返すようにしています。
実際に、インスタンスを生成してみます。

````Swift:小さな長方形インスタンス
var rectangle = SmallRectangle()

rectangle.height = 9
rectangle.height        // height is 9.
````

長方形の高さは`9`になりました。
次に、長方形の高さを`15`に変更してみます。

````Swift:
rectangle.height = 15   
rectangle.height        // height was assigned 15, but now is 12.
````

割り当てようとした高さが大きすぎたので、計算プロパティによって`12`が割り当てられました。

## コードを分離する
では、次に「小さな円」や「小さな三角形」の構造体を定義する必要があった場合はどうでしょう？
いずれも単純な構造体ですが、`SmallRectangle`構造体と同じような記述を繰り返す必要があります。
繰り返している部分を抽出して、別の構造体に定義します。

````Swift:12以下の数値を返す構造体
struct TwelveOrLess {
    private var number = 0
    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, 12) }
    }
}
````

`TwelveOrLess`構造体は割り当てた数値が`12`より大きかった場合、`12`を返します。
この`TwelveOrLess`構造体を利用して、各辺のサイズが`12`以下の長方形を表現する構造体を定義します。

````Swift:小さな長方形を表現する構造体
struct AnotherSmallRectangle {
    private var _height = TwelveOrLess()
    private var _width = TwelveOrLess()
    var height: Int {
        get { return _height.wrappedValue }
        set { _height.wrappedValue = newValue }
    }
    var width: Int {
        get { return _width.wrappedValue }
        set { _width.wrappedValue = newValue }
    }
}
````

`SmallRectangle`と`AnotherSmallRectangle`に大した違いはありませんが、__12以下を返す__部分のコードを再利用できるようになりました。
「直径または半径が`12`以下の円」や「高さが`12`以下の三角形」の構造体を定義する際に、`TwelveOrLess`構造体を再利用できます。

## @propertyWrapper
ここで、プロパティ・ラッパーを使用すると、コードを簡略化できます。
プロパティ・ラッパーを使用するには、`TwelveOrLess`構造体の定義直前に`@propertyWrapper`キーワードを記述します。

````Swift:
@propertyWrapper        // add keyword 
struct TwelveOrLess {
    private var number = 0
    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, 12) }
    }
}
````

プロパティがラップされた長方形の構造体は、次のように定義されます。
プロパティの直前に`@TwelveOrLess`属性を指定します。

````Swift:プロパティをラップした構造体
struct SmallRectangleWithPropertyWrapper {
    @TwelveOrLess var height: Int
    @TwelveOrLess var width: Int
}
````

すごくシンプルに記述できるようになりました。
同じように動作します。

````Swift:
var simpleRectangle = SmallRectangleWithPropertyWrapper()
simpleRectangle.height = 7
simpleRectangle.height        // height is 7

simpleRectangle.height = 17
simpleRectangle.height        // height is 12
````

ただ、Playgroundで実行している限りですが、動作が不安定で、以下のようなエラーが発生することがあります。
`error: MyPlayground.playground:58:17: error: 'height' is inaccessible due to 'internal' protection level`

原因が思い当たらないのですが、次のように修正すると確実に動作するようになります。

````Swift:修正
struct SmallRectangleWithPropertyWrapper {
    @TwelveOrLess public var height: Int    // add public keyword
    @TwelveOrLess public var width:  Int    // add public keyword
}
````

上記、エラーの原因と修正対応について、みなさまどう思われるでしょうか？
ぜひ、お考えなどをお聞かせください。