Meine liebsten Swift-Features – Teil 1

Standardwerte für Parameter

Die Programmiersprache Swift liegt heute bereis in Version 5.7 vor und hat bereits einen weiten Weg hinter sich. In all den Jahren, in denen Swift inzwischen zur Verfügung steht, wurde die Sprache stetig verbessert und weiterentwickelt.

Ich selbst nutze Swift seit Version 1 und es gibt so einiges, das mir an dieser Sprache immens gut gefällt. Diese neue Artikelreihe soll euch – aus meiner ganz persönlichen Sicht – einige dieser Features vorstellen.

Dabei geht es mir nicht um die aufwendigsten oder modernsten Funktionen. Es spielt auch keine Rolle, ob ein Feature von Beginn an existierte oder erst in einer späteren Version hinzukam. Es handelt sich schlicht um einen persönlichen Auszug dessen, was mir bei der Arbeit mit Swift besondere Freude bereitet.

Starten möchte ich mit einem Feature, das ich persönlich sehr exzessiv nutze und das in meinen Augen massiv zu sauberem und übersichtlichen Code in Swift-Projekten beitragen kann: Standardwerte für Parameter (die sogenannten Default Parameter Values).

Ausgangslage

Lasst mich die Schönheit dieses Features direkt einmal anhand eines simplen Beispiels demonstrieren. Gegeben ist die Klasse User, die den Anwender einer App abbildet. Neben einem eindeutigen username, der für jede User-Instanz zu setzen ist, bringt die Klasse auch noch diverse optionale Eigenschaften mit sich. Dazu gehören Properties zum Abbilden von Vor- und Nachname und dem Geburtstag des Nutzers. Außerdem wird über die access-Eigenschaft festgelegt, ob ein Nutzer Vollzugriff auf alle Funktionen hat (pro) oder nur die Standard-Funktionen verwenden kann (standard).

class User {
    enum Access {
        case standard
        case pro
    }
    
    var username: String
    var access: Access
    var firstName: String?
    var lastName: String?
    var birthday: Date?
    
    init(username: String, access: Access) {
        self.username = username
        self.access = access
    }
    
    init(username: String, access: Access, firstName: String?, lastName: String?, birthday: Date?) {
        self.username = username
        self.access = access
        self.firstName = firstName
        self.lastName = lastName
        self.birthday = birthday
    }
}

Bei der Initialisierung müssen in jedem Fall Werte für username und access übergeben werden, da diese Parameter nicht optional sind. Entsprechend bringt die Klasse User einen passenden Initializer namens init(username:access:) mit. Zusätzlich sollen aber auch nach belieben Werte für die anderen Eigenschaften bei der Initialisierung gesetzt werden können (abhängig davon, ob entsprechende Informationen vorliegen). Zu diesem Zweck steht ein zweiter Initializer namens init(username:access:firstName:lastName:birthday:) zur Verfügung. Die Parameter für die optionalen Eigenschaften sind auch in diesem Initializer als optional deklariert.

Umsetzung und Nutzen von Default Parameter Values

Technisch gesehen gibt es an der Klasse User nichts auszusetzen. Die Initialisierung kann sich jedoch teils als umständlich erweisen.

Das beginnt bereits beim Einsatz von init(username:access:). Wenn wir davon ausgehen, dass die meisten Nutzer nur die Standard- und nicht die Pro-Funktionen nutzen, muss dennoch bei der Initialisierung immer der entsprechende standard-Wert für access übergeben werden. Schöner wäre es, diesen Wert standardmäßig für den access-Parameter zu nutzen. Und genau das lässt sich in Swift realisieren, wie die folgende Anpassung des Initializers demonstriert:

init(username: String, access: Access = .standard) {
    self.username = username
    self.access = access
}

Durch schlichte Zuweisung eines Werts nach der Angabe des zugehörigen Parameter-Typs definiert man diesen Wert als Standard. Die folgenden zwei Aufrufe dieses Initializers sind nun identisch und führen zum selben Ergebnis:

let firstUser = User(username: "First")
let secondUser = User(username: "Second", access: .standard)

Für den Zugriff auf die Standard-Funktionen muss also nicht länger explizit der standard-Wert für den access-Parameter bei der Initialisierung gesetzt werden (es ist aber natürlich auch kein Fehler, ihn trotzdem mit anzugeben). Nur in dem Fall, dass pro für access zu verwenden ist, muss dieser Wert auch entsprechend übergeben werden:

let proUser = User(username: "Pro", access: .pro)

Ähnlich lässt sich nun auch der zweite Initializer init(username:access:firstName:lastName:birthday:) optimieren. Denn auch der hat einen Nachteil (neben dem noch fehlenden Standardwert für access): Setzt man beispielsweise nur den Vornamen, muss für lastName und birthday trotzdem jeweils nil übergeben werden, was den Aufruf dieses Initializers unnötig aufbläht:

let firstUser = User(username: "First", access: .standard, firstName: "Thomas", lastName: nil, birthday: nil)

Zur Optimierung kann man deshalb festlegen, dass die optionalen Parameter standardmäßig nil entsprechen. Zusätzlich weist man access noch den Standardwert standard zu:

init(username: String, access: Access = .standard, firstName: String? = nil, lastName: String? = nil, birthday: Date? = nil) {
    self.username = username
    self.access = access
    self.firstName = firstName
    self.lastName = lastName
    self.birthday = birthday
}

Der vorherige Initializer-Aufruf lässt sich nun wie folgt abkürzen:

let firstUser = User(username: "First", firstName: "Thomas")

Das entschlackt den Code deutlich und macht ihn übersichtlicher. Gleichzeitig können wir uns mit dieser Anpassung des zweiten Initializers nun den ersten Initializer vollständig sparen! Da es für all die zusätzlichen Parameter des zweiten Initializers passende Standardwerte gibt, lässt der sich genau so verwenden wie init(username:access:). Die Klasse Person wird dadurch auch noch mal etwas schlanker:

class User {
    enum Access {
        case standard
        case pro
    }
    
    var username: String
    var access: Access
    var firstName: String?
    var lastName: String?
    var birthday: Date?
    
    init(username: String, access: Access = .standard, firstName: String? = nil, lastName: String? = nil, birthday: Date? = nil) {
        self.username = username
        self.access = access
        self.firstName = firstName
        self.lastName = lastName
        self.birthday = birthday
    }
}

Fazit

Ich bin ein großer Fan von Standardwerten für Parameter in Swift. Wie bereits dieses simple Beispiel der User-Klasse gezeigt hat, lässt sich damit einerseits der benötigte Code verschlanken, da nicht mehr mehrere verschiedene Varianten mit unterschiedlichen Parametern für eine Funktion benötigt werden. Gleichzeitig lassen sich auch die Funktionsaufrufe vereinfachen, wenn nur jene Werte gesetzt werden müssen, die notwendig sind und nicht in dem meisten Fällen einem expliziten Wert entsprechen (wie im Falle von access, für das meist standard zum Einsatz kommt).

Natürlich sollte man es aber auch mit den Default Parameter Values nicht übertreiben. Sie sollten wirklich nur dann zum Einsatz kommen, wenn ein bestimmter Wert als Standard für einen Parameter angesehen werden kann. Dann sind sie aber in jedem Fall eine große Hilfe und tragen zur Optimierung und Übersichtlichkeit des eigenen Codes bei.

Euer Thomas


Kommentare

Schreibe einen Kommentar

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