Neu in Swift 5

Am 26. März wurde Swift 5 fertiggestellt. Dieser Beitrag fasst die wichtigsten Neuerungen zusammen. Swift 5 gilt insofern als »Meilenstein«, als Apple erstmalig die binäre Kompatibilität der Schnittstellen garantiert. Die größte Auswirkung in der Praxis besteht darin, dass Standardbibliotheken nun auf Apple-Geräten installiert werden und nicht mehr mit jeder App mitgeliefert werden müssen. Xcode erzeugt daher kleinere Binärdateien.

Update 30.3.2019: Swift 5 ist offiziell im Docker-Hub angekommen.

ABI-Stabilität

Das Application Binary Interface (ABI) beschreibt die Schnittstelle zwischen Programmen bzw. Bibliotheken auf binärer Ebene, also zur Laufzeit. Im Unterschied dazu betrifft das Application Programming Interface (API) »nur« den Quellcode.

Das vermutlich wichtigste Ziel bei der Entwicklung von Swift 5 war die Erreichung der ABI-Stabilität. Ein mit Swift 5 kompiliertes Programm kann sich also darauf verlassen, dass die Zusammenarbeit mit einer für Swift 5 kompilierten Bibliothek funktioniert — auch dann, wenn die App mit Swift 5.0 kompiliert ist, die (z.B. unter iOS installierte) Bibliothek aber bereits mit Swift 5.1.

Das Erreichen der ABI-Stabilität hat insofern große praktische Auswirkungen, als Swift-Bibliotheken nun direkt als Teil von iOS, macOS usw. ausgeliefert werden. In Swift entwickelte Apps können sich darauf verlassen, dass die Bibliotheken zur Verfügung stehen. Es ist nicht mehr möglich, mit jeder App diverse Bibliotheken mitzuliefern, so dass die Apps selbst kleiner werden. Besonders stark bemerkbar macht sich das bei kleinen Apps. Einige Beispiele dafür, wie groß die Platzersparnis voraussichtlich sein wird, hat 9to5mac gesammelt.

https://swift.org/abi-stability
https://swift.org/blog/abi-stability-and-more/
https://theswiftdev.com/2018/11/06/swift-5-and-abi-stability
https://en.wikipedia.org/wiki/Application_binary_interface

Raw-Zeichenketten

Mitunter soll eine Zeichenkette as is angegeben werden. Ein Backslash soll ein Backslash bleiben und keine Zusatzfunktionen entfalten. Auch Anführungszeichen sollen erhalten bleiben. Diese Möglichkeit bietet Swift nun mit Raw-Strings. Die Syntax ist häßlich, aber sie funktioniert:

let sql = #"SELECT * FROM table WHERE column="ab""#
let winfn = #"C:\verzeichnis\name.txt"#

Zeichenketten werden also zusätzlich von einem #-Zeichen umschlossen. Das hat folgende Auswirkung:

  • Innerhalb der Zeichenkette sind Anführungszeichen erlaubt. (Erst "# beendet die Zeichenkette.)
  • Sequenzen wie \n oder \u{1234} haben nicht die übliche Wirkung, sondern werden as is übernommen.
  • String-Interpolation wie \(ausdruck) funktioniert nicht mehr.

Die neue Syntax ist auch für mehrzeilige Zeichenketten erlaubt:

let sql = #"""
    SELECT *
    FROM table
    WHERE column="abc"
    """#

So weit, so gut. Es gibt nun aber noch diverse Sonderfälle. Wenn in einem Raw-String doch String-Interpolation verwendet werden soll, lautet die Syntax hierfür \#(ausdruck), also:

let id = 17
let sql = #"SELECT * FROM t WHERE col="abc" AND id=\#(id)"#
print(sql)
// Ausgabe: SELECT * FROM t WHERE col="abc" AND id=17

Für den Fall, dass in der Zeichenkette "# vorkommt, kann die Anzahl der #-Zeichen vor und nach der Zeichenkette vergrößert werden.

let special = ##"abc"#efg"##
print(special)   // Ausgabe: abc"#efg

Auch dieser Fall kann mit String-Interpolation verbunden werden, dann eben mit \##(ausdruck). Noch mehr Spitzfindigkeiten und Sonderfälle sind hier dokumentiert:

https://github.com/apple/swift-evolution/blob/master/proposals/0200-raw-string-escaping.md

Result-Datentyp

Die Swift-Standardbibliothek definiert den neuen Datentyp Result:

public enum Result<Success, Failure: Error> {
    case success(Success), failure(Failure)
}

Result ist vor allem für asynchrone Algorithmen gedacht, die wahlweise ein Ergebnis liefern oder einen Fehler auslösen. Der Einführung von Result sind schier endlose Diskussionen im Swift-Forum vorangegangen (siehe initial review, second review und acceptance). Erst der zweite Entwurf wurde nach weiteren Modifikationen akzeptiert. In der Diskussion wurde u.a. der Einwand eingebracht, dass Result eine halbherzige Lösung im Hinblick auf die für Swift 6 geplanten asynchronen Spracherweiterungen sind. Man hat sich aber darauf geeinigt, dass Result bis dahin auf jeden Fall besser als gar keine Lösung ist.

Details zur Implementierung sowie Anwendungsbeispiele finden Sie hier:

https://github.com/apple/swift-evolution/blob/master/proposals/0235-add-result.md

Optional-Flattening mit try?

Wenn Sie in Swift 4 mit try? einen Ausdruck verarbeiten, der selbst ein Optional zurückgibt, erhalten Sie als Ergebnis ein doppeltes (verschachteltes) Optional. Das macht die weitere Verarbeitung recht mühsam.

In Swift 5 werden hingegen alle Optional-Ebenen verflacht. Das Resultat ist ein einfaches Optional. Es hat entweder einen sinnvollen Wert oder nil.

enum MyErrors : Error {
  case tooBig
}
func f(_ n: Int) throws -> Int? {
  if n < 0   { return nil}
  if n > 100 { throw MyErrors.tooBig }
  return n + 1
}
let result = try? f(1000)
// Swift 4: result hat den Typ Int??
// Swift 5: result hat den Typ Int?

https://github.com/apple/swift-evolution/blob/master/proposals/0230-flatten-optional-try.md

Sonstiges

  • Exclusivity Enforcement: Schon seit Swift 4 versucht der Compiler, fehlerhafte Parallelzugriffe auf Variablen zu entdecken. Diese Tests, die vor allem Fehler in multiläufigen Algorithmen vermeiden sollen, sind in Swift 5 noch strenger geworden. Details können Sie hier nachlesen: https://swift.org/blog/swift-5-exclusivity

  • SIMD-Unterstützung (Single Instruction, Multiple Data): SIMD ist eine Low-Level-Bibliothek um mit Vektoren fixer Größe zu arbeiten (simd/simd.h). Diese Bibliothek kann nun auch von Swift genutzt werden. Der resultierende Code ist nicht sonderlich elegant; Entwickler, die mathematische Algorithmen in Swift effizient implementieren wollen, werden sich über das Feature trotzdem freuen. Details siehe: https://github.com/apple/swift-evolution/blob/master/proposals/0229-simd.md

  • Dynamically callable types: Swift bietet einen neuen Weg zur Definition von Typen, die wie Funktionen aufgerufen werden können. Für »gewöhnliche« Swift-Programmierer ist das Feature von geringer Relevanz. Es verbessert aber das Zusammenspiel mit anderen Programmiersprachen, z.B. wenn ein Swift-Programm auf Python-Bibliotheken zurückgreifen will. Details: https://github.com/apple/swift-evolution/blob/master/proposals/0216-dynamic-callable.md

  • @unkown-Attribut zur Auswertung von Enumerationen: Die Auswertung von Enumerationswerten in switch mit einem default-Zweig kann problematisch sein, wenn die Enumeration später erweitert wird: Dann gilt der default-Zweig für mehr Fälle als ursprünglich gedacht. Abhilfe schafft das Attribut @unknown für den Default-Zweig, das eine Warnung auslöst, wenn die externe Enumeration erweitert wurde. Bitte beachten Sie, dass dieses Feature nicht für selbst definierte Enumerationen gedacht ist und daher mit solchen Enumerationen auch nicht sinnvoll getestet werden kann! Es ist nur für Enumerationen gedacht, die sich außerhalb der Kontrolle des Entwicklers z.B. in irgendwelchen Bibliotheken befinden. Details: https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md

  • count(where:) zählt alle Elemente, die eine Bedingung erfüllen (Details). Dieses Feature stand in den Betas zur Verfügung, wurde dann aber wieder entfernt.

  • compactMapValues verändert die Values eines Dictionaries und eliminiert alle Key/Value-Paare, deren Werte nil sind. compactMapValues ist eine Kombination aus mapValues und filter (Details).

  • Die Methode index(of:) wurde umbenannt in firstIndex(of:).

  • Die Methode isMultiple(of:) testet, ob eine Zahl ein Vielfaches einer anderen Zahl ist. n.isMultiple(of: 4) entspricht n % 4 == 0.

Beachten Sie, dass bereits in Swift 4.1 und 4.2 diverse Änderungen/Erweiterungen der Standardbibliothek durchgeführt sowie einige Swift-Neuerungen eingeführt wurden (automatische Implementierung von Equatable und Hashable, Zufallszahlen, compactMap, toggle, allSatisfy, removeAll etc.). Die wichtigsten Neuerungen habe ich in diesem Blog in den Artikeln Swift 4.1 und Swift 4.2 zusammengefasst.

Swift mit Linux/Docker

Apple stellt wie bisher Swift-Binärpakete für Ubuntu 14.04, 16.04 und 18.04 zur Verfügung. Neu ist, dass es für die Swift-Version 4.2 ab sofort geregelte monatliche Updates gibt (siehe diese Ankündigung im Swift-Forum). Es ist zu erwarten, dass es derartige Releases auch für Swift 5 geben wird, sobald diese Version fertig gestellt ist.

Leider kann sich Apple weiterhin nicht aufraffen, Swift als APT- oder YUM-Paketquelle anzubieten. Das würde die Installation und Wartung natürlich stark vereinfachen.

Etwas verbessert hat sich die Docker-Unterstützung: Aus meiner Sicht war Docker schon seit jeher der beste Weg, um Swift unter verschiedenen Linux-Distributionen sowie unter Windows zum Laufen zu bringen. Mittlerweile gibt es im Dockerhub offizielle Swift-Images — seit Ende März 2019 endlich auch für Version 5.

Praxis

Der Umbau der Beispielprogramme meines Buchs von Swift 4 auf Swift 5 hat sich bisher als erstaunlich unkompliziert erwiesen: Viele Änderungen in Swift 5 sind Zusatz-Features und bedürfen gar keiner Anpassung am Code. Die wenigen doch erforderlichen Modifikationen erkannte der in Xcode integrierte Code-Konverter sehr zuverlässig.

Ganz generell richten sich sehr viele Neuerungen, die in Swift seit Version 3 eingeflossen sind, an die Entwickler von (Standard-)Bibliotheken. Für den »normalen« App-Entwickler bzw. die Entwicklerin sind viele Features nur von geringer Relevanz.

Vorschau auf die Neuauflage meines Buchs

Die Neuauflage meines Swift-Buchs wird voraussichtlich im Mai erscheinen. In dem Buch berücksichtige ich natürlich alle wichtigen Neuerungen von Swift 5. Ein neues Kapitel behandelt auf vielfachen Leserwunsch Core Data und SQLite. Stark überarbeitet habe ich den Abschnitt zur Lokalisierung von Apps. (Xcode zickt dabei wie eh und je …) Außerdem gibt es da und dort ein paar neue Beispiele (Slider-Puzzle-App, macOS Dark Mode etc.)

Das Buch kann bereits beim Rheinwerk Verlag oder bei Amazon vorbestellt werden. Die E-Book-Ausgabe bzw. das Bundle aus Buch und E+Book gibt es allerdings nur beim Rheinwerk Verlag!

Quellen/Links