Property Wrapper in Swift 5.1 – Teil 3

Projected Value

In diesem (voraussichtlich letzten) Teil zum Thema Property Wrapper in Swift 5.1 möchte ich euch ein weiteres kleines Feature vorstellen, das die Arbeit mit dieser neuen Technik abrundet.

Die Rede ist vom sogenannten Projected Value. Das erlaubt es, eine beliebige zusätzliche Information über einen Property Wrapper auszuliefern. Typ und damit Art dieser Information kann von euch vollkommen frei festgelegt werden. Dieses Projected Value wird innerhalb einer Property Wrapper-Implementierung über eine Property namens projectedValue umgesetzt. Der Name ist entscheidend und muss immer zwingend projectedValue lauten (vergleichbar der wrappedValue-Property).

Ein Beispiel für die Umsetzung und Implementierung eines Projected Value findet ihr im folgenden Listing. Basis ist der SmallNumber-Property Wrapper aus dem vorangegangenen Teil dieser Artikelserie. Der wird nun um eine passende projectedValue-Property ergänzt, die in diesem Fall dem Typ Bool entspricht (wie geschrieben könnt ihr diesen Typ pro Property Wrapper selbst frei bestimmen).

In dem gezeigten Beispiel dient das Projected Value als ergänzende Information beim Setzen eines Werts über den SmallNumber-Property Wrapper. Es entspricht false, wenn der zugewiesene Wert unterhalb des Maximums liegt und so der zugewiesene Wert wie gewünscht übernommen wird. Andernfalls – sprich wenn der neue Wert das definierte Maximum übersteigt und so jenes Maximum verwendet wird – wird das Projected Value auf true gesetzt. So kann nach Zuweisung einer Zahl zu einer SmallNumber-Instanz nachvollzogen werden, welche Art von Änderung durchgeführt wurde.

@propertyWrapper
struct SmallNumber {
    
    private var maximum: Int
    
    private var currentNumber: Int
    
    var projectedValue = false
    
    var wrappedValue: Int {
        get {
            return currentNumber
        }
        set {
            if newValue <= maximum {
                currentNumber = newValue
                projectedValue = false
            } else {
                currentNumber = maximum
                projectedValue = true
            }
        }
    }
    
    init(wrappedValue: Int, maximum: Int) {
        self.maximum = maximum
        currentNumber = min(wrappedValue, maximum)
    }
    
}

Doch wie greift man nun auf jene Info zurück, die innerhalb des Projected Value und damit innerhalb der projectdValue-Property von SmallNumber gespeichert ist?

Basis hierfür ist das $-Zeichen. Stellt man das einer Instanz voran, die konform zu einem Property Wrapper ist, greift man so statt auf den eigentlichen Wert der Instanz auf das Projected Value zu.

Ein Beispiel hierfür findet ihr im folgenden Listing. Darin wird erneut der SomeRectanlge-Typ mit den beiden SmallNumber-Properties height und width definiert. Anschließend erfolgt die Erstellung einer SomeRectangle-Instanz und eine Änderung der beiden Properties.

Interessant hierbei: height erhält mit 50 einen Wert, der unter dem Maximum von 100 liegt, weshalb height auch erfolgreich der Wert 50 zugewiesen werden kann. width hingegen erhält als Wert 100, nachdem versucht wurde, einen Wert von 200 zuzuweisen (der aber eben das Maximum von 100 übersteigt). Das Projected Value entspricht demnach für die height-Property false und für die width-Property true.

Um jenes Ergebnis des Projected Value zu ermitteln, stellt man den Properties height und width schlicht ein $-Zeichen voran. So erhält man statt des zugewiesenen Zahlenwerts die boolesche Information aus dem Projected Value (wie in den abschließenden print(_:)-Aufrufen im Listing zu sehen).

struct SomeRectangle {
    
    @SmallNumber(maximum: 100) public var height: Int = 10
    
    @SmallNumber(maximum: 100) public var width: Int = 10
    
}

var someRectangle = SomeRectangle()
someRectangle.height = 50
someRectangle.width = 200

print("height: \(someRectangle.height)")
// height: 50

print("$height: \(someRectangle.$height)")
// $height: false

print("width: \(someRectangle.width)")
// widht: 100

print("$width: \(someRectangle.$width)")
// $widht: true

Übrigens: Verfügt ein Property Wrapper über kein Projected Value, verhindert der Compiler die Verwendung eines vorangestellten $-Zeichens für zugehörige Properties (die würden schließlich dann ins Leere laufen). So müsst ihr euch keine Sorgen darüber machen, die $-Syntax ohne Verfügbarkeit eines Projected Value einzusetzen; das ist dank des Compilers nicht möglich.

Fazit

Mithilfe eines Projected Value lässt sich ein Property Wrapper um eine beliebige zusätzliche Information ergänzen. Das macht zwar nicht für jeden Property Wrapper Sinn, kann aber im Fall der Fälle sehr hilfreich sein, um mehr über die Funktionsweise eines Property Wrapper an eine zugehörige Property weiterzugeben.

Euer Thomas


Kommentare

2 Antworten zu „Property Wrapper in Swift 5.1 – Teil 3“

  1. Avatar von Martin Kowollik
    Martin Kowollik

    Danke. Gute Erklärung und bestimmt mal nützlich. Dies scheint ein paste&copy-Fehler zu sein. // height: 19
    Gruß, Martin

    1. Danke für dein Feedback, das freut mich zu lesen. 🙂 Danke auch für den Hinweis bezüglich des Kommentars im Code, ich habe das geändert.

      PS: Ich war so frei und habe den vorigen Kommentar entfernt.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert