此れは、Swift5.1から出てきて Class{}
とStruct{}
の実装部に{var{get{}}}
と{var{set{}}}
そしてcomputed property コードの重複を減らす方法を提供します。
十三・壱 Property Wrapper
壱・甲 Property Wrapperについて
Class
とStruct
{Instance{}}
にある{Property = }
で値を割り当うとか、
接近する時値を保存するとか、読み込む前に変更作業とValidationをする時が度々あります。実はcomputed propertyとしてする事ができます。
だが、様々な Class
とStruct
で生成されたComputed Propertyは似た様なパータンを持っている場合にが頻繁にあります。
Swift5.1前には、computedPropertyを共有する唯一な方法はいちいちClass
とStruct
で含まるだけでした。
その短所を補完するためにPropertyWrapperを導入しました。
此れは基本的にcomputedPropertyの機能を個別Class
とStruct
と分離できるようにして、アフリコードで再利用できる様にします。
壱・乙 Property Wrapper例題
都市の名前を保存するString Propertyを持っているStruct{}
あるとしよう。
struct Address{
var city: String
}
使用者が都市の名前をどうやって入力するかはかかわらずに大文字に保存されると、次の様にComputedPropertyをStruct{}
で追加できます。
struct Address{
private var cityname: String = ""
var city: String {
get {cityname}
set {cityname = newValue.uppercased()}
}
}
都市の名前が{Property = }
で割り当てられると、演算プロパティのset
がprivate cityname var
で値を保存する前に大文字に変更する事になる
今作ったStruct{}
をテストする為に次の様にします。
var address = Address()
address.city = "London"
print(address.city)
>> LONDON
演算フロパティは都市名前の文字を大文字に変更されました。
もし、この様な作業が他の Struct
とかClass
でひつよなら、今のコードをコピーして必要なところで貼り付ける方法もあります。
だが、今の例題はコードの多さが少ないからそうなるですけど、大量のコードと複雑な演算がある場合には適度はないと思います。
演算フロパティを使用する代わりに、このロジックをフロパティラッパーで実装できます。
例えば、次の宣言部は文字列を大文字で変更すると設計されたFixCaseというフロパティラッパーを実装できます。
@propertyWrapper
struct FixCase {
private(set) var value: String = ""
var wrappedVlaue: String {
get { value }
set { value = newValue.uppercased()}
}
init(wrappedValue initialValue: String){
self.wrappedValue = initialValue
}
}
フロパティラッパーは propertyWrapper 指示者を使って宣言され、 Class{}
とStruct{}
の中で実装されます。
全てのPropertyWrapper
は値を変更するとかValidationTest
する{var{get{}}}
と{var{set{}}}
コードが含まれるwrappedValue
Property
を持つべきである。
{init{}}
は選択事項で含めることができる。前のコードには{init{}}
が大文字で変更すしてprivate var
で保存するwrappedValue {Property = }
で割り当てます。
PropertyWrapper
を使う為にはこのこの動作が必要な Class{}
とかStruct{}
の {Property = }
前で@FixCase支持者を付けてばいいです。
struct Contact{
@FixCase var name:String
@FixCase var city:String
@FixCase var country:String
}
var contact = Contact(name: "John Smith", city:"London", country: "United Kingdom")
print("\(contact.name),\(contact.city),\(contact.country)")
>> JOHN SMITH, LONDON, UNITED KINGDOM
壱・丙 複数の変数とタイプをサポート
前の例題にPropertyWrapper
はラップングされる {Property = }
で割り当てる var
の形でただ一つの値を受けました。
どんな作業を実行する時使用される複数の値を受ける様にもっと複雑な{@ProWrap var :}
を実装する事ができます。追加される値は{@ProWrap(ここで) var :}
で置きます。
@propertyWrapper
struct MinMaxVal {
var value: Int
let max: Int
let min: Int
init(wrappedValue: Int, max: Int, min: Int) {
value = wrappedValue
self.max = max
self.min = min
}
var wrappedValue: Int {
get{ return value}
set{
if newValue > max {
value = max
} else if newValue < min {
value = min
} else {
value = newValue
}
}
}
}
struct Demo{
@MinMaxVal(min: 10, max: 150) var value: Int = 100
}
{init{}}
は Wrapper Valueで追加されるminとmaxの値を受け取って実装されます。wrappedValueのsetは値が特定範囲の中であるかどうか検査してその値をminとmaxで割り当てます。
前のpropertyWrapperは次のコードでテストできます。
struct Demo{
@MinMaxVal(min: 100, max: 200) var value: Int = 100
}
var demo = Demo()
demo.value = 150
print(demo.value)
demo.value = 250
print(demo.value)
このコードを実行するとさ一番目のprint syntax
は150を出力します。なでなら150は許容範囲内に入っているからです。反面、2番目のprint syntax
は200を出力します。なぜならwrapperが最大値を200までしか許容の為です。
現在、実装されたPropertyWrapper
はInt var
だけで作業します。もし、同じターフの他の var
と比較できる全ての変数ターフと一緒に使えるなら有用になるはずです。
幸いにも、PropertyWrapper
は特定のprotocolを従う全てのターフと一緒に作業ができます。
PropertyWrapper
の目的は比較作業をすることで、Foundation frameWorkでふくまれるComparable : Protocol
を従う全てのデータターフを対応するように修正する必要があります。
Comparable : Protocol
を従うターフは値が同じか大きいか小さいかを比較するに使用される事ができます。Stifng, Int, Date, DateInterval, Characterみたいな多様なターフがこの : Protocol
を従います。
Comparable : Protocol
を使用されるためにPropertyWrapper
を実装するために宣言部は次の様に修正必要があります。
@propertyWrapper
struct MinMaxVal<V: Comparable> {
var value: V
let max: V
let min: V
init(wrappedValue: V, min: V ,max: V) {
value = wrappedValue
self.min = min
self.max = max
}
var wrappedValue: V {
.
.
.
この様に修正されたwrapper
は前でした様にIntvar
でも動作し、Comparable : Protocol
を従う方の全てのターフでも使用できます。
次の例題は文字列の値がアルファベットの観点からminとmax範囲の中で入るのかどうかを判断する事です。
struct Demo{
@MinMaxVal(min: "Apple", max: "Orange") var value: String = ""
}
var demo = Demo()
demo.value = "Banana"
print(demo.value)
>> Banana
demo.value = "Pear"
print(demo.value)
>> Orange <- この値は与えられたAlpabet範囲外なので。。。
同じ様に、このWrapper
は Date Object:
としても動作します。
次の例題は現在日付と一月の後の日付まで制限しています。
struct DateDemo{
@MinMaxVal(min: Date(), max: Calendar.current.date(byAdding: .month, value: 1, to: Date())!) var value: Date = Date()
}
次のコードは結果は私たちが作れたwrapperがDate var
を持つても動作得ることを示すことです。
var dateDemo = DateDemo()
print(dateDemo.value)
dateDemo.value = Calendar.current.date(byAdding: .day, value: 10, to: Date())!
print(dateDemo.value)
dateDemo.value = Calendar.current.date(byAdding: .month, value: 2, to: Date())!
print(dateDemo.value)
予約
Swift 5.1で導入されたPropertyWrapper
はClass
とStruct
宣言内でコードの重複を避けながらアフリprojectのコードを通じて再利用される{var{get,set{}}}
のStruct{}
を使用できる様にします。