
Swift ist eine von Apple im Jahre 2014 auf der weltweiten Entwicklerkonferenz (World Wide Developer Conference - kurz WWDC) heraus gebrachte Programmiersprache. Swift sollte die bisher auf iOS und macOS (damals OSX) vorherrschende Programmiersprache Objective C durch ihre Sicherheit, Schnelligkeit und Einfachheit ablösen. Seit Dezember 2015 ist Swift auch als Open Source Projekt auf GitHub verfügbar und belegte in der StackOverflow Developer Survey 2019 den 6. Platz der beliebtesten Programmiersprachen unter Entwicklern. Neben macOS oder Linux ist auch bald ein offizielles Release auf Windows geplant.
Nachdem ich ein wenig auf die Grundlagen von Swift eingegangen bin, werde ich mich mit dieser Ausarbeitung auf Optionals, einem zentralen Bestandteil für die Sicherheit in Swift, fokussieren.
Apple legt bei seiner Programmiersprache großen Wert auf die Verständlichkeit und hat gleichzeitig mit der Veröffentlichung von Swift unzählig viele Ressourcen zum Lernen der neuen Sprache herausgegeben. So gibt es mehrere von Apple geschrieben Bücher kostenlos in iBooks zum Herunterladen.
Um Swift ausführen zu können, wird die Swift Toolchain benötigt. Auf macOS wird diese automatisch mit Xcode, der offiziellen IDE von Apple, installiert. Auf Linux benötigt es ein paar mehr Schritte welche hier näher beschrieben sind. Die Swift Toolchain kommt mit vielen nützlichen Programmen zum Erstellen und Debuggen von Swift Anwendungen. Eines dieser Tools ist auch eine REPL (Read-eval-print loop), ein interaktiver Editor zum einfachen Ausführen und Evaluieren von Swift Code. Möchte man Swift ohne viel Setup einfach mal ausprobieren, ohne die Toolchain lokal installieren zu müssen, bietet es sich an, eine "Online REPL", bspw. auf repl.it zu öffnen. Wer ein iPad besitzt, der kann sich im App Store eine sehr stark vereinfachte Version von Xcode unter dem Namen "Playgrounds" installieren. Dort bekommt man in Interaktiven Kursen die einfachsten Grundlagen des Programmierens mit Swift beigebracht.
Swift wurde primär zum Erstellen von Anwendungen auf Apples eigenen Betriebssystemen (iOS / iPadOS, macOS, tvOS, watchOS) entwickelt. Seit der Veröffentlichung von Swift als OpenSource Sprache, findet Swift aber auch in anderen Feldern immer mehr Beliebtheit. Einige spannende Projekte sind beispielsweise eine Implementierung auf einem AVR-Microcontroller oder die Verwendung von Swift als Backend-Server (IBM Kitura, Vapor).
Für jeden der mal eine objektorientierte Programmiersprache gelernt hat, sollte Swift, bis auf einige Besonderheiten schnell zu verstehen sein. Immerhin war die Komplexität und die Unleserlichkeit von ObjC ein Grund dafür, warum Swift entwickelt wurde.
Im Nachfolgenden werde ich auf die Besonderheiten von Swift eingehen. Dabei werde ich aber Grundlagen und Konzepte, die aus anderen Sprachen bekannt sind, wie Konstanten let oder Konstrukte wie enum, class, und struct überspringen, da sie sich im Wesentlichen nicht groß von den anderen Programmiersprachen unterscheiden.
Swift erlaubt es nicht, Werte unterschiedlicher Datentypen miteinander zu kombinieren. So ist das Addieren eines Float mit einem Double erst nach einer Umwandlung in einer der beiden Formate möglich.
Da der Swift Compiler über eine Funktion namens "Type Inference" verfügt, müssen Variablentypen nicht explizit festgelegt werden, sondern werden vom Compiler automatisch zugeordnet.
var eng: String = "Hello World!"
var deu = "Hallo Welt!" // Type Inferation
In der Ausarbeitung werde ich den Datentyp einer Variable immer mit angeben, da es hier vielleicht zum besseren Verständnis beisteuert. Dies ist allerdings wie bereits erwähnt, nicht der Standard und sollte zur Übersichtlichkeit auch möglichst vermieden werden.
Swift bietet die Möglichkeit, Werte schnell und einfach in einen String einzubinden. Damit dies jedoch möglich ist, muss ein Datentyp dem CustomStringConvertible protocol folgen. Dieser schreibt einen String Rückgabewert mit der Bezeichnung description vor, welcher als solcher in den zu konstruierenden String eingesetzt wird. Hier an einer Beispiel-Implementierung für einen zweidimensionalen Punkt.
struct Point: CustomStringConvertible {
let x: Int
let y: Int
var description: String {
// Int folgt auch dem
// CustomStringConvertible protcol
return "(\(x), \(y))"
}
}
let p1 = Point(x: 4, y: 5)
print("Punkt: \(p1)")
print("Punkt: \(p1.description)")
Ich habe hier Apples IDE mit als Feature aufgeführt, da sie die Programmiererfahrung von Swift mit vielen nützlichen Features deutlich verbessert. Beispielsweise kann man sich durch einen Mausklick mit gedrückter ⌥ Optionstaste weitere Informationen zu einzelnen Variablen, Funktionen und Objekten anzeigen lassen.
Ein Optional baut auf das Konzept eines Wächterwertes / Sentinels auf. Ein Optional ist ein Datentyp, welcher darstellen kann, dass, ein Wert möglicherweise nicht vorhanden ist. Dieses nicht Vorhandensein eines Wertes gibt es in vielen Sprachen und wird häufig durch ein bestimmtes Steuerzeichen wie undefined, Null oder wie in Swift durch nil dargestellt.
Was Swift in dieser Hinsicht einzigartig macht ist, dass man dem Compiler explizit mitteilen muss, ob man davon ausgeht, dass eine Variable zu irgend einem Zeitpunkt keinen Wert haben kann. Standardgemäß erlauben die Variablentypen in Swift dies nämlich nicht. Das macht den Umgang mit nicht vorhandenen Werten deutlich sicherer. Da man sich, wenn kein Optional vorliegt auch nicht um einen möglichen Fall kümmern muss, in dem kein Wert vorhanden ist.
Was in C als der "Billion Dollar Mistake" bekannt ist, löst Swift mit der Unterscheidung zwischen optimalen Werten und nicht optionalen Werten.
Ein Optional wird mit einem ? im Suffix des Variablentyps deklariert. Dies teilt dem Compiler mit, dass eine Variable möglicherweise keinen Wert gespeichert hat.
var a: String?
Ein Optional ist im Grunde nichts anderes als eine Aufzählung, eine Enumeration zweier Zustände. Diese zwei Zustände sind "es ist ein Wert vorhanden" .some oder "es ist kein Wert vorhanden" .none.
enum Optional<Wrapped> {
case some(Wrapped)
case none
}
Wrapped ist ein Generischer Typ und spiegelt den gewünschten Datentypen wider.
Anstelle der Kurzform mit dem ?, könnte ein Optional nach der Implementierung oben also auch wie folgt definiert werden:
var b: Optional<String> = .some("Hallo")
// gleich mit
var c: String? = "Hallo"
Gehen wir von einem einfachen Szenario aus. Ein Programmierer möchte gerne das Alter eines Nutzers mittels eines Eingabefeldes auf dem Bildschirm feststellen und dieses für eine Altersvalidierung nutzen. Von dem Textfeld erhält der Programmierer einen Wert vom Typ String zurück. Dieser muss nun in ein geeignetes Format bspw. einen Int überführt werden.
Für den Datentyp Int gibt es eine passende Initialisierungsfunktion die aus unserem String einen vermeintlichen Integerwert macht. Vermeintlich deswegen, da die Apple Programmierer mit der Initialisierungsfunktion keinen Int sondern einen Int? zurück geben. Damit sagen sie uns, dass mit dem übergebene String nicht unbedingt ein Integerwert erzeugt werden konnte.
/*
Die Typen-Deklarationen sind nicht zwingend notwendig,
da der Compiler diese selber herausfindet.
Sie dienen hier dem besseren Verständnis.
*/
let textFeld: String = "22"
let optionalAlter: Int? = Int(textFeld)
let mindestAlter: Int = 12
// 🛑 Value of optional type 'Int?' must be unwrapped to a value of type 'Int'
if (optionalAlter >= mindestAlter) {
print("Darf ins Kino")
} else {
print("Muss draußen bleiben")
}
Steht in dem Textfeld ein Wert, welcher nicht in einen Integer überführt werden konnte, erhalten wir nil als optionalAlter zurück.
Würden wir nun probieren, mit einem möglicherweise nicht vorhandenem Wert, unsere Altersvalidierung durchzuführen, würden wir ziemlich bald auf Widerstand des Compilers stoßen. (🛑)
In anderen Sprachen, die keinen Optional haben, wäre uns dieser Fehler wohlmöglich erst deutlich später aufgefallen.
Um diesen Fehler zu beheben und um einen Nutzen aus dem optionalen Wert zu ziehen, werde ich als Nächstes auf mögliche Operationen eingehen.
Das Unwrapping oder zu dt. entpacken eines Optional soll uns Zugriff auf den im Optional verschachtelten Wert geben. Das Unwrapping ist der Teil, welcher der ganzen Optional-Logik seine Sicherheit verleiht, da in diesem Schritt Fehler auf möglichst elegante Weise ausgesondert werden.
Für das Unwrapping bietet Swift mehrere Möglichkeiten:
guard let alter: Int = optionalAlter else {
print("Das Alter konnte nicht in einen Int überführt werden!")
return // Wenn in eine Funktion
}
print("Der Nutzer ist \(alter) Jahre alt.")
Die if Abfrage fragt: "Wenn du die neue Variable alter gleich der nicht optionalen Version von optionalAlter setzen kannst, dann mache folgendes…".
if let alter: Int = optionalAlter {
print("Der Nutzer ist: \(alter) Jahre alt.")
} else {
print("Das Alter konnte nicht in einen Int überführt werden!")
}
Mit dem Nil-Coalescing Operator kann man einer Variablen einen Standardwert zuweisen, wenn der linke Wert nil sein sollte.
let alter: Int = optionalAlter ?? 0
Das Force Unwrapping zerstört den eigentlichen Nutzen eines Optionals. Mit ihm wird gesagt, dass man sich absolut sicher ist, dass in einem Optional ein Wert vorhanden ist und diesen entpacken kann.
let alter: Int = optionalAlter! // <- Force Unwrapped
Ist der entpackte Wert dennoch nil so kommt es zu einem Fehler und das Programm stürzt mit der Fehlermeldung "Unexpectedly found nil while unwrapping an Optional value" ab.
Ich hoffe, dass ich mit meinem Aufsatz einen kleinen Einblick in Swift und dessen Optional Typ verschaffen konnte und ich zudem ein wenig von meiner Begeisterung für diese Sprache teilen konnte.
Ich würde mir wünschen das die Swift Community in der Zukunft noch weiter wächst und diese Sprache noch lange fortbestand hat.
Wie in fast all meinen Texten sind Rechtschreibfehler leider nicht ganz ausgeschlossen.
In Swift ist es möglich Funktions- und Variablennamen mit Unicode Charakteren darunter auch Emojis zu deklarieren.
let 🐓 = "🍗"
func 🥦() -> String {
return "🤢"
}