Nach mehr als 3 Monaten veröffentlicht Apple eine neue Version von Swift, welche neben einigen Änderungen auch einige gute Neuerungen vorzuweisen hat. Nicht zuletzt sind auch wir sehr froh, dass Swift 1.2 die Abwärtskompatibilität bis auf einige kleine Einzelfälle wart.
Stabilität und mehr Performance
In unseren ersten Tests haben wir eine deutliche Verbesserung der Stabilität von Xcode feststellen können. Unterlegt wird dies durch das GitHub Projekt SwiftCompilerCrashes von practicalswift, welches mit dem ersten Release von Swift Programmcode dokumentiert, der zum Absturz führt. Seit dem Erscheinen von Xcode 6.3 ist hier merklich ein Rückgang verzeichnet worden. Zudem soll Swift 1.2 an einigen Stellen entschlackt und optimiert worden sein. Etwaige Benchmarks stehen hier bis Dato aber noch aus.
Der Changelog von Xcode 6.3 Beta 1 liest sich selbst wie ein eigenes Buch. Es gab viele Bugfixes und neben Optimierungen für Swift, wurde auch Objective-C für die bessere Interoperabilität mit Swift „gepimpt“. Wir haben uns die Zeit genommen, einige Veränderungen unter die Lupe zu nehmen und zu erklären. Alle Änderungen, die zum Teil auch das Swift Buch betreffen, sind hier aufgelistet. In den kommenden Tagen werden wir noch entsprechendes Material als Ergänzung für Durchstarten mit Swift für euch kostenlos zur Verfügung stellen!
Wir lassen euch nicht alleine in diesem ganz neuen Jungle 🙂
Inkrementelle Builds
Der Swift Compiler, welcher den Programmcode übersetzt in Maschinensprache, hat bisher bei jedem Kompiliervorgang den kompletten Programmcode neu übersetzt. Durch inkrementelle Builds werden ab jetzt bereits übersetzte Dateien nur noch bei Veränderungen erneut kompiliert. Dies spart vor allem bei umfangreicheren Projekten deutlich Zeit und spart auch sicherlich einiges an Akkulaufzeit bei mobilen Macs. Apple selbst weist darauf hin, dass es vorkommen kann, dass man mehr Dateien kompilieren sieht, als nötig. Dies soll aber in Ordnung sein und ist kein Indikator für ein „rebuild“.
Multiple Optionals Überprüfungen mit „if let“
Swift setzt es voraus, dass eine Variable einen Wert enthält, sobald ich mit dieser arbeiten will. Bei Optionals kann dies nicht garantiert werden, da diese bekanntlich leer sein können. Mit if let kann man gezielt prüfen, ob ein Wert in einer Variable existiert. Sollte dies der Fall sein, wird der Wert einer temporären Variabel zugewiesen. Anschließend wird überprüft, ob tatsächlich ein Wert in der temporären Variable gespeichert wurde. Ist dies der Fall wird der Programmcode für den true Fall ausgeführt. Gerade im Umgang mit Objective-C API’s kann es mit unter vorkommen, dass man vor einer Aktion mehrere Variablen prüfen muss. Dies führte nicht zuletzt zu sehr hässlichen Verschachtelungen die jetzt entfallen können.
Das folgende überspitzte Beispiel demonstriert dies als Programmcode. Beachten Sie bitte, dass Sie für den Swift 1.2 Beispielcode mindestens Xcode 6.3 Beta 1 benötigen.
Swift 1.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
var vorname: String? var nachname: String? var pizza: Bool? vorname = "Stefan" pizza = false var allesGut = false if let tVorname = vorname { if let tNachname = nachname { if let tPizza = pizza { allesGut = true if tPizza { println("\(tVorname) \(tNachname)! Eine Pizza für dich!") } else { println("Keine Pizza für dich!") } } } } if !allesGut { println("Nicht alle Werte waren vorhanden.") } |
Swift 1.2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
var vorname: String? var nachname: String? var pizza: Bool? vorname = "Stefan" pizza = false if let tVorname = vorname, tNachname = nachname, tPizza = pizza { if tPizza { println("\(tVorname) \(tNachname)! Eine Pizza für dich!") } else { println("Keine Pizza für dich!") } } else { println("Nicht alle Werte waren vorhanden.") } |
Es gibt sicherlich auch bessere Strategien um einen solchen Code zu vermeiden. Sollte man aber in eine ähnliche Situation kommen, ist man jetzt bestens gerüstet.
Aus unserer Sicht eine wirklich sehr schöne und praktikable Lösung!
Verspätete Initialisierung von Konstanten
Bisher war es in Swift nötig dafür zu sorgen, dass das Deklarieren einer Konstante immer verbunden war mit der unmittelbaren Initialisierung eines Wertes. Ausnahmen bildeten hier nur die Konstruktoren von Klassen und Strukturen, bei denen spätestens am Punkt der Objekterstellung das Setzen der konstanten Werte nötig war. Swift erlaubt es ab Version 1.2, dass man die Konstante verspätet initialisieren kann. Man darf lediglich nicht auf die Konstante zugreifen, solange kein Wert in ihr vorhanden ist. Das Überschreiben des Inhaltes einer Konstante, ist nach erstmaliger Initialisierung nicht mehr möglich!
Zu beachten ist ausserdem, dass das Verhalten von let in Klassen und Strukturen ebenfalls angepasst wurde! War es bisher möglich in einem Konstruktor den Wert beliebig oft zu ändern, ist es ab jetzt ebenfalls nur noch einmalig möglich, die Konstante zu initialisieren!
13.02.2015 – Wir bekommen bei diesem Beispiel in Xcode 6.3 Beta 1 mit Swift 1.2 immer noch Kompilierfehler. Eventuell gefixt in Xcode 6.3 Beta 2 oder wir haben das nicht richtig interpretiert.
Beispiel
1 2 3 4 5 6 7 8 9 10 11 12 |
// ✓ - Ist Kompilierbar let pi = 3.14159 struct Quadrat { let seitenlänge: Double } var einQuadrat = Quadrat(seitenlänge: 123.0) // ✖︎ - Geht nicht in Swift 1.1 let konstanterString: String konstanterString = "1234" |
static vs. class
Eine sehr sinnvolle und längst überfällige Veränderung ist das jetzt vereinheitlichte Deklarieren von statischen Methoden und Funktionen. War es vorher nötig, statische Methoden in Klassen und Protokollen mit class zu deklarieren, kann jetzt wie schon für Strukturen gültig das Schlüsselwort static genutzt werden. Abgerundet wird das Ganze durch das jetzt mögliche Deklarieren von statischen Properties in Klassen selbst.
Das folgende Beispiel, welches nur eine Darstellung der Möglichkeit ist, verdeutlicht die neue Syntax und Möglichkeiten.
Swift 1.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
protocol CounterProtocol { class func resetCounter() } struct CounterStruct: CounterProtocol { static var Count: Int = 0 static func resetCounter() { CounterStruct.Count = 0 } } ++CounterStruct.Count // 1 ++CounterStruct.Count // 2 CounterStruct.resetCounter() // 0 |
Swift 1.2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
protocol CounterProtocol { static func resetCounter() } class CounterClass: CounterProtocol { static var Count: Int = 0 static func resetCounter() { CounterClass.Count = 0 } } ++CounterClass.Count // 1 ++CounterClass.Count // 2 CounterClass.resetCounter() // 0 |
as as? as!
Bei einem expliziten Downcast – wie in unserem Folgebeispiel zu sehen – ist es ab jetzt nötig, den bisherigen as-Operator, mit einem ! zu versehen – as!. Dies soll Entwickler explizit darauf hinweisen, dass dieser Downcast fehlschlagen kann und dadurch die Applikation mit einem Laufzeitfehler abstürzen würde. Für einen Upcast kann weiterhin der bisher bekannte as-Operator genutzt werden, da ein Upcast zu Basistypen immer sicher ist in Swift.
Aus unserer Sicht eine sehr wertvolle Anpassung, da hier jetzt Klarheit herrscht und das Verhalten dem generellen Vorgehen mit Optionals angepasst wurde.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class Mensch { } class Nerd: Mensch { } class ÜberNerd: Nerd { } // Downcast let nerdImMenschPelz: Mensch = Nerd() let nerdOhneMenschPelz = nerdImMenschPelz as! Nerd // Upcast let derÜberNerd = ÜberNerd() let überNerdImNerdPelz = derÜberNerd as Nerd |
Explizite Casts beim Bridging
Swift machte optisch für den Anwender bisher keinen Unterschied zwischen NSString & String oder NSArray & Array, wenn wir mit Objective-C Bibliotheken gearbeitet haben. Damit ist ab Swift 1.2 jetzt aber Schluss. Möchte man einen NSString an eine Funktion übergeben, die einen Swift String erwartet, so muss dieser ab jetzt explizit zu einem Swift String gecasted werden. Andernfalls erhält man einen Kompilierfehler
Interessanterweise gilt dies nicht wenn man einen Swift String an eine Methode übergibt, die einen NSString erwartet. Zusätzlich kann man zum Initialisieren von einem NSString in Swift, weiterhin den Literal „“ nutzen.
Swift 1.1
1 2 3 4 5 6 7 8 |
import Foundation func logSwiftString(var logString: String) { println(logString) } let meinNSString: NSString = "Mein NSString" logSwiftString(meinNSString) |
Swift 1.2
1 2 3 4 5 6 7 8 |
import Foundation func logSwiftString(var logString: String) { println(logString) } let meinNSString: NSString = "Mein NSString" logSwiftString(meinNSString as! String) |
@autoclosure
Das Schlüsselwort @autoclosure wurde bei der Methodendeklaration bisher hinter den Variablennamen geschrieben. Ab Swift 1.2 muss dieser vor den Variablennamen bei der Parameterdefinition geschrieben werden.
Swift 1.1
1 |
func oder(a: Bool, b: @autoclosure () -> Bool) -> Bool { ... } |
Swift 1.2
1 |
func oder(a: Bool, @autoclosure b: () -> Bool) -> Bool { ... } |
Benannte Parameter für Curry Funktionen
Eine sehr schöne Vereinheitlichung ist das jetzt mögliche Angeben von Parameternamen bei Curry Funktionen. Ausserdem muss jetzt der Rückgabetyp angegeben werden. Curry Funktionen sind bisher nicht wirklich übersichtlich und vor allem für Einsteiger etwas sehr Ungewöhnliches. Mit den jetzt benannten Parameternamen wird zumindest eine erste optische Hürde optimiert und die Lesbarkeit deutlich verbessert!
Swift 1.1
1 2 |
func addiere(faktorA: Int)(_ faktorB: Int) { return faktorA + faktorB } addiere(1)(2) |
Swift 1.2
1 2 3 4 |
func addiere(#faktorA: Int)(faktorB: Int) -> Int { return faktorA + faktorB } addiere(faktorA: 1)(faktorB: 2) |
Swift 1.1 zu 1.2 Konverter
Damit der Umstieg von Swift 1.1 nahezu nahtlos auf Swift 1.2 erfolgen kann, bietet Apple bereits jetzt in Xcode die Möglichkeit seinen Programmcode automatisch konvertieren lassen zu können. Hierfür müssen Sie nichts weiter tun, als Ihr Projekt mit Xcode 6.3 zu öffnen. Anschließend klicken Sie auf das Menü „Edit“ in dem Sie unter dem Menüpunkt „Convert“ den Punkt „To Swift 1.2“ finden. Dieser enthält einen Migrationsassistenten der Sie beim konvertieren unterstützen wird.
Objective-C
Da Swift sehr stark mit den iOS und Mac OS X Frameworks arbeitet, welche überwiegend auf Objective-C basieren, hat Apple an einigen Ecken und Enden nachgefeilt. Damit die Interoperabilität verbessert wird, hat Apple einige neue Schlüsselwörter eingeführt, welche bei Methodendeklarationen in Objective-C benutzt werden können. Diese Schlüsselwörter sollen Klarheit darüber verschaffen, ob Objekte die Übergeben oder als Rückgabewert definiert sind, auch tatsächlich zurück geliefert werden könnten.
In Objective-C ist es nicht umbedingt unüblich, Objekte oder Pointer an Methoden zu übergeben. Als Beispiel sei hier zum Beispiel ein Pointer auf NSError* genannt, welchen man an Methoden übergeben kann um Optional über Fehler informiert zu werden. Aber auch Failable Initializer sind keine Seltenheit. Mit den neuen Keywords __nonnull , nullable oder null_unspecified kann dies nun ganz genau zur Hilfe für Swift definiert werden. Aber auch Objective-C selbst profitiert davon, denn gerade für Hersteller von Frameworks oder statischen Bibliotheken kann es sehr sinnvoll sein, ihren Code für uns lesbarer zu machen.
nonnull – Der Wert wird niemals als nil erwartet. Z.B. UINib (Bei Funktionsargumenten kann es Ausnahmen geben)
nullable – Der Wert kann nil sein. Z.B. UITableViewCell?
null_unspecified – Der Werte könnte nil sein, soll er aber nicht. Es ist also unbekannt. Z.B. NSDate!
null_resettable
Properties können jetzt in Objective-C mit null_resettable konfiguriert werden. Dies weist darauf hin, dass die Setter auch nil als Wert akzeptieren. Das ist zum Beispiel dann sinnvoll, wenn ein Standardverhalten oder Default-Wert in Kraft treten soll. Apple nutzt als einfache Erklärung das tintColor Property von UIView. Ist dieses gesetzt, erhalten alle Subviews ebenfalls diese Eigenschaft. Ein Subview kann aber individuell die Farbe überschreiben, wodurch dessen Subviews ebenfalls die neu gesetzte Farbe annehmen. Sollte ein View keine solche tintColor gesetzt haben, geht es solange in der View Hierarchie nach oben, bis entweder ein Farbwert gefunden wird oder am Ende der Rekursion nil zurück kommt.
Objective-C
1 |
@property (nonatomic, retain, null_resettable) UIColor *tintColor; |
Swift equivalent
1 |
var tintColor: UIColor! |
Fazit
Der Changelog weist noch jede Menge weitere Punkte auf, die zumeist Bugfixes sind. Es gibt noch einige weitere Punkte betreffend Objective-C und der C Integration, die meisten betreffen einen normalen Entwickler aber im Alltag nicht.
Wir persönlich sind sehr zufrieden mit den Veränderungen und hoffen weiterhin auf sinnvolle Optimierungen zu Gunsten der Produktivität und der Lesbarkeit. Es bleibt zu hoffen, dass Apple weiterhin mit Volldampf voran fährt, sodass auch wir in den kommenden Monaten den Ritterschlag für Swift erteilen können. Selbstverständlich erhalten, wie Eingangs bereits erwähnt, Käufer unseres Buches Durchstarten mit Swift in den kommenden Tagen und Wochen ein Update für ihr Buch. Lasst euch überraschen =)
Wo gibt es Xcode 6.3?
Um Xcode 6.3 Beta 1 installieren zu können benötigen Sie einen iOS Developer Account der aktuell mit 99€ pro Jahr zu Buche schlägt. Mit relativ wenig Aufwand lässt sich das Installationspacket Xcode_6.3_beta.dmg oder der Changelog Xcode_6.3_beta_Release_Notes.pdf im Internet aber finden.
Ab wann ist Swift 1.2 relevant?
Aktuell hat Apple eine Xcode 6.2 Beta und Xcode 6.3 Beta im Umlauf. Wir gehen persönlich davon aus, dass mit der Veröffentlichung von Apple Watch Xcode 6.3 veröffentlicht wird. Ein Veröffentlichung zur WWDC im Juni wäre ebenfalls nicht unrealistisch, sicher kann dies aber niemand sagen. Mit ein bisschen Glück können wir Entwickler hoffen, dass Swift 1.2 ebenfalls in Xcode 6.2 integriert sein könnte. Wir sind gespannt!
2 Comments
Da steht einmal null_unspecified und einmal null_specified – ich glaub das sollte beidesmal null_unspecified heißen.
Super! Vielen Dank für den Hinweis!
Du hasst natürlich recht =)
Ich habe den Artikel angepasst.