10 Top-Features in Swift

10 Top-Features in Swift

Avatar von Christopher Stock

Im ersten Teil dieser Artikelserie über Swift möchte ich Ihnen einen schnellen Einblick in die Programmiersprache Swift geben und die 10 interessantesten syntaktischen Besonderheiten und innovativsten Features vorstellen, die Swift von vielen anderen Hochsprachen unterscheidet.

In folgenden Artikeln tauchen wir gemeinsam in einen praktischen Workshop mit dem Apple SpriteKit ein, schauen uns die Neuerungen der kommenden Major-Version an und behandeln einige der häufigsten Problemlösungen, die uns im täglichen Einsatz mit Swift begegnen.

Was ist Swift?

Swift ist eine Programmiersprache aus dem Hause Apple, die für die Entwicklung von Software für die wichtigsten Apple-Produkte, darunter iPhone, iPad, iMac und MacBook, verwendet wird.

Als Apple die Sprache Swift der Öffentlichkeit 2014 vorstellte, überraschte sie Experten mit ihren innovativen Features und ihren revolutionären Konzepten. In ihrer aktuellen Version 4.2 ist die Sprache technisch so ausgereift, dass das Programmieren damit nicht nur riesigen Spaß macht, sondern auch das Schreiben von High-Quality-Code mit einem hohen Maß an Pragmatismus und Lesbarkeit fördert.

Xcode und der Playground

Um Swift in der Praxis auszuprobieren, benötigen wir lediglich Apples primäre Entwicklungsumgebung Xcode. Diese IDE kann kostenfrei über Apples App Store heruntergeladen und installiert werden. Nach dem Start von Xcode kann mit „Get started with a playground“ ein neues Projekt erstellt werden, in dem wir die Syntax und die coolsten Features von Swift 4.2 direkt ausprobieren können.
 

1. Verzicht auf Semikolons

Swift kommt ohne ein abschließendes Semikolon am Zeilenende aus. Der Einsatz von Semikolons ist lediglich dann erforderlich, wenn mehrere Anweisungen innerhalb einer Zeile formuliert werden, was die meisten Coding Styles aber ohnehin als Bad Practice ansehen:

 
    // logs one line of output to the debug console
    print( "Welcome to Swift" )

    // logs three lines of output to the debug console
    print( "Welcome" ); print( "to" ); print( "Swift" )
 

2. Parameter Labeling

Standardmäßig verlangt Swift beim Aufruf einer Funktion, dass jedes Argument mit dem Namen des zugehörigen Funktionsparameters angeführt wird. Obwohl diese Anforderung zum Schreiben von mehr Code zwingt, wirkt sie sich positiv auf die Lesbarkeit des Programmcodes aus:

 
    func interpolate( start:Double, end:Double, ratio:Double ) -> Double
    {
        return ( start + ( ( end - start ) * ratio ) );
    }

    // parameter names must be specified
    let result:Double = interpolate( start: 10, end: 20, ratio: 0.75 )

    // logs '17.5' to the debug console
    print( result )
 

Optional kann für jeden internen Parameternamen ein zusätzlicher öffentlicher Parametername definiert werden, dessen Name sich von dem des internen unterscheidet:

 
    func interpolate( x start:Double, y end:Double, ratio:Double ) -> Double
    {
        // internal parameter names are used here
        return ( start + ( ( end - start ) * ratio ) )
    }

    // public parameter names must be specified
    let result:Double = interpolate( x: 10, y: 20, ratio: 0.75 )
 

Die obligatorische Angabe des Parameternamens beim Aufruf einer Funktion kann für bestimmte Parameter explizit deaktiviert werden, indem ein Unterstrich als öffentlicher Parametername vergeben wird. Diese Technik bietet sich für selbsterklärende Funktionen an, bei denen die Angabe des Parameternamens beim Funktionsaufruf keinen Mehrwert für die Lesbarkeit darstellt:

 
    func toInt( _ value:Double ) -> Int
    {
        return Int( value )
    }

    // parameter name must be omitted
    let result:Int = toInt( 7.89 )

    // logs '7' to the debug console
    print( result )
 

3. Type Inference

Das strikte und statische Typensystem von Swift stellt sicher, dass eine typisierte Variable nie mit dem Wert eines anderen Datentyps belegt werden darf und schützt den Entwickler damit vor Laufzeitfehlern.

Trotzdem macht Swift die explizite Definition von Typdeklarationen überflüssig, indem der Swift-Compiler anhand des zugewiesenen Wertes Rückschlüsse auf den erforderlichen Datentyp einer Variable zieht und diesen inferiert:

 
    var meaningOfLife = 42  // infere type 'Int'
    meaningOfLife = 23      // compiles smooth

    meaningOfLife = "Hello" // won't compile!
 

Darüber hinaus ist die explizite Spezifikation eines Datentyps optional bei der Deklaration einer Variable. Die folgende Zuweisung ist daher äquivalent und ebenso gültig:

 
    var meaningOfLife:Int = 42
 

4. Kein Fallthrough in Switch-Cases

Das aus anderen Sprachen gewohnte und fehleranfällige fallthrough-Verhalten in case-Statements existiert in Swift per Design nicht. Somit kann auf das Hinzufügen von break-Statements gänzlich verzichtet und Code wie der folgende formuliert werden:

 
    enum Direction
    {
        case Left
        case Right
    }

    var playerX = 10.0
    let stepSize = 2.5

    func updatePosition( _ direction:Direction ) -> Void
    {
        switch ( direction )
        {
            case .Left:
                playerX -= stepSize

            case .Right:
                playerX += stepSize
        }
    }

    updatePosition( Direction.Right )
    updatePosition( Direction.Right )
    updatePosition( Direction.Right )
    updatePosition( Direction.Right )
    updatePosition( Direction.Left )

    // logs '17.5' to the output console
    print( playerX )
 

5. Umfassende Switch-Statements

switch-Statements müssen alle Zustände einer Variable abdecken, andernfalls reagiert der Compiler mit einem Fehler. So muss bei einem switch nach einer Variable vom Typ Int oder String immer ein default-case angegeben werden und bei einem switch nach einer Variablen vom Typ Enumeration alle Enumerationskonstanten abgedeckt oder alternativ ein default-case definiert werden:

 
    enum Direction
    {
        case Left
        case Right
        case Up
        case Down
    }

    func updatePosition( _ direction:Direction ) -> Void
    {
        switch ( direction )
        {
            case .Left:
                playerX -= stepSize

            case .Right:
                playerX += stepSize

            default:
                print( "TODO player Y movements are not implemented yet" )
        }
    }
 

6. Optionals

Meiner Meinung nach hat Swift den Umgang mit nil-Werten, in anderen Sprachen besser bekannt als null-Werte, sehr pragmatisch gelöst: Jede Variable muss bei deren Deklaration oder im Konstruktor der zugehörigen Kompilationseinheit einen Wert zugewiesen bekommen, der nicht nil ist – andernfalls reagiert der Compiler darauf mit einem Fehler.

Per Design kann es natürlich vorkommen, dass man den Wert einer Variablen bei deren Deklaration oder im Konstruktor noch nicht belegen kann oder will, da deren Wert hier noch nicht bekannt oder verfügbar ist. Für den Fall, dass eine Variable mit dem Wert nil vorbelegt werden muss, verlangt Swift, dass diese Variable explizit mit einem optionalen Datentyp deklariert wird. Hierfür muss der für die Variable angegebene Datentyp von einem Fragezeichen begleitet werden:

 
    var firstName:String = "Christopher" // firstName is a non-optional String
    firstName = nil                      // won't compile!

    var lastName:String? = "Stock"       // lastName is an optional String
    lastName = nil                       // compiles smooth
 

Optionals werden in Swift als ein eigener Datentyp gehandhabt. Daher kann der Swift-Compiler beim Zuweisen von Werten an Variablen Verletzungen des Typensystems erkennen:

 
    // won't compile:
    // Value of optional type 'String?' must be unwrapped to a value of type 'String'
    firstName = lastName
 

7. Asserted Variable States

Um auf Variablen eines optionalen Datentyps zugreifen zu können, müssen diese explizit ausgepackt werden. Dieser Vorgang wird als unwrapping oder auch unboxing bezeichnet und stellt sicher, dass eine Variable bei deren Zugriff nicht nil ist. Swift bietet hierfür zwei eigene Sprachkonstrukte an:

Unwrapping mit if let

Das if let-Statement überführt ein Optional in eine lokale Variable, die anschließend innerhalb des if let-Körpers verwendet werden kann. Diese neu aufgestellte lokale Variable darf dabei sogar den selben Namen haben wie die Variable, die ausgepackt werden soll. Der Körper des if let-Statements wird nur dann ausgeführt, wenn der Wert des Optionals nicht nil ist:

 
    func printUppercased( msg:String? ) -> Void
    {
        if let msg = msg
        {
            print( msg.uppercased() )
        }
    }

    // logs 'HELLO SWIFT' to the output console
    printUppercased( msg: "Hello Swift" )

    // doesn't log to the output console
    printUppercased( msg: nil )
 

Unwrapping mit guard else

Im Gegensatz zum if let-Statement kann mit dem guard else-Konstrukt ein Optional in eine lokale Variable überführt und dabei ein Körper ausgeführt werden, wenn der Wert des Optionals nil ist:

 
    func printUppercased( msg:String? ) -> Void
    {
        guard let msg = msg else { return }

        print( msg.uppercased() )
    }

    // logs 'HELLO SWIFT' to the output console
    printUppercased( msg: "Hello Swift" )

    // doesn't log to the output console
    printUppercased( msg: nil )
 

Obwohl diese Anweisungen erstmal ziemlich gewöhnungsbedürftig erscheinen, ist deren Vorteil klar ersichtlich: Bereits der Compiler erkennt und überprüft das Zuweisen von nil-Werten, sodass der Entwickler keine eigene Prüfung auf nil einbauen oder eine NullPointerException abfangen muss.

8. Open for Extension

Alle Funktionen und Typen können in Swift erweitert werden, ohne dass hierfür eine eigene Kompilationseinheit abgeleitet werden muss. Mithilfe des Schlüsselworts extension kann jedes bestehende Element mit zusätzlicher Funktionalität angereichert werden, egal ob es sich dabei um eigene oder um grundlegende Einheiten handelt. Hier ein triviales Beispiel für die Erweiterung der nativen Klasse String:

 
    extension String
    {
        func simon()
        {
            print( "Simon says '" + self + "'" )
        }
    }

    // logs "Simon says 'Hello Swift'" to the output console
    "Hello Swift".simon()
 

Da in Swift auch globale Funktionen überschrieben werden können, ermöglicht uns dies Konstruktionen wie beispielsweise das Überschreiben des print-Statements und dessen bedingter Außerkraftsetzung:

 
    var enableLogs = true

    func print( _ msg:Any )
    {
        if ( enableLogs )
        {
            Swift.print( msg )
        }
    }

    // logs 'Hello Swift' to the output console
    print( "Hello Swift" )

    // logs nothing to the output console
    enableLogs = false
    print( "Hello Swift" )
 

9. Konstante Funktionsparameter

Viele Coding Styles sehen das Neuzuweisen von Funktionsparametern als Bad Practice an, da diese Praxis bei der Übergabe von Referenzen zu unerwünschtem Verhalten führen kann. Swift löst dieses Problem konsequent, indem es alle Parameter innerhalb einer Funktion als Konstanten definiert, deren Neuzuweisung mit einem Compilerfehler quittiert wird:

 
    func square( x:Int ) -> Int
    {
        // won't compile!
        x *= x

        return x
    }
 

Dieser Fehler kann aufgelöst werden, indem eine sprechende lokale Variable eingeführt wird, die zudem auf die Lesbarkeit dieser Funktion einzahlt:

 
    func square( x:Int ) -> Int
    {
        let squareValue = ( x * x )

        return squareValue
    }
 

10. UTF-8 Namings und eigene Operatoren

In Swift können alle Character aus dem Unicode-Zeichensatz zur Benennung von Variablen, Funktionen, Operatoren und Kompilationseinheiten verwendet werden. Darüber hinaus erlaubt Swift die Definition eigener Operatoren, die als Präfix-, Postfix- oder Infix-Operatoren fungieren können. Im folgenden Beispiel wird ein Infix-Operator erstellt, der zwei Strings konkateniert und mit einem einzelnen Unicode-Character benannt wird:

 
    // specify operator name and position ( prefix, infix or postfix )
    infix operator ♥
    func ♥( left:String, right:String ) -> String
    {
        return ( left + " loves " + right )
    }

    // logs 'Christopher loves Swift' to the output console
    print( "Christopher" ♥ "Swift" )
 

Da der Einsatz eigener Operatoren die Interoperabilität unseres Programmcodes erschweren und auch dessen Lesbarkeit eher verschlechtern kann, rate ich von deren Einsatz in quelloffenem Code ab. Trotzdem kann es praktisch und vereinfachend sein, sie in abgeschlossenen und nicht öffentlichen Komponenten unseres Programms einzusetzen.

Mehr Swift!

Das waren einige von Swifts Vorteilen und spannendsten syntaktischen Neuerungen, die diese Programmiersprache von vielen anderen Sprachen abhebt. Meiner Meinung nach handelt es sich bei Swift um eine sehr gut konzipierte und durchdachte Programmiersprache, die die Stärken ihres Vorgängers Objective-C ausgebaut und sich in ihrer aktuellen Version 4.2 sehr gut entwickelt hat.

Der Release der Major-Version 5.0 von Swift wurde für 2019 angekündigt. Sobald dieser erfolgt, stelle ich Ihnen die wichtigsten Neuerungen dieser Version hier im Mayflower-Blog vor. In weiteren Artikeln dieser Serie werfen wir einen Blick auf die Swift-IDE AppCode von JetBrains und behandeln Lösungen für die häufigsten Problemstellungen mit Swift.

Swift auf dem Developer Camp 2019

Vielleicht konnte ich Sie mit meinem Artikel sogar dazu begeistern, selbst einmal Swift in der Praxis auszuprobieren? Auf dem Developer Camp 2019 in Würzburg biete ich zusammen mit meinem Kollegen Sebastian Sellmeier einen praktischen Einstieg in die Programmierung mit Swift und dem Apple SpriteKit an. Einsteiger und Fortgeschrittene können hier die Grundlagen der Anwendungsentwicklung mit Xcode und Swift erlernen. Das praktische Vorgehen des Workshops werde ich darüberhinaus in einem Folgeartikel dieser Blogserie festhalten

Ich würde mich sehr freuen wenn ich Ihnen einen kurzen Einblick in diese interessante Programmiersprache aus dem Hause Apple geben konnte. Über Feedback oder Rückfragen zu Swift oder dieser Blogserie können Sie mich sehr gerne über christopher.stock@mayflower.de erreichen.

Apple, App Store, iPad, iPhone, iMac, MacBook, Swift, Xcode und ihre Logos sind eingetragene Marken von Apple Inc in den USA und anderen Ländern.
Foto von Flo Maderebner von Pexels

In eigener Sache …

Mit WebAssembly die Kosten für die Wartung Deiner Cloud-Anwendung sparen und die Wirtschaftlichkeit und Effizienz des Unternehmens steigern?

Am 26. September 2024 um 11:30 Uhr bieten Dir unsere Experten einen tiefen Einblick in ein spannendes R&D-Projekt.

Melde Dich jetzt kostenlos an!

Avatar von Christopher Stock

Kommentare

Schreibe einen Kommentar

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


Für das Handling unseres Newsletters nutzen wir den Dienst HubSpot. Mehr Informationen, insbesondere auch zu Deinem Widerrufsrecht, kannst Du jederzeit unserer Datenschutzerklärung entnehmen.