Property Wrapper in Swift 5.1 – Teil 2

Standardwerte festlegen

Im ersten Teil dieser Artikelreihe zu Property Wrappers haben wir gemeinsam die Grundlagen dieser neuen Technik aus Swift 5.1 betrachtet. Nach einer kurzen Pause möchte ich euch nun an dieser Stelle weitere Besonderheiten und Funktionen der Property Wrapper vorstellen. Das Thema dieses Mal: das Festlegen von Standardwerten.

Um dieses Thema zu erläutern, dient als Basis für diesen Artikel der folgende Property Wrapper namens SmallNumber. Dessen wrappedValue entspricht einem Integer, der nicht größer sein kann als der Wert, der in der maximum-Property des Property Wrappers festgelegt ist. Somit ist das Beispiel sehr ähnlich zu dem aus dem ersten Artikel, lediglich die maximum-Property und deren Einbindung ist neu.

@propertyWrapper
struct SmallNumber {
    
    private var maximum: Int
    
    private var currentNumber: Int
    
    var wrappedValue: Int {
        get {
            return currentNumber
        }
        set {
            if newValue <= maximum {
                currentNumber = newValue
            } else {
                currentNumber = maximum
            }
        }
    }
    
}

Damit dieser Property Wrapper einwandfrei funktioniert, müssen bei der Initialisierung passende Werte für maximum und currentNumber definiert werden. Die einfachste Möglichkeit, das zu erreichen, ist die Implementierung eines entsprechenden Default Initializers innerhalb des Property Wrappers, so wie im folgenden Listing zu sehen:

@propertyWrapper
struct SmallNumber {
    
    // ... bisherige Implementierung ...
    
    init() {
        maximum = 9
        currentNumber = 0
    }
    
}

Jede Property, die damit den SmallNumber-Property Wrapper nutzt, besitzt einen Standardwert von 0 und einen Maximalwert von 9. Das ist bereits eine praktische Ergänzung, das ganze lässt sich aber noch weiter optimieren.

So ist es möglich, einer Property bei Zuweisung eines Property Wrappers auch direkt einen alternativen Standardwert zuzuweisen. Dazu benötigt man einen Initializer, der als Parameter ein wrappedValue erwartet. Der Typ dieser Property entspricht dem des zugehörigen Wrapped Value des jeweiligen Property Wrappers. Eine beispielhafte Implementierung eines solchen Initializers wird im folgenden Listing innerhalb von SmallNumber ergänzt:

@propertyWrapper
struct SmallNumber {
    
    // ... bisherige Implementierung ...
    
    init(wrappedValue: Int) {
        maximum = 9
        currentNumber = min(wrappedValue, maximum)
    }
    
}

Der Initializer init(wrappedValue:) setzt intern den passenden Wert für currentNumber. Um den Initializer aufzurufen, weist man einer Property, die auf dem SmallNumber-Property Wrapper basiert, einfach den gewünschten Standardwert zu. Das folgende Listing zeigt ein entsprechendes Beispiel:

struct SomeRectangle {
 
    @SmallNumber public var height: Int = 19
 
    @SmallNumber public var width: Int = 5
 
}

var someRectangle = SomeRectangle()

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

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

Den beiden Properties height und width der Structure SomeRectangle kann nun ein Standardwert zugewiesen werden, da der Property Wrapper SmallNumber den dafür benötigten Initializer init(wrappedValue:) mitbringt. Da der height zugewiesene Wert 19 das Maximum von 9 übersteigt, wird die Property – wie es Aufgabe des Property Wrappers ist – auf 9 gesetzt.

Mit den gezeigten Initializern lässt sich die Verwendung von Property Wrappern bereits deutlich optimieren. Im gezeigten Beispiel des SmallNumber-Property Wrappers gibt es aber – neben dem Wrapped Value – noch eine weitere Information, nämlich das erlaubte Maximum. Das steht bisher statisch auf 9, doch auch das lässt sich mittels eines passenden Initializers ändern. Möchte man hierbei auch zusätzlich einen Standardwert für das Wrapped Value übergeben, muss man darauf achten, den als ersten Parameter mit dem Namen wrappedValue im Initializer zu verwenden. Eine passende Implementierung dazu zeigt das folgende Listing:

@propertyWrapper
struct SmallNumber {
    
    // ... bisherige Implementierung ...
    
    init(wrappedValue: Int, maximum: Int) {
        self.maximum = maximum
        currentNumber = min(wrappedValue, maximum)
    }
    
}

Einen solchen Initializer kann man direkt über die Property Wrapper-Deklaration aufrufen. Alternativ setzt man darüber nur jene Werte, die nicht dem Wrapped Value entsprechen, und übergibt letzteres weiterhin mittels Zuweisungsoperator. Beide Beispiele seht ihr einmal im Folgenden in der Praxis angewandt:

struct SomeRectangle {
 
    @SmallNumber(wrappedValue: 19, maximum: 20) public var height: Int
 
    @SmallNumber(maximum: 100) public var width: Int = 105
 
}

var someRectangle = SomeRectangle()

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

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

Fazit

Zwar mag die Syntax im ersten Moment etwas gewöhnungsbedürftig anmuten, doch lassen sich Property Wrapper auf die gezeigte Art und Weise flexibel um eigene Initializer ergänzen. Die verwendet man entweder dazu, um Standardwerte festzulegen oder bei Erstellung einer Property, die einen bestimmten Property Wrapper nutzt, dynamisch passende Werte für verschiedene Attribute wie das Wrapped Value zu definieren.

Euer Thomas


Kommentare

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

  1. Avatar von Martin Kowollik
    Martin Kowollik

    Hallo Thomas,
    das verstehe ich nicht:

    struct SomeRectangle {

    @SmallNumber(wrappedValue: 19, maximum: 20) public var height: Int

    @SmallNumber(maximum: 100) public var width: Int = 105

    }

    var someRectangle = SomeRectangle()

    print(„height: \(someRectangle.height)“)
    // height: 9

    print(„width: \(someRectangle.width)“)
    // widht: 5

    Hier hätte ich jetzt als Ausgabe für height 19 und für widht 100 erwartet. Was habe ich übersehen?
    Gruß, Martin

    1. Hallo Martin,

      besten Dank für dein Feedback! Du hast absolut recht, mir ist da im letzten Code-Listing ein Fehler unterlaufen (ich schätze, ich war hier beim Copy & Paste unvorsichtig). Es ist genau, wie du sagst: height entspricht 19, und width 100 (aufgrund des Maximums). Ich passe die Ergebnisse entsprechend im Text an.

      Nochmals herzlichen Dank! 🙂

      Mit besten Grüßen,
      Thomas

Schreibe einen Kommentar

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