Holger Hinzberg iOS-Apps mit Swift 2 Das umfassende Praxis-Handbuch Bibliografische Information der Deutschen Nationalbibliothek Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über <http://dnb.d-nb.de> abrufbar. ISBN 978-3-95845-222-0 1. Auflage 2016 www.mitp.de E-Mail: [email protected] Telefon: +49 7953 / 7189 - 079 Telefax: +49 7953 / 7189 - 082 © 2016 mitp Verlags GmbH & Co. KG, Frechen Dieses Werk, einschließlich aller seiner Teile, ist urheberrechtlich geschützt. Jede Verwertung außerhalb der engen Grenzen des Urheberrechtsgesetzes ist ohne Zustimmung des Verlages unzulässig und strafbar. Dies gilt insbesondere für Vervielfältigungen, Übersetzungen, Mikroverfilmungen und die Einspeicherung und Verarbeitung in elektronischen Systemen. Die Wiedergabe von Gebrauchsnamen, Handelsnamen, Warenbezeichnungen usw. in diesem Werk berechtigt auch ohne besondere Kennzeichnung nicht zu der Annahme, dass solche Namen im Sinne der Warenzeichen- und Markenschutz-Gesetzgebung als frei zu betrachten wären und daher von jedermann benutzt werden dürften. Lektorat: Sabine Schulz Sprachkorrektorat: Petra Heubach-Erdmann Coverbild: © Max Krasnov @fotolia.de Satz: III-satz, Husby, www.drei-satz.de Referenzkarte Swift 2 Variablen und Konstanten Schleifen // Variablen und Konstanten var variableValue = 3.1415 variableValue = 3.0 for var index = 0; index <= 5; index++ { print(index) // Ausgabe von 0 bis 5 } let constValue = 42 // Neue Zuweisung nicht möglich // constValue = 50 Fallunterscheidungen var salary = 10000 if salary > 15000 { print("Zu viel") } else if salary < 10000 { print("Zu wenig") } else { print("Ok") } var index = 4 switch index { case 1: print("Index case 2: print("Index case 3...5: print("Index case 6: print("Index fallthrough default: print("Index } for index in 0...5 // Closed Range { print(index) // Ausgabe von 0 bis 5 } for index in 0..<5 // Half-Open Range { print(index) // Ausgabe von 0 bis 4 } for index in (5...10).reverse() { print(index) // Ausgabe von 10 bis 5 } var access = 1 repeat { print(access) // Ausgabe von 1 bis 9 access++ } while access < 10 ist 1") ist 2") liegt zwischen 3 und 5") ist 6") liegt nicht zwischen 1 bis 5") Referenzkarte zum Buch »iOS-Apps mit Swift 2« von Holger Hinzberg, ISBN 978-3-95845-221-3, www.mitp.de/221, mitp-Verlag Referenzkarte Swift 2 Array var cities:[String] = ["Rom","Paris","Berlin"] cities.append("London") for city in cities { print(city) // Ausgabe aller Städtenamen } var count = cities.count // Anzahl der Elemente Methoden/Funktionen // Ohne Parameter und ohne Rückgabewert func doSomething() { print("Hello World!") } doSomething() // Mit einem Double als Parameter und einem Double // als Rückgabewert func doSomething(value:Double) -> Double { return value * 2.0 } var twice = doSomething(4.5) // Mit zwei benannten Parametern // und einem Double als Rückgabewert func doSomething( firstValue first:Double, secondValue second:Double) -> Double { return first + second } var sum = doSomething(firstValue: 3.2, secondValue: 4.7) Referenzkarte zum Buch »iOS-Apps mit Swift 2« von Holger Hinzberg, ISBN 978-3-95845-221-3, www.mitp.de/221, mitp-Verlag Referenzkarte Swift 2 Dictionary var animals:[Int: String] = [11:"Hund", 22:"Katze", 33:"Maus"] animals[22] = "Elefant" // Element ersetzen animals[99] = "Huhn" // Element anfügen print(animals[22]) // Ausgabe "Optional (Elefant)" print(animals[99]) // Ausgabe "Optional (Huhn)" animals[11] = nil // Element entfernen Zeichenketten var firstName = "Mike" var lastName = "Müller" // String-Interpolation print("Mein Name ist \(firstName) \(lastName).") // Zahlenwerte formatieren var numeric = 3.34983632 var num = String(format:"%.2f", numeric) // Ausgabe 3.35 Klassen class Person { // Eigenschaften var firstName:String var lastName:String init() { // Eigenschaften initialisieren firstName = "" lastName = "" } } // Klasseninstanz erzeugen und // Eigenschaften zuweisen var pers = Person() pers.firstName = "Mike" pers.lastName = "Müller" Enumerationen enum Color { case Red case Green case Blue } var exterior:Color = Color.Green Referenzkarte zum Buch »iOS-Apps mit Swift 2« von Holger Hinzberg, ISBN 978-3-95845-221-3, www.mitp.de/221, mitp-Verlag Referenzkarte Swift 2 Fehlerbehandlung mit try-catch // Eigene Fehlertypen definieren enum CalculationError : ErrorType { case DivideByZero case OtherError } // Funktion kann einen Fehler zurückgeben func divide(dividend:Int, divisor:Int) throws -> Int { guard divisor > 0 else { throw CalculationError.DivideByZero } return dividend / divisor } // Funktionsaufruf und Fehlerbehandlung do { var result = try divide(100, divisor: 0) } catch(CalculationError.DivideByZero) { print("Es wurde versucht, durch null zu teilen") } catch { print("Ein anderer Fehler ist aufgetreten") } Referenzkarte zum Buch »iOS-Apps mit Swift 2« von Holger Hinzberg, ISBN 978-3-95845-221-3, www.mitp.de/221, mitp-Verlag Inhaltsverzeichnis Danksagung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 Einleitung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . An wen richtet sich das Buch? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Aufbau des Buches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Teil I: Grundlagen der Sprache Swift . . . . . . . . . . . . . . . . . . . . . . . . . . Teil II: iOS-Apps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Swift ist nicht Objective-C Version 3.0 . . . . . . . . . . . . . . . . . . . . . . . . . Kommentare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Stilmittel in den Listings. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Frameworks der Apple-Plattformen . . . . . . . . . . . . . . . . . . . . . . . . Die Installation von Xcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Entwickler-Community von Apple . . . . . . . . . . . . . . . . . . . . . . . . . Webseite und Downloads zum Buch. . . . . . . . . . . . . . . . . . . . . . . . . . . 15 15 16 16 18 21 23 24 24 26 28 28 Teil I Grundlagen der Sprache Swift . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 Datentypen und Optionals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Willkommen auf dem Spielplatz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Variablen und Konstanten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zahlendatentypen konvertieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Werte runden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Minimum und Maximum. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Datentyp Bool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Optionals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Programmieren mit Optionals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 31 33 38 41 42 43 44 46 2 2.1 2.2 2.3 2.4 Zeichenketten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . String Interpolation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zeichenketten vergleichen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Textabschnitte finden und ersetzen . . . . . . . . . . . . . . . . . . . . . . . . . . . Zeichenketten in Zahlentypen konvertieren . . . . . . . . . . . . . . . . . . . . 49 50 52 54 57 5 Inhaltsverzeichnis 6 2.5 2.6 2.7 2.8 Die Klasse NSNumber. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Texte teilen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Subscripting mit Unicode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anfang und Ende von Zeichenketten. . . . . . . . . . . . . . . . . . . . . . . . . . 60 61 63 65 3 3.1 3.2 3.3 3.4 Arrays und Dictionaries. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arrays – Listen von Elementen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arrays sortieren und filtern. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dictionaries, die schnellen Wörterbücher . . . . . . . . . . . . . . . . . . . . . . Arrays aus Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 67 71 73 77 4 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 Fallunterscheidungen und Schleifen . . . . . . . . . . . . . . . . . . . . . . . . . . Die if-Struktur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die switch-case-Struktur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die for-Schleife. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Schleifen und Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Rückwärts durch die for-Schleife . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die while-Schleife . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Schleifen mit Fallunterscheidungen . . . . . . . . . . . . . . . . . . . . . . . . . . Werte als Diagramme anzeigen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Gültigkeitsbereiche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 79 85 88 90 91 93 94 97 99 5 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die erste Funktion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Benannte Parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen mit Rückgabewert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tupel und anonyme Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine unbestimmte Anzahl von Parametern. . . . . . . . . . . . . . . . . . . . . Funktionen mit Standardwerten. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Parameter sind unveränderlich. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Aliasse. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 103 107 109 111 114 117 119 121 6 6.1 6.2 6.3 6.4 6.5 6.6 6.7 Closures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Closures sind Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Closures als Parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arrays sortieren mit Closures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Variablen einfangen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Asynchrone Closures mit Grand Central Dispatch . . . . . . . . . . . . . . . Parallele Verarbeitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Asynchrone Aufrufe mittels Completion-Handler . . . . . . . . . . . . . . . 125 125 127 131 135 138 141 143 Inhaltsverzeichnis 7 7.1 7.2 7.3 7.4 7.5 7.6 7.7 Klassen und Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das erste Projekt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erste Schritte im Workspace-Fenster . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse Person . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Von der Klasse zum Objekt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eigenschaften und Punktnotation . . . . . . . . . . . . . . . . . . . . . . . . . . . . Berechnete Eigenschaften mit Getter und Setter . . . . . . . . . . . . . . . . Eigenschaften beobachten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 147 150 153 155 156 157 161 8 8.1 8.2 8.3 8.4 8.5 Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Methoden zur Initialisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Failable Initializers – Wenn es mal schiefgeht . . . . . . . . . . . . . . . . . . Methoden zu Deinitialisierung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Klassenmethoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Methoden kontra Eigenschaften. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 169 172 174 175 177 9 9.1 9.2 9.3 9.4 9.5 9.6 9.7 Vererbung und Assoziationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Erbe der Klasse Person . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erweiterungen der abgeleiteten Klasse . . . . . . . . . . . . . . . . . . . . . . . . Methoden überschreiben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Initialisierung abgeleiteter Klassen . . . . . . . . . . . . . . . . . . . . . . . . Assoziationen – Beziehungen zwischen Objekten . . . . . . . . . . . . . . . Optional Chaining . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Coalescin Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 181 184 185 187 191 195 198 10 10.1 10.2 10.3 10.4 10.5 Protokolle und Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein eigenes Protokoll entwickeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Protokolle statt Datentypen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Optionale Protokolle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Extensions mit Protokollen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 199 202 204 207 209 11 11.1 11.2 11.3 11.4 11.5 11.6 Strukturen und Enumerationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zurück zum Spielplatz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Strukturen oder Klassen? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . String Dependencies – Abhängigkeiten von Zeichenketten . . . . . . . Enumerationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verschachtelte Enumerationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Enumerationen mit Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 213 217 219 220 223 224 7 Inhaltsverzeichnis 8 11.7 11.8 Enumerationen mit begleitenden Werten . . . . . . . . . . . . . . . . . . . . . . Binärzahlen und OptionSetType-Strukturen. . . . . . . . . . . . . . . . . . . . 226 228 12 12.1 12.2 12.3 12.4 12.5 Sicherer Programmcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen absichern mit guard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . defer-Blöcke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fehlerbehandlung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fehler werfen mit throw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fehler auffangen mit catch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235 235 237 239 241 242 13 13.1 13.2 13.3 Speicherverwaltung mit Referenzzähler . . . . . . . . . . . . . . . . . . . . . . . Automatic Reference Counting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Starke und schwache Referenzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zirkelverweise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245 247 248 249 14 14.1 14.2 14.3 14.4 14.5 14.6 14.7 14.8 Robuste Anwendungen und automatisierte Tests . . . . . . . . . . . . . . . Markup-Kommentare im Playground . . . . . . . . . . . . . . . . . . . . . . . . . Kommentare in Projekten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Kommentare für die Jumpbar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Assertions – Die Behauptungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Kompilieren als Debug oder Release . . . . . . . . . . . . . . . . . . . . . . . . . . Präprozessor-Makros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Automatisiertes Testen mit Xcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tests für eigene Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251 251 254 257 258 261 264 266 269 Teil II iOS-Apps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275 15 15.1 15.2 15.3 15.4 15.5 15.6 15.7 15.8 15.9 277 277 279 282 288 289 291 295 298 302 Lotto – 6 aus 49 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . MVC: Model View Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der iOS-Simulator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Lottozahlengenerator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Interface Builder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Inspector und Bibliothek. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arbeiten mit dem Interface Builder . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zurück zum Interface Builder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das war es jetzt schon? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Inhaltsverzeichnis 16 16.1 16.2 16.3 16.4 16.5 16.6 16.7 Ein Blick hinter die Kulissen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Was verbirgt sich hinter IBAction und IBOutlet? . . . . . . . . . . . . . . . . Ein View wird geladen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Programmstart einer iOS-Anwendung . . . . . . . . . . . . . . . . . . . . . . . . Instanzen erzeugen mit Lazy Loading . . . . . . . . . . . . . . . . . . . . . . . . . Alles dreht sich . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erste Schritte mit Autolayout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Icon für die App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305 305 307 309 314 315 317 324 17 17.1 17.2 17.3 17.4 17.5 17.6 17.7 Eingaben mit virtuellen Tastaturen . . . . . . . . . . . . . . . . . . . . . . . . . . . Einsatz für Autolayout. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Auftritt für den Assistenten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tastaturen ein- und ausblenden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der First Responder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Navigation zwischen Eingabefeldern . . . . . . . . . . . . . . . . . . . . . . . . . . Aus dem View in das Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mitteilungen mit dem UIAlertView . . . . . . . . . . . . . . . . . . . . . . . . . . . 329 331 333 338 341 343 345 347 18 18.1 18.2 18.3 18.4 18.5 Schieberegler, Textformatierungen und unendliche Werte . . . . . . . . Der Schieberegler – UISlider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Daten-Model: ConsumptionCalculator . . . . . . . . . . . . . . . . . . . . . . . . Methodenaufrufe mit Absender . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Statische und dynamische Textformatierungen . . . . . . . . . . . . . . . . . Ungültige Werte erkennen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351 352 356 359 363 366 19 19.1 19.2 19.3 19.4 19.5 19.6 19.7 Storyboards – Mit dem Drehbuch durch die App . . . . . . . . . . . . . . . . Ein einfaches Storyboard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Segue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Lebenszyklus einer Szene . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Storyboards mit Datenübergabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Detailansicht und Navigation Controller . . . . . . . . . . . . . . . . . . . . . . . Vorbereitungen für den Übergang. . . . . . . . . . . . . . . . . . . . . . . . . . . . Daten für die View Controller. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371 371 375 379 381 387 391 393 20 20.1 20.2 20.3 Storyboards mit Protokollen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Protokoll für den View Controller . . . . . . . . . . . . . . . . . . . . . . . . . Das UIApplicationDelegate-Protokoll . . . . . . . . . . . . . . . . . . . . . . . . . Eine Szene für das Jahr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399 400 403 404 9 Inhaltsverzeichnis 10 20.4 20.5 Verpflichtungen für den View Controller . . . . . . . . . . . . . . . . . . . . . . Kommunikation in alle Richtungen . . . . . . . . . . . . . . . . . . . . . . . . . . . 408 413 21 21.1 21.2 21.3 21.4 21.5 Navigation mit einem Tableisten-Controller . . . . . . . . . . . . . . . . . . . . Der Tableisten-Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konfiguration der Schaltflächen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausflug in die Delegation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Delegate für den Tableisten-Controller . . . . . . . . . . . . . . . . . . . . . Delegation – meine Nachrichten an dich. . . . . . . . . . . . . . . . . . . . . . . 415 416 419 423 424 427 22 22.1 22.2 22.3 22.4 22.5 Grafische Oberflächen ohne Interface Builder . . . . . . . . . . . . . . . . . . Kontrolle ist gut – eine Checkliste ist besser! . . . . . . . . . . . . . . . . . . . Steuerelemente erstellen mit Swift-Anweisungen . . . . . . . . . . . . . . . Nachrichten mit Target-Action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tags – Etiketten für Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Suchen im Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433 434 438 444 447 448 23 23.1 23.2 23.3 23.4 23.5 23.6 23.7 Serialisierung – Aus dem Speicher in eine Datei . . . . . . . . . . . . . . . . Ein Dictionary für das Repository. . . . . . . . . . . . . . . . . . . . . . . . . . . . . Objekte sortieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Alles nicht ganz so einfach . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Sandbox – Apps im Sandkasten. . . . . . . . . . . . . . . . . . . . . . . . . . . Das NSCoding-Protokoll . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Serialisierung – Jetzt wird gespeichert! . . . . . . . . . . . . . . . . . . . . . . . . Die Nachrichtenzentrale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455 455 458 460 463 464 466 472 24 24.1 24.2 24.3 24.4 24.5 24.6 24.7 Der Picker – ein ganz besonderes Steuerelement. . . . . . . . . . . . . . . . Eine neue Klasse für Farben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Picker-Steuerelement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Protokolle für den Picker. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Kommunikation mit einem Picker. . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Farbmischer-App. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Picker-Einstellungen auslesen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Von Dezimal zu Hexadezimal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477 478 482 484 487 489 492 493 25 25.1 25.2 25.3 25.4 Tabellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Showroom-App – der digitale Ausstellungsraum . . . . . . . . . . . . Wohin mit dem Daten-Model? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konfigurationen im Interface Builder . . . . . . . . . . . . . . . . . . . . . . . . . Der Tabellen-View-Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497 498 501 502 506 Inhaltsverzeichnis 25.5 25.6 25.7 25.8 Zweite Szene: Die Detailansicht. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein neuer Segue. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vorsicht, Fehlerquelle! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bilder anzeigen mit dem UIImageView . . . . . . . . . . . . . . . . . . . . . . . 510 513 517 518 26 26.1 26.2 26.3 26.4 26.5 26.6 26.7 Eye Candy – Unsere App soll schöner werden . . . . . . . . . . . . . . . . . . Angepasste Tabellenzellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Controller für die Tabellenzelle . . . . . . . . . . . . . . . . . . . . . . . . . . . Abgerundete Ecken und Rahmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . Settings – die Einstellungen einer App . . . . . . . . . . . . . . . . . . . . . . . . NSUserDefaults – die Benutzereinstellungen . . . . . . . . . . . . . . . . . . Standardeinstellungen erforderlich . . . . . . . . . . . . . . . . . . . . . . . . . . . Kommunikation mit der Nachrichtenzentrale . . . . . . . . . . . . . . . . . . 525 529 533 536 539 544 546 548 27 27.1 27.2 27.3 27.4 27.5 27.6 Der Collection View. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Storyboard mit Collection View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Collection View Controller. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mit dem Segue zur Detailansicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Teilen mit dem UIActivityViewController. . . . . . . . . . . . . . . . . . . . . . Drucken mit AirPrint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Unterschiedliche Texte für unterschiedliche Dienste. . . . . . . . . . . . . 553 553 557 563 566 570 572 28 28.1 28.2 28.3 28.4 28.5 28.6 28.7 28.8 Zeichnen mit Core Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse UIView. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Rechtecke zeichnen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Frame und Bounds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Farbige Formen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Speichern der Context-Einstellungen. . . . . . . . . . . . . . . . . . . . . . . . . . Zeichnen von Farbverläufen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arbeiten mit Beschneidungspfaden. . . . . . . . . . . . . . . . . . . . . . . . . . . Kurven und Figuren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577 577 581 585 590 593 596 599 603 29 29.1 29.2 29.3 29.4 29.5 29.6 29.7 Multi-Touch mit Gestenerkennung . . . . . . . . . . . . . . . . . . . . . . . . . . . Linien zeichnen einen Pfeil . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einfache Berührungserkennung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wischgesten erkennen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Objekte bewegen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Pinch to Zoom – Vergrößern mit zwei Fingern . . . . . . . . . . . . . . . . . Verschiedene Gesten gleichzeitig erkennen . . . . . . . . . . . . . . . . . . . . Objekte drehen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 609 609 611 613 614 618 622 624 11 Inhaltsverzeichnis 12 29.8 29.9 Gesten im Konflikt. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Schüttelgeste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 626 628 30 30.1 30.2 30.3 30.4 30.5 30.6 Digitale und analoge Uhren mit Timern . . . . . . . . . . . . . . . . . . . . . . . Die Klasse NSTimer. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wie spät ist es? – Datum und Uhrzeit ermitteln . . . . . . . . . . . . . . . . Extensions für die Klasse NSTimer . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine analoge Uhr mit Core Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . Ein Zifferblatt zeichnen mit Winkelfunktionen . . . . . . . . . . . . . . . . . Timer im Einsatz – die Uhr tickt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 631 631 635 636 641 645 647 31 31.1 31.2 31.3 31.4 Lokale Benachrichtigungen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lokale Benachrichtigungen mit UILocalNotification . . . . . . . . . . . . . Eingehende Nachrichten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Benachrichtigungen anpassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Push-Benachrichtigungen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651 652 656 659 661 32 32.1 32.2 32.3 32.4 32.5 32.6 Karten und Koordinaten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Location-Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Auswerten von Positionsinformationen. . . . . . . . . . . . . . . . . . . . . . . . Geocoding – Wo bin ich hier?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Karten mit dem MKMapView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Annotations – Anmerkungen auf der Karte . . . . . . . . . . . . . . . . . . . . Benutzerdefinierte Anmerkungen gestalten und anzeigen . . . . . . . . 663 664 666 669 672 678 681 33 33.1 33.2 33.3 33.4 33.5 33.6 33.7 33.8 Daten suchen und finden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriff mit Key Value Coding. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Schlüsselpfade und Aggregatfunktionen . . . . . . . . . . . . . . . . . . . . . . . Listen filtern mit NSPredicate. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erweiterte Vergleiche, logische Operatoren und Closures . . . . . . . . . Prädikate mit regulären Ausdrücken . . . . . . . . . . . . . . . . . . . . . . . . . . Daten filtern für Tabellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listen für den Tabellen-View-Controller . . . . . . . . . . . . . . . . . . . . . . . Suchen mit dem UISearchController. . . . . . . . . . . . . . . . . . . . . . . . . . 685 685 688 692 695 697 698 703 706 Stichwortverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715 Danksagung Die Programmiersprache Swift und die iOS-Entwicklung sind aufregende Technologien und es gibt viele Themen, die ebenfalls ein Kapitel in diesem Buch verdient hätten. Als Autor habe ich die Erfahrung machen müssen, dass ein Buch nie wirklich fertig ist. Irgendwann muss man allerdings aufhören, zu schreiben, denn die Mitarbeiter vom Verlag möchten auch ihren Beitrag leisten. Mein Dank geht an erster Stelle an meine geduldige Lektorin Sabine Schulz, die mir erneut die Gelegenheit gab, meine Ideen und Erfahrungen in einem Buch zu veröffentlichen. Ich danke ihr, auch stellvertretend für alle Mitwirkenden in der Korrektur, im Satz und in der Produktion. Dank geht an meinen Freund Peter Mekelburg, der seit vielen Jahren bereitwillig alle meine Texte prüft und mich davor bewahrt, durch zu viele fehlende Satzzeichen und zu komplizierte Sätze unangenehm aufzufallen. Nicht vergessen darf ich Horst Andreas Roemer, der ebenfalls gerne einen kritischen Blick auf meine Texte wirft. Unsere kreative Zusammenarbeit war wieder ein Spaß, den wir hoffentlich beim nächsten Buch fortsetzen können. Mein besonderer Dank geht an alle Leser meiner Bücher, von denen ich einige inzwischen persönlich auf Konferenzen und Seminaren kennenlernen durfte. Ich hoffe, wir werden viele weitere Jahre zusammen Spaß haben! Holger Hinzberg Holzwickede, März 2016 13 Einleitung Das iPhone – für viele technikbegeisterte Menschen ist dieser Name gleichbedeutend mit dem modernen Mobiltelefon, das inzwischen leistungsfähiger ist als die sperrigen Computer, die noch vor einigen Jahren auf vielen Schreibtischen zu finden waren. Auch Softwareentwickler zieht es seit Jahren zum iPhone. Zu verlockend ist die iOS-Plattform mit ihren Geräten. Neben leistungsfähigen Prozessoren verfügen das iPhone und seine Geschwister durch die eingebauten Sensoren und Kameras über Fähigkeiten, die ein klassischer Rechner nicht bieten kann. Eine Multi-Touch-Oberfläche, die es erlaubt, Programme direkt auf dem Bildschirm zu bedienen, bringt bisher unbekannte und aufregende Möglichkeiten. Verständlich ist der Wunsch vieler Programmierer, für diese Plattform zu entwickeln. Ein Einstieg war oft nicht leicht, denn seit über 20 Jahren wurde bei der Firma aus Kalifornien fast ausschließlich mit Objective-C programmiert – eine leistungsfähige Programmiersprache, die außerhalb von Apple aber kaum bekannt war. Die Abkehr vom Vertrautem erfolgte am 1. Juni 2014, als Apple auf der WWDCKonferenz in San Francisco die Programmiersprache Swift der Öffentlichkeit vorstellte. Für die Zuschauer war das eine Überraschung und bei vielen Entwicklern die Skepsis groß. Das Erlernen einer Programmiersprache braucht Zeit, und die ist für viele Programmierer, besonders jene, die mit der Softwareentwicklung ihren Lebensunterhalt verdienen, gleichbedeutend mit Geld. Dessen ungeachtet bringt Swift endlich Möglichkeiten für OS-X- und iOS-Projekte, die es in anderen Programmiersprachen schon seit vielen Jahren gibt. Typsicherheit, Closures und Optionals waren einige der Schlagwörter, mit denen man während der Präsentation das Herz der Programmierer schneller schlagen ließ. Ein Jahr später wurde Swift in der Version 2.0 vorgestellt. Ein untrügliches Zeichen, dass es Apple mit der neuen Sprache ernst meint. Inzwischen verwenden immer mehr Entwickler die Sprache Swift für neue Projekte. Verpassen Sie nicht die Gelegenheit, dabei zu sein! An wen richtet sich das Buch? Trotz meiner guten Vorsätze, eine wirklich einfache Einführung in die iOS-Entwicklung mit Swift zu schreiben, fängt auch dieses Buch leider nicht bei null an. Dafür reichen die zur Verfügung stehenden Seiten bedauernswerterweise nicht aus. Wenn Sie als Leser aber schon ein wenig Erfahrung in einer anderen objektorientierten Programmiersprache haben und jetzt iOS-Apps mit Swift schreiben 15 Einleitung wollen, sollten die Beispiele für Sie keine großen Hürden sein. Dabei ist es auch unerheblich, ob Sie Vollzeitentwickler oder nur Hobbyprogrammierer sind. Wenn Begriffe wie Compiler, Objekte und Vererbung für Sie keine Fremdwörter sind, dann steht Ihnen der Weg offen, ein erfolgreicher iOS-Entwickler zu werden. Mit ein wenig Übung und einer guten Idee können Sie vielleicht schon bald Ihre erste App veröffentlichen! Aufbau des Buches Swift ist vielen anderen Programmiersprachen sehr ähnlich, aber trotzdem werden wir uns in den ersten Kapiteln zunächst mit einfachen Dingen wie Datentypen und Kontrollstrukturen beschäftigen, denn selbst bei den Grundlagen der Sprache gibt es Besonderheiten. Ein Schwerpunkt liegt außerdem in der Verarbeitung und Manipulation von Zeichenketten, da fast kein Programm ohne die Ausgabe von Texten auskommt. Zusätzlich werden Sie Apples Entwicklungsumgebung Xcode kennenlernen, die in der Version 6 um einen »Spielplatz« erweitert wurde, mit dem man Swift besonders gut lernen kann. In den weiteren Kapiteln folgen dann ein wenig anspruchsvollere Themen wie Arrays, Dictionaries, Funktionen, Klassen und Methoden. Falls Ihnen Tuples, Optionals und Closures noch nicht vertraut sind, werden Sie diese ebenfalls kennenlernen. Nicht fehlen dürfen selbstverständlich die Themen Vererbung und Protokolle. Was wäre die objektorientierte Programmierung ohne sie? Die Entwicklung von iOS-Apps füllt den zweiten Teil des Buches. Hier lernen Sie, wie Textfelder, Schaltflächen und viele andere Steuerelemente funktionieren und wie eine App aufgebaut ist. MVC-Architektur, Delegation und Grafik mit MultiTouch sind nur einige der behandelten Themen. Allerdings gibt es in diesem Buch kein einzelnes Projekt, das über mehrere Kapitel hinweg erweitert wird, sondern mehrere unabhängige Beispiele, an denen gezielt spezielle Technologien und Anwendungsfälle erklärt werden. Zunächst sind die Projekte noch klein und überschaubar, und erst die umfangreichen Themen werden uns etwas länger beschäftigen. Obwohl die Firma Apple in ihrer Entwicklungsumgebung für viele verschiedene Arten von Programmen Vorlagen anbietet, werden wir davon nur wenig Gebrauch machen und die Dinge selbst programmieren. Das verlangt zwar einen größeren Aufwand, gibt uns aber Gelegenheit, mehr über die Hintergründe und den Aufbau einer iOS-App zu erfahren. Teil I: Grundlagen der Sprache Swift 쐽 16 Kapitel 1: Datentypen und Optionals Wir beginnen mit einer Einführung in die Entwicklungsumgebung Xcode und spielen ein wenig mit Variablen und Konstanten. Dann sehen wir uns an, wie Teil I: Grundlagen der Sprache Swift man Zahlen rundet und aus einer Gruppe von Werten den kleinsten oder den größten ermittelt. Optionals, eine Besonderheit von Swift, werden Sie ebenfalls in diesem Kapitel kennenlernen. 쐽 Kapitel 2: Zeichenketten Ohne Ausgabe von Texten sind die meisten Programme wenig nützlich, und deshalb haben Zeichenketten ein eigenes Kapitel verdient. Swift arbeitet mit Unicode und dort gibt es einiges zu beachten. 쐽 Kapitel 3: Arrays und Dictionaries Eine Liste von Werten ist immer interessanter als viele einzelne Werte. Man kann sie sortieren, filtern oder der Reihe nach ansehen. Wenn es schnell gehen muss, helfen Wörterbücher, einen Eintrag aufzuspüren. 쐽 Kapitel 4: Fallunterscheidungen und Schleifen Wenn Ihnen der sequenzielle Programmablauf zu langweilig ist, sollten Sie Schleifen und Fallunterscheidungen ausprobieren. Swift bietet verschiedene Möglichkeiten, Anweisungen zu wiederholen oder nur bedingt auszuführen. Vorwärts oder rückwärts durch ein Array springen ist dann auch kein Problem mehr. 쐽 Kapitel 5: Funktionen Lassen Sie uns wiederverwendbaren Code schreiben. Den Anfang machen Funktionen. Mit ihnen können wir Anweisungen zusammenfassen und strukturieren. Parameter und Rückgabewerte sind dann auch ganz schnell erklärt und wir haben Zeit für Tupel und Aliasse. 쐽 Kapitel 6: Closures Stellen Sie sich vor, Sie könnten nicht nur Werte in Variablen ablegen, sondern auch Blöcke aus Programmcode. Mit Closures ist das möglich. Sie sind vielseitig einsetzbar und können ohne große Mühe sogar asynchron innerhalb einer App ausgeführt werden. Genau richtig also, wenn Vorgänge, wie der Download eines Bildes, mal wieder etwas länger dauern. 쐽 Kapitel 7: Klassen und Objekte Die Grundlagen der objektorientierten Programmierung dürfen nicht fehlen. Wie wird aus einer Klasse ein Objekt und was sind die Eigenschaften einer Klasse? Dieses Kapitel verrät es! 쐽 Kapitel 8: Methoden Methoden sind Funktionen, die zu einer Klasse gehören. Meinen Sie, damit wäre alles gesagt? Dann lassen Sie uns über Klassenmethoden, Failable Initializers und Methoden zur Deinitialisierung reden. 쐽 Kapitel 9: Vererbung und Assoziationen Nicht immer alles neu programmieren, sondern auf dem Bewährten aufbauen und erweitern. Das ist ein weiterer Grundsatz der objektorientierten Program- 17 Einleitung mierung. Wenn es dann nicht passt, lassen sich Methoden auch überschreiben. Assoziationen können zusätzlich helfen, die einzelnen Bausteine zusammenzusetzen. 쐽 Kapitel 10: Protokolle und Extensions Die Kommunikation zwischen verschiedenen Partnern kann kompliziert sein und deshalb ist es wichtig, dass man sich zuvor auf ein gemeinsames Protokoll einigt. So ist es auch in der Softwareentwicklung. Extentions ermöglichen es, bestehende Klassen um zusätzliche Methoden zu erweitern. Das funktioniert in Swift sogar ohne Vererbung. 쐽 Kapitel 11: Strukturen und Enumerationen Es müssen nicht immer Klassen sein. Strukturen sind vielseitig und können in Swift Aufgaben übernehmen, die ihnen in anderen Programmiersprachen verwehrt sind. Geschickt eingesetzt lassen sich mit ihnen einige Probleme vermeiden. Enumerationen sind auf dem Weg zu gutem Code ebenfalls eine große Hilfe. 쐽 Kapitel 12: Sicherer Programmcode Soll ein Programm bei fehlerhaften Daten weiterhin zuverlässig arbeiten, muss der Code für jede Situation optimal vorbereitet werden. Kommt es trotzdem zu einem Fehler, sollte man auch damit ganz gelassen umgehen. In weiser Voraussicht haben die Entwickler von Apple einige Techniken für uns vorbereitet. 쐽 Kapitel 13: Speicherverwaltung mit Referenzzähler Um die Speicherverwaltung muss man sich in Swift nur selten Gedanken machen, ganz ignorieren kann man aber das Thema nicht. Hier erfahren Sie die Dinge, die Sie wissen sollten. 쐽 Kapitel 14: Robuste Anwendungen und automatisierte Tests Die eigenen Projekte nach Veränderungen automatisch testen zu lassen, klingt wie der Traum vieler Programmierer. Mit der aktuellen Version von Apples Entwicklungsumgebung ist das jetzt möglich. Teil II: iOS-Apps 18 쐽 Kapitel 15: Lotto – 6 aus 49 In diesem Kapitel werden Sie endlich Ihre erste App entwickeln und dabei Schaltflächen und Bezeichnungsfelder kennenlernen. Zuvor ist allerdings ein wenig Theorie nötig und Sie erfahren, was es mit dem MVC-Entwurfsmuster auf sich hat. 쐽 Kapitel 16: Ein Blick hinter die Kulissen Wie startet eine App? Wie bekommt das Programm ein Icon und warum dreht sich die Ansicht, wenn wir das Gerät drehen? Die Antworten erhalten Sie durch einen Blick auf die internen Vorgänge. Teil II: iOS-Apps 쐽 Kapitel 17: Eingaben mit virtuellen Tastaturen iOS-Apps bringen ihre eigenen Bildschirmtastaturen mit und deshalb gibt es einige Besonderheiten zu beachten. Welche Tastatur ist für welchen Anwendungsfall die richtige und wie kann eine Tastatur wieder ausgeblendet werden? In diesem Kapitel finden Sie die Antworten. 쐽 Kapitel 18: Schieberegler, Textformatierungen und unendliche Werte Eine App zur Berechnung des Kraftstoffverbrauchs ist optimal geeignet für den Einsatz eines Schieberegler-Steuerelements. Ein weiterer Schwerpunkt in diesem Kapitel liegt auf der Formatierung von Zahlen in Abhängigkeit von den aktuellen Einstellungen. Nicht überall auf der Welt wird ein Komma als Dezimaltrennzeichen verwendet. 쐽 Kapitel 19: Storyboards – Mit dem Drehbuch durch die App Der Bildschirm eines iOS-Geräts ist klein und umfangreiche Informationen können nur schwer angezeigt werden. Da kann ein Storyboard hilfreich sein, um von einer Ansicht in eine andere zu wechseln. Im grafischen Editor der Entwicklungsumgebung ergeben sich ganz neue Möglichkeiten, eine App zu planen und zu gestalten. 쐽 Kapitel 20: Storyboards mit Protokollen Die Kommunikation zwischen verschiedenen Klassen ist nicht immer einfach, doch Protokolle helfen, für eine reibungslose Verständigung zu sorgen. In einem Projekt sorgen sie zusätzlich für größere Flexibilität, mehr Sicherheit und bessere Wiederverwendbarkeit von zuvor programmierten Komponenten. 쐽 Kapitel 21: Navigation mit einem Tableisten-Controller Eine Tableiste ist für den Nutzer einer App eine bequeme Möglichkeit, zwischen verschiedenen Ansichten umzuschalten, und für einen erfahrenen Entwickler auch schnell umzusetzen. Das gibt mir genug Gelegenheit, das Thema »Delegation« anzusprechen. Mit dieser Technik, die in fast allen Bereichen der iOS-Entwicklung eingesetzt wird, erhalten wir ganz neue Möglichkeiten in unseren Programmen. 쐽 Kapitel 22: Grafische Oberflächen ohne Interface Builder Der grafische Editor der Entwicklungsumgebung ist ein mächtiges Werkzeug, doch wenn es sein muss, kann die grafische Oberfläche einer App auch durch Anweisungen im Programmcode erzeugt werden. Das schafft Flexibilität, und Sie erfahren, wie die Kommunikation mit grafischen Elementen funktioniert. 쐽 Kapitel 23: Serialisierung – Aus dem Speicher in eine Datei In diesem Kapitel beschäftigen wir uns wieder mit Arrays und wie man sie aus dem Speicher des Geräts in eine Datei überträgt und wieder lädt. Außerdem erfahren Sie, wie die Nachrichtenzentrale verschiedenen Programmteilen dabei hilft, miteinander zu kommunizieren. 19 Einleitung 20 쐽 Kapitel 24: Der Picker – ein ganz besonderes Steuerelement iOS verwendet viele grafische Objekte, die dem Anwender schon von DesktopAnwendungen her vertraut sind und deshalb nur wenig Umgewöhnung verlangen. Neu in der mobilen Welt ist dagegen das Picker-Steuerelement, das auch in der Programmierung einen etwas ungewöhnlichen Weg einschlägt. Ein weiteres Mal treffen wir auf Protokolle und Delegation. 쐽 Kapitel 25: Tabellen Tabellen mit Master- und Detailansicht sind unter iOS ebenfalls ein spannendes Thema, das ein eigenes Kapitel mehr als verdient hat. Sehr viele Apps verwenden Tabellen, auch wenn diese manchmal gar nicht mehr als solche zu erkennen sind. 쐽 Kapitel 26: Eye Candy – Unsere App soll schöner werden Unsere App soll schöner werden, und Rahmen um die Bilder oder abgerundete Ecken sind nur zwei der möglichen Verbesserungen. Gefallen dem Anwender diese Stilmittel nicht, dann sollten wir ihm die Möglichkeit geben, sie in den Systemeinstellungen zu deaktivieren. 쐽 Kapitel 27: Der Collection View Das Collection-View-Steuerelement ist eine Alternative zur Tabellenansicht und ermöglicht eine andere Darstellung von Informationen. Zusätzlich beschäftigen wir uns in diesem Kapitel mit den Themen Drucken und wie wir mit unseren Apps auf Twitter und Facebook Bilder und Texte veröffentlichen können. 쐽 Kapitel 28: Zeichnen mit Core Graphics Trotz der Vielzahl an vordefinierten Steuerelementen ist es manchmal erforderlich, den digitalen Pinsel selbst in die Hand zu nehmen und Objekte oder Symbole, die man benötigt, selbst zu zeichnen. Das Core Graphics Framework bietet uns nahezu unbegrenzte Möglichkeiten, kreativ zu werden und Formen und Farbverläufe nach unseren Wünschen zu gestalten. 쐽 Kapitel 29: Multi-Touch mit Gestenerkennung Die Möglichkeit, Berührungen auf dem Bildschirm zu erkennen und auszuwerten, ist eine der faszinierenden Eigenschaften von iPhone, iPad und iPod touch. Mit nur wenigen Anweisungen können Sie die Objekte auf dem Bildschirm mit den Fingern verschieben, drehen oder skalieren. 쐽 Kapitel 30: Digitale und analoge Uhren mit Timern Soll eine bestimmte Funktion einer App immer wieder ausgeführt werden, greift der Entwickler schnell zu einem Timer. Damit ist es ein Leichtes, immer die aktuelle Uhrzeit anzuzeigen. In diesem Kapitel bietet sich Ihnen außerdem die Gelegenheit, zu zeigen, was Sie über Blöcke und Core Graphics inzwischen gelernt haben. Unsere Uhr bekommt ein Zifferblatt und sogar einen Sekundenzeiger. Swift ist nicht Objective-C Version 3.0 쐽 Kapitel 31: Lokale Benachrichtigungen Wird Ihre App momentan nicht ausgeführt? Sie wollen den Anwender aber trotzdem über wichtige Ereignisse informieren? Ein scheinbar unlösbares Problem – doch lokale Benachrichtigungen schaffen Abhilfe. 쐽 Kapitel 32: Karten und Koordinaten Wo bin ich und was gibt es hier zu sehen? In diesem Kapitel beschäftigen wir uns mit Straßenkarten und wie Sie mehr über Ihren aktuellen Standort erfahren können. In den Frameworks Core Location und Map Kit finden sich einige sehr interessante Funktionen. 쐽 Kapitel 33: Daten suchen und finden In einer großen Menge an Daten schnell eine gesuchte Information zu finden, ist für jeden Programmierer eine Herausforderung. Zum Glück gibt es im iOS bereits Funktionen, die einen Großteil der Arbeit für uns übernehmen. Eine Tabelle für die Erweiterung einer Suchleiste ist dann auch kein großes Problem mehr. Swift ist nicht Objective-C Version 3.0 Bevor es im nächsten Kapitel mit der Swift-Entwicklung losgehen kann, sind einige einführende Worte über die Programmiersprache nötig. Das gilt besonders für alle Entwickler, die bisher mit Objective-C auf den Apple-Plattformen gearbeitet haben. Einige Dinge, die Ihnen seit Jahren vertraut sind, funktionieren jetzt anders und müssen neu erlernt werden. Sieht man sich als erfahrener Entwickler den Quellcode eines Swift-Programms an, bekommt man schnell den Eindruck, es mit einer weiteren auf C basierenden Sprache zu tun zu haben. Tatsächlich ist die Syntax von Swift anderen Sprachen der C-Familie in manchen Bereichen ähnlich, unterscheidet sich aber in anderen Teilen genauso oft. Swift ist zweifellos mit C verwandt, aber keineswegs mit der Sprache kompatibel. Das war bei Objective-C, der Sprache, mit der für die ApplePlattformen bisher bevorzugt entwickelt wurde, anders. Sie können in Apples Xcode-Entwicklungsumgebung ein Objective-C-Projekt anlegen und dort ohne weitere Anpassungen 20 Jahre alten C-Code einfügen, ohne auf Probleme zu stoßen. Mit Swift funktioniert das nicht mehr. Die Sprache hat den Anspruch, sicher zu sein, und muss deshalb Anforderungen erfüllen, auf die man in der Vergangenheit weniger Wert legte. So ist es beispielsweise nicht mehr möglich, bei einer if-Struktur auf den Block aus geschweiften Klammern zu verzichten. Einige andere Programmiersprachen erlauben das, wenn nur ein Befehl bedingt ausgeführt werden soll, aber es ist dann auch nur genau eine Anweisung. Ist der Programmcode jedoch ungünstig formatiert, kann es bei dem Entwickler leicht zu Fehlinterpretationen kommen. Mit Swift hat Apple sich das Ziel gesetzt, solche 21 Einleitung und ähnliche Situationen zu vermeiden. Nicht mehr schreiben muss man hingegen die runden Klammern, mit denen in vielen anderen Sprachen die Bedingung der if-Struktur eingeklammert wird. Im Gegensatz zu dem Block aus geschweiften Klammern erhöhen sie weder die Lesbarkeit noch die Sicherheit des Codes und auch sonst haben die Klammern keinen weiteren Nutzen. var value = 10000 if value == 10000 { print("Der Wert ist 10.000!") } else { print("Der Wert ist nicht 10.000.") } Ein Semikolon nach einer Anweisung ist in Swift ebenfalls nicht mehr erforderlich: Der Compiler kann das Ende eines Befehls ohne diese Markierung erkennen. Sollten Sie trotzdem weiterhin Semikolons schreiben, vielleicht weil Sie das aus jahrelanger Gewohnheit automatisch tun, wird Ihr Code trotzdem akzeptiert. Nötig wird ein Semikolon nur, wenn Sie mehrere Anweisungen in eine Zeile schreiben wollen. Um den Programmcode lesbar zu halten, sollte man dies aber vermeiden. Wie Sie in den folgenden Beispielen sehen werden, benötigt ein Swift-Programm nicht zwingend eine main-Methode, um starten zu können. Natürlich ist es wichtig, den Startpunkt einer Anwendung genau festzulegen, allerdings ermöglicht Swift auch Programme, die anders arbeiten. Ein »Playground« kann ein vollwertiges Programm in einer Datei abbilden, die von oben nach unten abgearbeitet wird. Im nächsten Kapitel werden wir uns mit diesem Dokumententyp und seinen Möglichkeiten detailliert beschäftigen. Sind Sie bereits Entwickler für die Apple-Plattformen, werden Sie vermutlich die eckigen Klammern vermissen, die in Objective-C immer zum Einsatz kamen, wenn mit Klassen und Objekten programmiert wurde. Auf diese besondere Syntax wurde in Swift ebenfalls verzichtet. Die Sprache verwendet stattdessen für Eigenschaften und Methodenaufrufe die Punktnotation, wie sie in Java und C# schon seit vielen Jahren üblich ist. Auch das Konzept, dass sämtliche Objekte im Speicher unabhängig voneinander existieren und nur durch Nachrichten miteinander kommunizieren, wird bei Swift nicht mehr angewendet. Dies führt zwar gegenüber Objective-C zu Einbußen in der Flexibilität, ermöglicht Swift im Gegenzug aber schnellere Methodenaufrufe. 22 Kommentare Kommentare Die Syntax der Schreibweise von Kommentaren hat sich im Vergleich zu C und Objective-C nicht verändert. Es gibt weiterhin zwei verschiedene Möglichkeiten, Anmerkungen im Programmcode zu hinterlegen. Die erste und einfachste ist die Kommentarzeile, eingeleitet durch zwei Schrägstriche. // Dies ist ein Kommentar. Kommentarzeilen eignen sich gut, um Variablen zu beschreiben. Es ist sogar möglich, den Kommentar direkt hinter einer Anweisung einzufügen. Nach den zwei Schrägstrichen wird der noch folgende Text zu einem Kommentar. // Das Alter der Person var ageOfPerson:Int = 45 var salaryOfPerson:Int = 45000 // Das Gehalt der Person Möchte man mehr als eine Kommentarzeile in den Code einfügen, können komplette Abschnitte als Kommentarblock ausgewiesen werden. Mit der Zeichenfolge /* beginnt ein solcher Block, der mit */ wieder beendet wird. Neben dem Einsatz zur Dokumentation können Kommentarzeilen und Kommentarblöcke verwendet werden, um Anweisungen vorübergehend aus einem Programm zu entfernen. So müssen die Befehle nicht gelöscht werden, falls man sie zu einem späteren Zeitpunkt wieder benötigt. // Eine auskommentierte Anweisung /* var tax:Double = 0.19 */ Wie Sie spätestens bei unserem ersten Projekt sehen werden, sind Kommentare im Editor der Entwicklungsumgebung immer durch eine spezielle Farbe gekennzeichnet, voreingestellt ist Grün. So lassen sich auskommentierte Anweisungen leicht erkennen. Nicht alle Dokumente und Lehrbücher weisen darauf hin, aber Kommentare sind wichtig! Nicht nur für Sie, sondern auch für andere Entwickler, die vielleicht in Zukunft mit Ihrem Programmcode in Kontakt kommen. Wenn sich die Möglichkeit bietet, sollten Sie immer versuchen, Ihre Arbeit und Erfahrungen mit anderen Programmierern auszutauschen. Egal, wie viele Kommentare Sie schreiben, diese haben keinerlei Einfluss auf die Dateigröße oder die Geschwindigkeit der Anwendung. Es gibt also keinen Grund, auf Kommentare im Programmcode zu verzichten! Bei den Beispielen in diesem Buch werden Sie ebenfalls viele wichtige Anmerkungen in den Listings finden. 23 Einleitung Stilmittel in den Listings Das begrenzte Seitenformat dieses Buches macht es in manchen Situationen erforderlich, den Programmcode etwas anders zu formatieren, als es in der XcodeEntwicklungsumgebung möglich wäre. Gelegentlich werden lange Befehle, die im Texteditor auf dem Computer in eine Zeile passen, für die Codebeispiele auf mehrere Zeilen verteilt. Unabhängig von der Schreibweise ergeben sich bei den abgedruckten Listings aber keine Unterschiede in der Funktion. An manchen Stellen im Buch werden Ihnen im Programmcode drei Punkte begegnen, die weder eine Anweisung der Programmiersprache Swift noch ein geheimes Zeichen der Apple-Entwickler sind: ... Diese Markierung dient ausschließlich als Hinweis für Sie, dass es sich bei den gezeigten Anweisungen nur um einen Ausschnitt eines schon zuvor gezeigten Listings handelt. Die Punkte werden als Stilmittel verwendet, um im Buch nicht wertvollen Platz mit Wiederholungen bekannten Codes zu füllen. Wird im Laufe eines Kapitels vorhandener Programmcode geändert oder ersetzt, sind die neuen Anweisungen innerhalb des Listings fett gedruckt. Sie müssen dann nur diese Befehle bearbeiten. Der übrige Code bleibt unverändert. Die Frameworks der Apple-Plattformen Unabhängig davon, für welches System man entwickeln möchte, ist neben dem Umgang mit der Programmiersprache oft eine umfangreiche Kenntnis der Systembibliotheken, den Frameworks, unverzichtbar. Frameworks entbinden Entwickler von der mühevollen Aufgabe, sämtliche Komponenten für eine Anwendung eigenhändig programmieren zu müssen. Stattdessen ermöglichen sie den Zugriff auf bewährte Bausteine für nahezu sämtliche Anwendungsfälle. Wenn man sich mit der Entwicklung für die OS-X-Plattform beschäftigt, stößt man deshalb früher oder später unvermeidlich auf den Begriff Cocoa Framework. Der Begriff Framework ist in Verbindung mit Cocoa allerdings nicht sehr präzise, denn hinter dieser Bezeichnung verbirgt sich kein eigenes Framework, sondern der Zusammenschluss von drei einzelnen Bibliotheken: Application Kit, Core Data und Foundation. Application Kit, oft abgekürzt »App Kit«, enthält die Bausteine für die Entwicklung grafischer Benutzeroberflächen. Es enthält Klassen für die unterschiedlichsten Steuerelemente wie beispielsweise Fenster, Schaltflächen und Schieberegler. Durch die Verwendung eines gemeinsamen Frameworks kann sichergestellt wer- 24 Die Frameworks der Apple-Plattformen den, dass diese Elemente in allen Programmen nicht nur gleich aussehen, sondern sich auch identisch verhalten. Eine weitere Aufgabe des Application Kits ist die Behandlung von Ereignissen, wie das Anklicken eines Buttons, das Verschieben eines Fensters oder die Eingabe von Text in ein grafisches Textfeld. Das Foundation Framework, gelegentlich nur als »Foundation« bezeichnet, enthält unzählige Klassen und Datenstrukturen, die es in der Sprache Swift nicht gibt, und ist somit die wichtigste Bibliothek. Foundation kann auf eine bewegte Geschichte zurückblicken, denn dieses Framework wurde von der Firma NeXT entwickelt, bevor das Unternehmen von Apple übernommen wurde. Zwar wurde Foundation seitdem mehrfach erweitert, seine Wurzeln sind aber noch immer leicht zu erkennen. Fast alle Klassen aus dieser Bibliothek beginnen mit den Buchstaben NS, die für das Betriebssystem NeXT Step stehen, das später zu Mac OS X wurde. Einige Datentypen aus dem Foundation Framework braucht man in Swift nur noch selten. So gibt es für Zeichenketten jetzt den Typ String und man muss nicht länger die Klasse NSString verwenden, wie es bei der Objective-C-Programmierung noch der Fall ist. Nach Aussage von Apple sind die Swift-Klassen und die bewährten Klassen aus dem Foundation Framework jedoch austauschbar. Eine Methode, die einen NSString erwartet, würde sich problemlos mit einem String zufriedengeben. Das ist besonders beim Einsatz von Bibliotheken von Drittanbietern wichtig. Leider fehlen den neuen Typen einige Funktionen, die besonders von langjährigen Objective-C-Entwicklern sehr vermisst werden. Oft muss man neue Wege gehen, um an das gleiche Ziel zu gelangen. Es spricht aber nichts dagegen, in Swift weiterhin die aus Objective-C bekannten Klassen zu verwenden, falls das nötig sein sollte. Im Laufe der Jahre wurden von Apple zusätzliche Frameworks entwickelt, die streng genommen kein Teil von Cocoa sind. Trotzdem können auch sie problemlos in Programmen eingebunden werden und funktionieren dort reibungslos mit den anderen Komponenten. Zwei der bekanntesten Erweiterungen sind Core Animation und WebKit. Letzteres ist eine Bibliothek zur Darstellung von Internetseiten, die auch im Safari-Browser verwendet wird. 25 Einleitung Wendet man sich der iOS-Plattform zu, bekommt man es mit dem Cocoa Touch Framework zu tun, das ebenfalls einen Zusammenschluss einzelner Bibliotheken darstellt. Wie man am Namen schon gut erkennen kann, findet man dort spezielle Komponenten, um Programme durch Berührungen des Bildschirms mit den Fingern zu bedienen. Der Aufbau der grafischen Oberfläche wird deshalb nicht länger von App Kit übernommen, sondern wird zu einer Aufgabe des UIKit Frameworks. Dieses hat seine eigenen Steuerelemente, die bei einem Einsatz unter OS X und der Bedienung mit der Maus wenig nützlich wären. Das Foundation Framework des Macs wurde vom iPhone übernommen, was sich in vielen Situationen als sehr praktisch erweist. Manche Klassen, die für OS-XAnwendungen geschrieben wurden, können ohne Anpassungen in iOS-Apps erneut verwendet werden. Im Gegensatz zu Cocoa wurde Cocoa Touch von Anfang an großzügiger geplant und enthält zusätzliche Frameworks. Map Kit und Core Location ermöglichen den Zugriff auf den GPS-Empfänger und auf Straßenkarten, während Game Kit Spielen den Informationsaustausch ermöglicht. Seit einigen Jahren kann sogar der ins Betriebssystem integrierte Twitter-Dienst von Entwicklern in eigenen Programmen genutzt werden. Innerhalb eines Projekts ist es nicht immer einfach, zwischen Programmiersprache und Framework zu unterscheiden, denn die Bausteine sind sehr eng miteinander verzahnt. Ähnlich wie die Grammatik einer Fremdsprache ist Swift für den Aufbau und die Syntax der Anweisungen verantwortlich, während die Klassen der Bibliotheken das Vokabular der Sprache bilden. Die Installation von Xcode Das Werkzeug zur Swift-Programmierung, die Entwicklungsumgebung Xcode, wird von der Firma Apple kostenlos zum Download angeboten und kann direkt 26 Die Installation von Xcode aus dem Mac App Store geladen werden. Sie finden Xcode am schnellsten über die Suchfunktion des Mac App Store. Gelegentlich steht die Entwicklungsumgebung auch in der Liste der beliebtesten kostenlosen Anwendungen auf der Startseite. Der Download kann ein wenig Zeit in Anspruch nehmen, denn mit einer Größe von über 2 GB ist Xcode keine kleine Anwendung. Nachdem die Entwicklungsumgebung auf Ihren Rechner übertragen wurde, müssen Sie ein weiteres Mal Geduld aufbringen, denn der erste Start von Xcode kann ebenfalls einige Minuten dauern. Lassen Sie sich dabei auch nicht vom Schein trügen, es würde nichts passieren. Ihr Computer ist fleißig mit der Installation beschäftigt, und das ist bei Xcode eine sehr umfangreiche Aufgabe. Schon bald können Sie mit der Programmierung beginnen. Sämtliche Beispiele in diesem Buch benötigen Xcode, und es ist daher eine gute Idee, die Anwendung für einen schnellen Start aus dem Ordner PROGRAMME in das OS-X-Dock zu ziehen. Über das Kontextmenü dieser Verknüpfung können dann auch bereits verwendete Projekte schnell neu geladen werden. 27 Einleitung Die Entwickler-Community von Apple Ein alternativer Weg, eine Xcode-Entwicklungsumgebung zu beziehen, führt über die Webseite der Entwickler-Community von Apple. Dazu muss man sich allerdings als Entwickler registrieren. Diese Mitgliedschaft ist in der einfachsten Version kostenlos und bietet somit nur Vorteile. Neben den Beta-Versionen von Xcode, mit denen Sie dann schon kommende Technologien ausprobieren können, bietet die Webseite Beispielprojekte sowie Zugriff auf die Dokumentation und auf die Videos der vergangenen WWDC-Konferenzen. Hier kann man sehr viel lernen. https://developer.apple.com Eine kostenpflichtige Mitgliedschaft wird für die Beispiele in diesem Buch nicht benötigt. Apple möchte Geld von Ihnen, sobald Sie Ihre Programme veröffentlichen oder Technologien wie Cloud Kit nutzen wollen. iOS-Apps können Sie auch nur als zahlendes Mitglied auf ein iOS-Gerät übertragen. Ohne diesen Mitgliedsbeitrag, der seit Anfang 2015 99 Euro pro Jahr beträgt, können Sie iOS-Projekte nur mit dem Simulator testen, der zusammen mit Xcode installiert wird. Webseite und Downloads zum Buch Swift ist auf dem besten Weg, eine großartige und moderne Programmiersprache zu werden, aber trotz aller Bemühungen von Apple ist Swift in der aktuellen Version keineswegs fertig. In den kommenden Monaten und Jahren müssen Entwickler mit kleinen und großen Änderungen rechnen, die dann eventuell Anpassungen an bereits bestehenden Projekten erfordern. Soweit es mir möglich ist, werde ich versuchen, diese auf meiner Webseite zu beschreiben. Sie finden die Webseite zum Buch unter der Adresse: http://www.cocoa-coding.de/iosswift Auf der Webseite finden Sie außerdem die einzelnen Beispielprojekte zum Download, aber auch Informationen zu anderen Themen, die es nicht in das Buch geschafft haben. Sollten Sie Fehler im Buch finden, werde ich ebenfalls versuchen, sie zeitnah auf der Webseite zu korrigieren. Bei Fragen zum Buch oder wenn Sie Fehler finden, können Sie sich gerne auch direkt an mich wenden. Meine E-Mail-Adresse finden Sie im Impressum der Webseite. 28 Teil I Grundlagen der Sprache Swift In diesem Teil: 쐽 Kapitel 1 Datentypen und Optionals . . . . . . . . . . . . . . . . . . . 쐽 31 Kapitel 2 Zeichenketten. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 쐽 Kapitel 3 Arrays und Dictionaries. . . . . . . . . . . . . . . . . . . . . . 67 쐽 Kapitel 4 Fallunterscheidungen und Schleifen . . . . . . . . . . . 79 쐽 Kapitel 5 Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 쐽 Kapitel 6 Closures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 쐽 Kapitel 7 Klassen und Objekte . . . . . . . . . . . . . . . . . . . . . . . . 147 쐽 Kapitel 8 Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 쐽 Kapitel 9 Vererbung und Assoziationen. . . . . . . . . . . . . . . . . 181 29 쐽 Kapitel 10 Protokolle und Extensions . . . . . . . . . . . . . . . . . 199 쐽 Kapitel 11 Strukturen und Enumerationen . . . . . . . . . . . . 쐽 Kapitel 12 Sicherer Programmcode. . . . . . . . . . . . . . . . . . . 쐽 245 Kapitel 14 Robuste Anwendungen und automatisierte Tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 235 Kapitel 13 Speicherverwaltung mit Referenzzähler . . . . . . 쐽 213 251 Kapitel 1 Datentypen und Optionals Im grundlegenden Umgang mit Variablen und Werten sind sich die meisten modernen Programmiersprachen sehr ähnlich und unterscheiden sich oft nur in der verwendeten Syntax. Swift ist dabei keine Ausnahme, und wenn Sie schon mit anderen Sprachen gearbeitet haben, wird Ihnen der Inhalt der nächsten Seiten bestimmt vertraut vorkommen. Trotzdem sollten Sie dieses Kapitel nicht überspringen, denn Swift bietet einige Besonderheiten und Anweisungen, die man in anderen Sprachen nicht findet. Selbst bei einfachen und alltäglichen Dingen, wie zum Beispiel der Addition von Variablen, können erfahrene Entwickler durch Swift noch überrascht werden. Lassen auch Sie sich also überraschen! 1.1 Willkommen auf dem Spielplatz Xcode bietet in der aktuellen Version einen besonderen Dokumententyp, der als »Playground«, englisch für Spielplatz, bezeichnet wird. Wenn Sie in einem Playground Programmcode schreiben, wird Ihnen das Ergebnis Ihrer Arbeit oft sofort in einer Seitenleiste neben dem Texteditor angezeigt, die von Apple als Results Sidebar bzw. als Ausgabebereich bezeichnet wird. Dort können Sie leicht überprüfen, ob die eingegebenen Anweisungen korrekt sind und das gewünschte Ergebnis liefern. Der Spielplatz eignet sich hervorragend, um neue Dinge auszuprobieren und zu testen, oder sich – wie in unserem Fall – mit Swift vertraut zu machen. Einen neuen Playground können Sie direkt vom Welcome-to-Xcode-Fenster aus erstellen, das beim Start der Entwicklungsumgebung angezeigt wird. Wählen Sie dort GET STARTED WITH A PLAYGROUND. Möchten Sie zu einem bereits bestehenden Projekt einen Playground hinzufügen, führt der Weg über die Menüpunkte FILE, NEW und FILE... Ein Playground der Plattform iOS unterscheidet sich von einem OS-X-Spielplatz nur durch die Anweisung, mit der das zugehörige Framework für die grafischen Oberflächen importiert wird. iOS-Apps arbeiten mit UIKit, während für OS X das Cocoa Framework importiert wird. Für unsere ersten Schritte benötigen wir jedoch nur das Foundation Framework und somit ist es egal, für welche Plattform Sie den Playground erzeugen. Foundation ist sowohl in UIKit als auch in Cocoa enthalten. Nennen Sie Ihren ersten Playground Datentypen und speichern Sie die Datei an einem Ort, an dem Sie sie später leicht wiederfinden. Sie sollten immer 31 Kapitel 1 Datentypen und Optionals bemüht sein, Ihre Projekte gut zu organisieren, um die Übersicht nicht zu verlieren und Dinge schnell finden zu können. Nachdem Xcode den Playground angelegt hat, öffnet die Entwicklungsumgebung ein in zwei Bereiche geteiltes Fenster. Die linke Seite ist der Editor, in den wir unsere Anweisungen schreiben können. Die rechte Seite wird als Ausgabebereich bezeichnet und dient zur Ausgabe von Werten und Variablen. Neben der import- 32 1.2 Variablen und Konstanten Anweisung und einem Kommentar gibt es im Programm sogar schon einen Befehl. Mit var str = "Hello, playground" wird der Variablen str der Text Hello, playground zugewiesen, der daraufhin im Ausgabebereich angezeigt wird. Da wir uns mit Texten erst später beschäftigen, können Sie die Anweisung entfernen. Löschen Sie aber nicht versehentlich den import-Befehl. Ohne ein geladenes Framework kann es in einem Swift-Programm zu verwirrenden Fehlermeldungen kommen, deren wahre Ursache nur schwer zu erkennen ist. 1.2 Variablen und Konstanten Lassen Sie uns bei der Reise in die Welt der Swift-Programmierung mit etwas ganz Einfachem beginnen. Wie alle modernen Programmiersprachen bietet Swift die Möglichkeit, im Programmcode Variablen zu deklarieren und ihnen Werte zuzuweisen. Der von Xcode vorbereitete Playground hat das mit seiner Anweisung schon gut demonstriert. Für verschiedene Anwendungsfälle kommen dabei dann unterschiedliche Datentypen zum Einsatz. Soll mit den Werten gerechnet werden, fällt die Wahl in der Regel auf die Typen Int, Double oder Float, je nach der erforderlichen Genauigkeit. Das nachfolgende kurze Listing zeigt beispielhaft die Definition zweier Variablen mit dem Befehl var und zwei unterschiedlichen Datentypen. Wenn Sie alle Anweisungen richtig eingegeben haben, wird Xcode im Ausgabebereich des Playground-Fensters die Inhalte der Variablen anzeigen. An dieser Stelle erneut der Hinweis, dass die Anweisungen auch funktionieren, wenn Sie die Befehlszeilen aus Gewohnheit mit einem Semikolon abschließen. 33 Kapitel 1 Datentypen und Optionals var age:Int = 43 var taxRate:Double = 9.5 Um welche Typen es sich bei den Variablen in unserem Code handelt, können Sie vermutlich auch ohne jahrelange Erfahrung in der Softwareentwicklung und ohne die explizite Angabe des Typs schnell selbst erkennen. Die Variable age ist eine ganze Zahl, ein sogenannter Integer. Bei taxRate finden wir Nachkommastellen, sodass es sich um eine Dezimalzahl handeln muss. Dort sollte dann der Typ Double oder Float verwendet werden, denn als ganze Zahl ließe sich dieser Wert nur unvollständig ablegen. Wichtig Für Variablennamen wird in Swift die Lower-Camel-Case-Schreibweise verwendet. Der erste Buchstabe im Namen ist ein kleiner Buchstabe, alle weiteren Wörter, aus denen der Name gebildet wird, beginnen mit einem Großbuchstaben. Beispiele für aussagekräftige Namen wären arrayOfSelectedItems oder firstDayInMonth. Unterstriche in Bezeichnern sind in Swift nicht üblich. Wie Sie sicher bestätigen können, geben die Werte selbst schon genau vor, welcher Datentyp verwendet werden muss, und so ist es in Swift oftmals gar nicht notwendig, den Typ einer Variablen bei der Definition explizit anzugeben. Der Compiler kann den Typ selbst erkennen. Die Definitionen der beiden Variablen würden in Swift daher auch so funktionieren: var age = 43 var taxRate = 9.5 34 1.2 Variablen und Konstanten Diese automatische Typerkennung des Compilers, die in der englischen Dokumentation als »Type Inference« bezeichnet wird, funktioniert in Swift sehr gut, sodass bei den meisten Variablen auf eine explizite Angabe des Datentyps verzichtet wird. Als Entwickler kann man jedoch sicher sein, dass der Compiler den richtigen Typ auswählt. Außerdem wird der Programmcode so kürzer und besser lesbar, worauf man bei Apple sehr viel Wert legt. Für das Programm selbst ändert sich dabei jedoch nichts. Die Variable age ist weiterhin eine ganze Zahl und für Dezimalbrüche wie taxRate wählt der Compiler immer den Typ Double, weil dieser im Vergleich zu Float über eine größere Genauigkeit verfügt. Bei beiden Variablen wird der Typ aus den zuerst zugewiesenen Werten ermittelt. Spätere Änderungen auf einen anderen Typ sind nicht möglich. Wurde eine Variable als Int festgelegt, können Sie ihr anschließend keine Zeichenkette zuweisen. Das folgende Beispiel wird nicht funktionieren, wie Sie in Ihrem Playground leicht ausprobieren können. Durch Anklicken des Fehlersymbols links neben dem Programmcode können Sie die Fehlermeldung ein- oder ausblenden. var age = 43 var taxRate = 9.5 age = "19" Die Deklaration und Definition einer Variablen muss in Swift keineswegs immer in einer Programmzeile geschehen, die Anweisungen können auf mehrere Zeilen verteilt werden. Jetzt ist der Compiler jedoch nicht mehr in der Lage, den Typ der Variablen selbstständig zu erkennen, und benötigt unsere Hilfe. Der Typ muss bei der Deklaration wieder explizit angegeben werden. 35 Kapitel 1 Datentypen und Optionals var age:Int var taxRate:Double age = 43 taxRate = 9.5 Wie groß der Wert einer Variablen sein kann, hängt teilweise von der Systemarchitektur ab, auf der das Programm ausgeführt wird. Auf einem 32-Bit-System wird für den Typ Int ein 32 Bit langes Datenwort verwendet, das den möglichen Wert auf einen Bereich von -2.147.483.648 bis 2.147.483.647 eingrenzt. Auf Systemen mit einer 64-Bit-Architektur ist Int ein 64 Bit langes Datenwort und kann Werte zwischen -9.223.372.036.854.775.808 und 9.223.372.036.854.775.807 annehmen. Werden explizit kleinere oder größere Datenworte benötigt, können statt Int die Datentypen Int8, Int16, Int32 und Int64 verwendet werden. Der Typ Float ist auf jedem System ein 32-Bit-Datenwort, Double hingegen immer 64 Bit lang. Welche Werte auf Ihrem System möglich sind, können Sie mit den folgenden vier Aufrufen der print-Funktion herausfinden. // Der größe Int-Wert print(Int.max) // Der kleinste Int-Wert print(Int.min) // Der größte Double-Wert print(DBL_MAX) // Der kleinste Double-Wert print(DBL_MIN) Ein Aufruf der Funktion print in einem Playground führt zu einer Ausgabe in der Results Sidebar. Dort wird der Wert direkt neben der zugehörigen Anweisung angezeigt. Der Ausgabebereich ist jedoch nicht der einzige Ort, an dem die Entwicklungsumgebung die Ausgaben von print anzeigt, zusätzlich werden sie in die Konsolenansicht geschrieben. Diese ist in der Standardkonfiguration eines Playgrounds allerdings immer unsichtbar und muss erst eingeblendet werden. Die Schaltfläche HIDE OR SHOW THE DEBUG AREA aus der Symbolleiste erledigt das. In der Konsole stehen die minimalen und maximalen Werte nicht neben den 36 1.2 Variablen und Konstanten Anweisungen, sondern erscheinen in der Reihenfolge, in der sie mit print ausgegeben wurden. In Xcode-Projekten für iOS-Apps oder OS-X-Anwendungen steht Ihnen während der Entwicklung kein Ausgabebereich zur Verfügung, und Ihre Anweisungen werden dort auch nicht automatisch sofort nach der Eingabe ausgeführt. Das funktioniert nur im Playground. Die Konsolenansicht gibt es jedoch auch dort und kann für Ausgaben genutzt werden. Zögern Sie nicht, print zu verwenden, um den Programmablauf Ihrer Anwendungen nachzuvollziehen oder Werte anzuzeigen. Ein Aufruf von print verrät Ihnen nicht selten schneller etwas über Ihr Programm als die Analysewerkzeuge der Entwicklungsumgebung. Wenn Sie den Mauspfeil auf einen Wert im Ausgabebereich bewegen, werden dort die Schaltflächen für Inline Results und Quick Look sichtbar. Beide Funktionen bieten weitere Möglichkeiten, den Wert einer Variablen im Playground auszugeben. Aktivieren Sie ein Inline Result, wird der Wert direkt im Codeeditor eingeblendet. Quick Look zeigt den Wert in einem kleinen Fenster, das jedoch sofort wieder verschwindet, sobald ein beliebiger anderer Punkt auf dem Bildschirm angeklickt wird. Die Ausgabe eines Inline Results bleibt sichtbar, bis sie wieder deaktiviert wird. Während Variablen zur Laufzeit eines Programms jederzeit neue Werte, allerdings nur desselben Typs, zugewiesen werden können, ist dies bei Konstanten nicht möglich. Konstanten werden in Softwareprojekten daher oft zur Konfiguration eingesetzt, um Werte einer Anwendung leichter zu verwalten und »magische Zahlen« zu vermeiden. Dabei handelt es sich um Zahlenwerte, die direkt im Programmcode stehen und deren Aufgaben oft nur schwer erkennbar sind. Eine Konstante mit einem aussagekräftigen Namen kann dann sehr zur Lesbarkeit des Codes betragen. In Swift werden Konstanten über den Befehl let erzeugt. Das nachfolgende Listing zeigt die Definition der Zahl Pi sowie einer Serveradresse als Zeichenkette. 37 Kapitel 1 Datentypen und Optionals let pi = 3.14159265359 let server = "http://cocoa-coding.de" In den ersten Versionen von Swift war es bei einer Konstanten nicht möglich, Deklaration und Definition auf mehrere Programmzeilen zu verteilen, das funktionierte selbst dann nicht, wenn der Typ explizit angegeben wurde. Seit Swift 2.0 können Deklaration und Definition getrennt werden, in vielen anderen Programmiersprachen funktioniert das bei Konstanten nicht. let pi:Double pi = 3.14159265359 let server:String server = "http://cocoa-coding.de" Eine nachträgliche Änderung des zugewiesenen Wertes ist anschließend nicht mehr möglich. Eine Konstante ist unveränderlich, und der Compiler wird bei dem Versuch, Änderungen vorzunehmen, Fehlermeldungen ausgeben. Wichtig Swift und Xcode erlauben bei der Entwicklung die Verwendung sämtlicher Unicode-Zeichen als Variablennamen. So können Sie beispielsweise statt des Variablennamens pi auch das Zeichen U+30C0 (Greek Small Letter Pi) verwenden. Inwieweit dies die Programmierung leichter und übersichtlicher macht, sollten Sie für sich selbst entscheiden. Empfehlen kann man diese Vorgehensweise vermutlich nur in Ausnahmefällen. let π = 3.14159265359 1.3 Zahlendatentypen konvertieren Nicht selten gibt es in Programmen Situationen, bei denen die Inhalte von mehreren Variablen zu einem Wert zusammengefasst werden müssen. Ein sehr gutes Beispiel sind mathematische Berechnungen, die aus einzelnen Werten ein Ergebnis ermitteln. Swift unterscheidet sich dabei nicht von anderen Programmiersprachen und unterstützt eine Vielzahl von Rechenoperatoren. Wie nicht anders zu erwarten, gilt immer Punkt- vor Strichrechnung. var a = 75 var b = 100 var c = 5 let sum1 = a + b * c let sum2 = (a + b) * c 38 1.3 Zahlendatentypen konvertieren Beachtenswert im gezeigten Beispiel ist der Umstand, dass auch das Ergebnis einer Berechnung einer Konstanten zugewiesen werden kann. Im Unterschied zu einigen anderen Programmiersprachen muss der Wert einer Konstanten in Swift nicht zwangsläufig während der Kompilierung bekannt sein, obwohl er sich in diesem Beispiel durchaus ermitteln ließe. Sogar eine Textfeldeingabe, die erst nach dem Start der Anwendung vorgenommen wird, kann in Swift einer Konstanten zugewiesen werden. Solange wir uns bei den Rechenoperationen auf nur einen Datentyp beschränken, können wir ohne Probleme programmieren. Schwierig wird es erst, wenn unterschiedlichen Typen verwendet werden. Dann kann schon eine einfache Addition zu Problemen führen: var a = 75 var b = 3.8 // Die Addition unterschiedlicher Datentypen // führt zu einer Fehlermeldung! let sum = a + b Auch ohne Angabe eines Typs können wir die Variable a als ganze Zahl und somit vom Typ Int identifizieren. Bei b hingegen handelt es sich um eine Dezimalzahl, für die der Swift-Compiler dann einen Double-Typ verwendet, sofern ihr Typ nicht explizit als Float angegeben wird. Doch welchen Typ muss man einsetzen, wenn die beiden Variablen addiert werden sollen? Mit einem weiteren Double wäre sichergestellt, dass der Nachkommateil der Summe nicht verloren geht, aber in manchen Situationen ist vielleicht genau das gewünscht und man möchte nur ganze Zahlen. Weil der Swift-Compiler diese Entscheidung nicht eigenständig treffen kann, führt die Addition zu einem Fehler. Schuld daran sind die unterschiedlichen Datentypen. Leider ist die Meldung der Entwicklungsumgebung nicht besonders aussagekräftig, wenn man mit der Problematik nicht vertraut ist: In der Softwareentwicklung unterscheidet man zwei Arten der Typkonvertierung. Bei der impliziten Konvertierung entscheidet der Compiler selbstständig, welcher Typ verwendet werden soll. Bei einer Berechnung, die sowohl Int als auch Double-Typen verwendet, würde vermutlich ein Double für das Ergebnis verwendet, um eine bestmögliche Genauigkeit zu gewährleisten. Allerdings gibt es keine Regel, dass dies so sein muss, und Programmiersprachen mit anderen Compilern können sich anders verhalten. Damit der Entwickler sich nicht auf Vermutungen 39 Kapitel 1 Datentypen und Optionals verlassen muss, ist in Swift eine implizite Konvertierung nicht möglich. Die gezeigte Fehlermeldung ist die Folge. Bei einer expliziten Konvertierung muss der gewünschte Typ im Programm genau festgelegt werden, was somit zu einer Aufgabe für uns Programmierer wird. Soll das Ergebnis vom Typ Double sein, müssen wir den Compiler anweisen, die Variable a vor der Addition mithilfe einer Funktion in einen Double-Typ zu konvertieren. Sind beide Summanden vom gleichen Typ, ergibt sich der Typ für die Summe. // Die Double-Funktion: // a wird zunächst in einen Double-Typ umgewandelt // c wird so zu einem Double-Typ var sumDouble = Double(a) + b Wollen wir unser Ergebnis lieber als ganze Zahl, muss die Variable b konvertiert werden. Doch Vorsicht, gerundet wird die Zahl von der Int-Funktion nicht! Die Nachkommastellen werden abgeschnitten, aus 3,8 wird 3 und die Summe der Addition somit 78! Mit einer expliziten Konvertierung erhalten wir immer den gewünschten Typ und sind nicht von der Entscheidung eines Compilers abhängig. // Die Int-Funktion: // b wird zunächst in einen Int-Typ umgewandelt // c wird so zu einem Int-Typ var sumInt = a + Int(b) Die gezeigten Arten der Konvertierung entsprechen einer Umwandlung, die als »cast« oder »typecasting« bezeichnet wird, in Swift allerdings mit einer etwas ungewöhnlichen Syntax. Viele andere Programmiersprachen verwenden eine Schreibweise, bei der dem zu konvertierenden Wert der gewünschte Typ in runden Klammern vorangestellt wird. Dort sieht es so aus: // Typecasting, wie es in anderen Sprachen // angewendet wird. // var sum = (Double)a + b Möchte man in einem Swift-Programm zwei Int-Werte addieren, das Ergebnis aber als Double erhalten, ist das ebenfalls nur ein kleines Problem. In den runden Klammern der Funktionen kann die Addition programmiert werden. So ist es nicht erforderlich, beide ganze Zahlen einzeln zu konvertieren. var x = 100 var y = 60 // Einzelne Konvertierung, nicht erforderlich 40 1.4 Werte runden // var sum = Double(x) + Double(y) // Konvertierung der Summe var sum = Double(x + y) 1.4 Werte runden Bei einer expliziten Konvertierung von Double zu Int sämtliche Nachkommastellen abzuschneiden, kann gewünscht sein, doch nicht weniger oft möchte man den Wert zuvor runden. Diese Aufgabe kann die Funktion round übernehmen. Wie Sie mit den folgenden Anweisungen selbst ausprobieren können, wird ab einem Nachkommawert von .5 aufgerundet, ansonsten wird abgerundet. var a1 = 99.49 var r1 = round(a1) // r1 ist jetzt 99.0 var a2 = 99.50 var r2 = round(a2) // r2 ist jetzt 100.0 Soll der gerundete Wert dann zu einem Int-Typ werden, spricht nichts dagegen, verschiedene Anweisungen zu verschachteln. So kann man im Code eine Zeile einsparen, ohne dabei allzu viel Lesbarkeit einzubüßen. var a2 = 99.50 var r2 = Int(round(a2)) // r2 ist jetzt 100 und vom Typ Int Zwei weitere wichtige Funktionen zur Manipulation von Double-Werten sind floor und ceil. Sie liefern den nächstniedrigeren und nächsthöheren ganzzahligen Wert, vollkommen unabhängig vom Wert hinter dem Komma. Das Ergebnis ist weiterhin ein Double. var aValue = 99.001 var c = ceil(aValue) 41 Kapitel 1 Datentypen und Optionals // c ist jetzt 100.0 var f = floor(aValue) // f ist jetzt 99.0 Einige Entwickler, die ich kennengelernt habe, hatten Schwierigkeiten, sich die Funktionsweisen von floor und ceil zu merken und diese zu unterscheiden. Denken Sie dabei an die englischen Begriffe. floor ist der Fußboden und somit der niedrige, untere Wert. ceil ist eine Abkürzung für ceiling, die Zimmerdecke. Bei dieser Funktion bekommen wir den höheren, oberen Wert. Die Zimmerdecke ist oben und der Fußboden ist unten. Ganz einfach! 1.5 Minimum und Maximum Wird der kleinere oder der größere Wert zweier Zahlen benötigt, kann man dies mit einer if-Struktur sicherlich leicht programmieren. Etwas eleganter sind die in Swift bereits vorhandenen Funktionen max und min. var m1 = 1234 var m2 = 9876 // Den größeren und den kleineren Wert zweier // Int-Typen ermitteln var maximum = max(m1,m2) var minimum = min(m1,m2) Eine besondere Erklärung ist an dieser Stelle vermutlich nicht nötig. Erwähnenswert ist jedoch, dass die beiden Funktionen in Swift auch mehr als zwei Werte verarbeiten können. Bei den ähnlichen Funktionen anderer Programmiersprachen ist das nicht möglich. Allerdings müssen sämtliche Werte vom gleichen Typ sein, also ausschließlich Double, Float oder Int. var m1 = 12.34 var m2 = 98.76 var m3 = 87.88 var m4 = 666.6 // Den größten und den kleinsten Wert aus vier 42 1.6 Der Datentyp Bool // Double-Typen ermitteln var maximum = max(m1,m2,m3,m4) var minimum = min(m1,m2,m3,m4) Eine Alternative zu min und max bieten die beiden Funktionen minElements und maxElements, die jedoch beide eine Auflistung des Typs Array als Parameter erwarten. Mit diesen Listen werden wir uns in Kapitel 3 noch genauer beschäftigen. Um die Funktionen auf unsere vier Variablen anzuwenden, genügt es für den Augenblick, die Bezeichner zusätzlich in eckige Klammern einzufassen. Sämtliche Funktionen akzeptieren neben Variablen und Konstanten auch feste Werte, die unveränderlich im Programmcode stehen. var minValue = minElement([m1, m2, m3, m4]) var maxValue = maxElement([4.5, 4.0, 3.9, 4.4]) 1.6 Der Datentyp Bool Einen Datentyp, den man in diesem Kapitel nicht unterschlagen sollte, ist der Typ Bool, benannt nach dem englischen Mathematiker George Boole. Eine Variable oder Konstante des Typs Bool kann ausschließlich die Werte true oder false annehmen und wird deshalb fast ausnahmslos dazu verwendet, um eine bestimmte Aussage als wahr oder unwahr zu kennzeichnen. Wenn wir uns in Kapitel 4 mit Fallunterscheidungen beschäftigen, wird der Typ Bool daher eine große Rolle spielen. // Zuweisungen boolescher Typen // mit Angabe des Datentyps var valueB1:Bool = true // Ohne Angabe des Datentyps var valueB2 = false In einigen anderen Programmiersprachen kann ein Bool alternativ durch Zahlenwerte gesetzt werden, wobei 0 einem false und jeder andere Wert einem true 43 Kapitel 1 Datentypen und Optionals entspricht. Weil so die Lesbarkeit des Codes aber sehr beeinträchtigt wird, gibt es diese Möglichkeit in Swift 2.0 nicht mehr. Die aus Objective-C bekannten Werte YES und NO funktionieren in Swift ebenfalls nicht. // Alternative Zuweisung von Zahlen // Funktioniert in Swift 2.0 nicht mehr var value1:Bool = 1 var value2:Bool = 0 1.7 Optionals Einer der größten Unterschiede zwischen Objective-C und Swift ist die Möglichkeit, Variablen und Konstanten im Programmcode als Optional zu deklarieren und ihnen damit die Fähigkeit zu geben, alternativ zu einem Wert den Zustand nil anzunehmen. Ein Optional ist nil, wenn ihm noch kein Wert zugewiesen wurde oder sein Zustand explizit unbestimmt sein soll. Haben Sie das alles sofort verstanden? Zugegeben, das klingt sehr technisch, aber so ist die Softwareentwicklung gelegentlich. Möchte man Optionals an einem Beispiel aus dem Alltag erklären, muss man dafür ein wenig ausholen: Stellen Sie sich ein digitales Thermometer vor, dessen gemessene Temperatur von einer Software verwaltet und angezeigt werden soll. Im Sommer erreicht das Thermometer Temperaturen von über 30 Grad, im Winter hingegen Werte unter 0 Grad. So ergibt sich ein großer Bereich von möglichen Temperaturen, wobei es sich immer um Dezimalzahlen handelt. In der Software scheint der Datentyp Double daher bestens geeignet, die ermittelte Temperatur aufzunehmen und zur Anzeige zu bringen. Wie Sie inzwischen wissen, müssen Variablen und Konstanten aber mit einem Wert initialisiert werden, bevor man das erste Mal lesend auf sie zugreifen kann. Es gibt Situationen, in denen eben dies zu einem Problem wird. Solange das Thermometer funktioniert, hat das Programm mit der Temperatur und dem Typ Double keine Schwierigkeiten. Problematisch wird es erst, wenn das Thermometer ausfällt oder von der Software aus einem anderen Grund keine Daten mehr empfangen werden können. Mit welcher Temperatur haben wir es dann zu tun und welcher Wert soll dann angezeigt werden? Eine Möglichkeit wäre es, die Variablen mit dem Wert 0 zu initialisieren und somit eine Temperatur von 0 Grad anzuzeigen, wenn keine Messungen vorliegen, aber das ist keine gute Lösung. 0 Grad ist eine der möglichen Temperaturen in unserem Bereich, und wenn im Programm mit diesem Wert gearbeitet wird, sollte man davon ausgehen können, dass die Temperatur so gemessen wurde. Die Software benötigt zusätzliche Informationen darüber, ob ein gemessener Wert vor- 44 1.7 Optionals liegt, aber ein Zahlenwert des Typs Double kann darüber keine Auskunft geben. Das Programm muss erweitert werden. Ob es sich bei dem abgelegten Wert tatsächlich um eine gemessene Temperatur handelt, ließe sich mit einer zusätzlichen booleschen Variablen abbilden. Oder Sie verwenden einen Optional! In Swift kann jeder Typ zu einem Optional werden, allerdings ist es bei der Definition einer Variablen oder Konstanten erforderlich, diese Erweiterung explizit anzugeben, denn dass eine Variable oder Konstante ein Optional sein soll, kann der Compiler nicht eigenständig erkennen. Er würde immer einen regulären Typ einsetzen und sich dann beschweren, wenn er für den Wert keine Zuweisung findet. Um aus einem regulären Typ einen Optional zu machen, wird dem Datentyp ein Fragezeichen angehängt. So erfährt der Compiler, dass dieser Wert nil sein darf. Wenn Sie die zwei Anweisungen aus dem nächsten Listing in Ihrem Playground eingeben, wird Ihnen Xcode für die Variable temperatur1 den Wert 32.5 im Ausgabebereich ausgeben. Für temperatur2 erhielten Sie in der Vergangenheit die Ausgabe { Some 32.5}, wobei Some ein Hinweis auf einen Optional war. In der momentan aktuellen Xcode-Version wird das Some leider nicht mehr angezeigt. // Definition einer Variablen var temperatur1:Double = 32.5 // Definition einer Variablen als Optional var temperatur2:Double? = 32.5 Eindeutig werden die Informationen der Entwicklungsumgebung, wenn Sie die beiden Variablen mit der print-Funktion ausgeben. Zusätzlich zum Wert für temperatur2 wird jetzt das Wort Optional angezeigt. ... // Ausgabe der beiden Variablen print(temperatur1) print(temperatur2) 45 Kapitel 1 Datentypen und Optionals Besonders interessant verhält sich der Playground, wenn Sie für die beiden Variablen die Zuweisungen der Werte entfernen. Die Variable temperatur2 ist ein Optional und kann legitim ohne Wert verwendet werden. Ihr Zustand ist dann automatisch nil und genau das wird im Ausgabebereich angezeigt, sobald man die Zuweisung entfernt. // Definition einer Variablen var temperatur1:Double = 32.5 // Definition einer Variablen als Optional var temperatur2:Double? // Ausgabe der beiden Variablen print(temperatur1) print(temperatur2) Löscht man die Zuweisung für die Variable temperatur1, kommt es sofort zu einer Fehlermeldung. Eine reguläre Variable muss vor der ersten Verwendung mit einem Wert initialisiert werden, sonst kann sie nicht ausgegeben werden. In Objective-C bekam man es mit nil zu tun, wenn ein Speicherzeiger auf kein gültiges Objekt verwies, und das war nicht immer ein gewünschter Zustand. In Swift ist nil bei einem Optional hingegen ein legitimer Wert. Er sagt aus, dass es für die Variable keinen Wert gibt. Um dies zu ermöglichen, muss der Typ aber explizit als Optional gekennzeichnet werden. So kann der Entwickler bestimmen, welche Variablen und Konstanten nil sein dürfen und welche nicht. 1.8 Programmieren mit Optionals Den Programmcode, wie er in der Software unseres digitalen Thermometers zum Einsatz kommen könnte, zeigt das folgende Beispiel. Ist die Variable temp gleich nil, wird der Text Keine Messung ausgegeben, gibt es hingegen einen Wert, finden wir stattdessen die Temperatur im Ausgabebereich. Probieren Sie das in Ihrer Entwicklungsumgebung aus. 46 1.8 Programmieren mit Optionals var temperatur:Double? = 18.2 if temperatur == nil { print("Keine Messung") } else { print(temperatur!) } Vereinfacht beschrieben, hat man es bei einem Optional immer mit zwei Werten zu tun. Wird eine Variable oder Konstante mit ihrem Namen angesprochen, kann man ausschließlich Informationen darüber erhalten, ob der Optional nil ist oder nicht. Ist der Zustand nicht nil, muss zunächst ein »Auspacken« stattfinden (Unwrapping in der englischen Dokumentation), um an den eigentlichen Wert zu gelangen. Das geschieht, indem man dem Bezeichner ein Ausrufezeichen anhängt, wie es im Listing mit temperatur! geschehen ist. Jetzt wird aus einem Optional-Double ein regulärer Double-Typ und in der Entwicklungsumgebung wird der Zahlenwert ohne den Zusatz Optional angezeigt. Auspacken sollte man einen Optional aber nur, wenn zuvor sichergestellt wurde, dass die Variable nicht nil ist, ansonsten führt der Versuch direkt zu einem Fehler. Vergleiche mit der if-Struktur werden wir uns in den folgenden Kapiteln genauer ansehen. Ein anderer Weg, einen Optional auf nil zu testen und auszupacken, funktioniert über ein Verfahren, das in Swift als Optional Binding bezeichnet wird. Dabei wird der Optional in der if-Struktur einer neuen Konstanten oder Variablen zugewiesen. Ist die Zuweisung erfolgreich, ist der Wert also nicht nil, erhält man den Wert automatisch in einer regulären Variablen, die aber nur innerhalb der ifStruktur verwendet werden kann. var temperatur:Double? = 18.2 if let temp = temperatur { print(temp) } else { print("Keine Messung") } 47 Kapitel 1 Datentypen und Optionals Für Umsteiger ist das Optional Binding vielleicht etwas gewöhnungsbedürftig, es lohnt sich aber sehr, diese Schreibweise zu verwenden, da sie zu kurzem und übersichtlichem Code führt. Optionale Typen kommen in Swift bei vielen Klassen und Strukturen zum Einsatz, sodass wir uns später noch mal mit diesem Thema beschäftigen werden. 48 Kapitel 2 Zeichenketten Neben den Typen Int, Double und Bool ist String einer der am häufigsten verwendeten Typen, denn Texte, in der Softwareentwicklung bekannt als Zeichenketten, sind für eine moderne Anwendung unverzichtbar. Zwar haben wir den Typ String schon kurz in einigen der vorherigen Beispiele verwendet, doch das war nur ein leichtes Kratzen an der Oberfläche. Im Gegensatz zu den Datentypen für Zahlen lässt sich ein String auf vielfältige Weise manipulieren. Wir können zusätzliche Zeichen anfügen oder Zeichen entfernen, Textteile tauschen oder die Groß- und Kleinschreibung ändern. Zusätzlich gibt es in Swift Funktionen, mit denen man prüfen kann, wie lang ein Text ist oder ob eine Zeichenkette eine andere enthält. Es lohnt sich also, diesen Datentyp genauer zu betrachten. Um die Beispiele auszuprobieren, legen Sie einen neuen Playground in Xcode an, den Sie Zeichenketten nennen. Der Datentyp String in Swift unterscheidet sich von der Klasse NSString, mit der Entwickler auf den Apple-Plattformen bisher gearbeitet haben. Der erste Unterschied wird sichtbar, sobald man eine Zeichenkette anlegt. In Objective-C war vor jedem Text ein @-Zeichen erforderlich, damit der Compiler Zeichenketten der Klasse NSString von den Texten der Sprache C unterscheiden konnte. Dies war nötig, denn Objective-C konnte mit C-Code vermischt werden. In Swift haben wir diese Möglichkeit nicht mehr und es gibt nur einen Datentyp für Zeichenketten. Die Definition eines Swift-Strings entspricht einer Schreibweise, wie sie in C# oder Java verwendet wird und es muss kein * mehr angegeben werden, um eine Variable als Speicherzeiger zu deklarieren. Die Zuweisung von einem Text zu einer Variablen oder Konstanten funktioniert genau wie bei den Datentypen Int, Float, Double oder dem Typ Bool. // Definition einer Zeichenkette in Swift // Der Datentyp müsste hier nicht angegeben werden var text:String = "Hallo, Welt!" // So sah die Definition einer Zeichenkette // in Objective-C aus // NSString *text = @"Hallo, Welt!" Möchte man mehrere Zeichenketten zu einem Text zusammenfügen, funktioniert das in Swift problemlos mit dem Plus-Operator (+). Obwohl man leicht den Ein- 49 Kapitel 2 Zeichenketten druck gewinnen könnte, handelt es sich dabei nicht um eine Addition. Die einzelnen Texte werden verkettet und so wird aus 50 + 40 nicht 90, sondern 5040. Bei Strings muss der Datentyp bei einer Variablendeklaration ebenfalls nicht mit angegeben werden. Der Compiler kann den Typ zuverlässig erkennen. // Strings mit dem Operator + verketten var value = "50" var newValue = value + "40" // newValue ist jetzt "5040" Der Plus-Operator ist in vielen Programmiersprachen ein gängiges Mittel, um Texte zu verketten, und leistet auch in Swift gute Dienste. Bei umfangreichen Verkettungen kann der Programmcode aber unübersichtlich werden. Das folgende Beispiel ist zwar noch gut zu verstehen, man muss bei der Programmierung aber schon aufpassen, um sämtliche Teile korrekt zu verketten. var firstName = "Georg" var lastName = "Freitag" // String durch Verkettung zusammengesetzt var fullName = "Mein Name ist " + firstName + " " + lastName + "." 2.1 String Interpolation Mehr Flexibilität und Übersicht bei der Zusammenstellung von Zeichenketten bietet in Swift ein Verfahren, das als String Interpolation bezeichnet wird. Bei der String Interpolation werden Variablennamen, eingebettet in \(), direkt in eine neue Zeichenkette geschrieben. So wird der Code besser lesbar, ist leichter einzugeben und Leerzeichen werden seltener vergessen oder an den falschen Stellen eingesetzt. Das \ ist auf deutschen Tastaturen leider nicht abgebildet. Sie finden es auf der Taste (7), wenn Sie gleichzeitig die Tasten (ª) und (Alt) drücken. ... // Zeichenketten werden mit der String Interpolation // zusammengesetzt var interpolatedFullName = "Mein Name ist \(firstName) \(lastName)." Nicht selten müssen in einem Projekt Zahlentypen in Zeichenketten umgewandelt werden, und das funktioniert mit der String Interpolation sehr einfach. Der Compiler ist sogar in der Lage, Rechenoperationen innerhalb der Klammern aufzulösen. Möchten Sie das Ergebnis einer Berechnung nur anzeigen, muss es zuvor nicht in einer eigenen Variablen oder Konstanten abgelegt werden. 50 2.1 String Interpolation // Double-Typen mit der String Interpolation // in einen Text umwandeln var price = 1.339 var liter = 8.5 var total = "\(liter) Liter zu \(price) kosten \(price * liter) €" Preise mit drei Dezimalstellen sind nicht so ungewöhnlich, wie man vielleicht erwarten würde. Bei fast jeder Tankstelle finden Sie für den Benzinpreis an dritter Stelle eine 9. Wollen wir in unserem Programm die Ausgabe auf zwei Dezimalstellen beschränken, müssen wir die Umwandlung anpassen. Mit der format-Anweisung und unter Verwendung sogenannter Format Specifier können wir dem Compiler genau mitteilen, wie die Zahlenwerte in Texte umgewandelt werden sollen. ... // Eine Konvertierung mithilfe von Format Specifiern var totalFormatted = String(format: "%.2f Liter zu %.2f kosten %.2f €", liter, price, liter * price) Wie das Beispiel zeigt, ist eine Konvertierung mit Format Specifiern aufwendiger als die Umwandlung mit der String Interpolation. Zunächst werden in einen vorbereiteten Text an den Stellen Platzhalter eingefügt, wo später formatierte Variablenwerte stehen sollen. Ein Format Specifier beginnt immer mit einem %-Zeichen und in unserem Beispiel bestimmt %.2f, dass wir an dieser Stelle eine Dezimalzahl einsetzen wollen, die auf zwei Nachkommastellen formatiert werden soll. Sämtliche Variablen in dem Ausdruck folgen erst nach dem String. Sie werden jeweils durch ein Komma getrennt und müssen in der gleichen Reihenfolge wie ihre zugehörigen Platzhalter im String angegeben werden. Beachtenswert ist die Konvertierung der Variablen liter. Sie wird auch mit %.2f formatiert und erhält somit ebenfalls zwei Dezimalstellen. Aus dem Wert 8.5 wird die Zeichenkette 8.50. Der Preis wird zu 1.34, wobei die Ausgabe gerundet wird. Der Variablenwert von 1.339 wird nicht verändert und steht in der Variablen price weiterhin zur Verfügung. 51 Kapitel 2 Zeichenketten Objective-C-Entwickler werden mit Format Specifiern vertraut sein, denn in dieser Programmiersprache haben sich die Platzhalter schon über viele Jahre bewährt. Zwei andere Format Specifier, die man kennen sollte, sind %i, als Platzhalter für eine ganze Zahl des Typs Int, sowie %@, als Platzhalter für Objekttypen wie String. Soll in einem formatierten String das %-Zeichen stehen, müssen Sie %% schreiben. // Int und String konvertieren mit Format Specifiern var firstName = "Georg" var lastName = "Freitag" var age = 49 var description = String(format: "Mein Name ist %@ %@. Ich bin %i Jahre alt.", firstName, lastName, age) 2.2 Zeichenketten vergleichen Ob eine String-Variable Zeichen enthält, kann über die Eigenschaft isEmpty ermittelt werden. Als Rückgabe erhalten wir einen booleschen Wert, entweder true oder false. So lassen sich beispielsweise die Eingaben eines Benutzers leicht überprüfen. Als alternativen Weg können wir über die Eigenschaften characters und count die Anzahl der Zeichen auswerten. Enthält ein String keine characters, also keine Zeichen, ist die Anzahl, die vom Typ Int ist, gleich 0. // Enthält die Zeichenkette Zeichen? var empty:Bool = fullName.isEmpty // Wie viele Zeichen enthält der String? var charCount = fullName.characters.count Sollen in Objective-C zwei Zeichenketten miteinander verglichen werden, ist eine spezielle Methode erforderlich, denn ein == überprüft nur die Speicheradressen zweier String-Objekte auf Gleichheit – und das ist selten die gewünschte Vorgehensweise. Wie in vielen anderen Programmiersprachen werden in Swift mit einem == tatsächlich die Textinhalte überprüft. Mit dem folgenden Beispiel können Sie das leicht selbst überprüfen. // Test von Zeichenketten auf Gleichheit var aFirstName = "Petra" var aLastName = "Brahms" if aFirstName + " " + aLastName == "Petra Brahms" { 52 2.2 Zeichenketten vergleichen print("Die Zeichenketten sind gleich.") } else { print("Die Zeichenketten sind ungleich.") } Soll bei einer Überprüfung auf Gleichheit die Groß- und Kleinschreibung ignoriert werden, wird die Programmierung aufwendiger. Dann muss man sich der compare-Methode von String bedienen und den Parameter NSStringCompareOptions auf CaseInsensitiveSearch setzen. Das Ergebnis dieses Vergleichs ist dann kein boolescher Wert, sondern ein NSComparisonResult. Dieser Typ verrät uns nicht nur, ob die Zeichenketten gleich oder ungleich sind, sondern zusätzlich, wie sie alphabetisch sortiert sind, wenn sie sich unterscheiden. Experimentieren Sie mit dem nächsten Beispiel, indem Sie verschiedene Wörter oder dieselben Wörter mit unterschiedlicher Groß- und Kleinschreibung ausprobieren. Die Funktionsweise vom compare wird sich Ihnen leicht erschließen. var city1 = "Dortmund" var city2 = "Bochum" // Vergleiche von city1 und city2 unter Verwendung der // Option CaseInsensitiveSearch, um die Groß- und // Kleinschreibung zu ignorieren. var result:NSComparisonResult = city1.compare(city2, options: NSStringCompareOptions.CaseInsensitiveSearch) if result == NSComparisonResult.OrderedSame { print("Die Zeichenketten sind gleich.") } else if result == NSComparisonResult.OrderedAscending { print("\(city1) kommt vor \(city2).") } else if result == NSComparisonResult.OrderedDescending { print("\(city2) kommt vor \(city1).") } Eine andere Möglichkeit, die Unterschiede der Groß- und Kleinschreibung bei einem Vergleich zu ignorieren, ergibt sich, wenn beide Zeichenketten zuvor in 53 Kapitel 2 Zeichenketten eine einheitliche Form umgewandelt werden. Über die Eigenschaft uppercaseString erhalten wir den Text eines Strings in Großbuchstaben und über lowercaseString als eine Zeichenkette ausschließlich kleiner Buchstaben. var city1 = "Frankfurt" var city2 = "frankFurt" // Die Städtenamen in große Buchstaben umwandeln var city1Upper = city1.uppercaseString var city2Upper = city2.uppercaseString if city1Upper == city2Upper { print("Die Zeichenketten sind gleich.") } else { print("Die Zeichenketten sind unterschiedlich.") } 2.3 Textabschnitte finden und ersetzen Gelegentlich muss in einer Software nicht genau geprüft werden, ob eine Zeichenkette einer anderen exakt entspricht, und es ist schon ausreichend, bestimmte Buchstabenfolgen in einem Text zu entdecken. Enthält die Statusmeldung von einem Server oder Dienst das Wort Error, ist es dort wohl zu einem Fehler gekommen, der untersucht werden muss. Fehlt dieses Wort in der Meldung, ist vermutlich alles in Ordnung. Genauso leicht könnte man differenzieren, ob eine Anrede das Wort Herr oder das Wort Frau enthält. Bestimmt fallen Ihnen sofort andere 54 2.3 Textabschnitte finden und ersetzen Anwendungsfälle ein. Möglich wird eine Überprüfung mit der Methode rangeOfString, wie sie das folgende Beispiel zeigt. Liefert der Aufruf ein Resultat ungleich nil, wurde die Zeichenkette gefunden. var city = "Hamburg" var search = "burg" // Prüfen, ob der Text von Variable city den Text // der Variablen search enthält. if city.rangeOfString(search) != nil { print("Die gesuchte Zeichenfolge ist enthalten.") } Der Rückgabewert der Methode rangeOfString ist ein Optional des Typs Range, mit dem man viel mehr tun kann, als ihn nur auf nil zu prüfen. Wurde eine Übereinstimmung gefunden, können wir über die Eigenschaften startIndex und endIndex genau ermitteln, an welcher Position die gesuchte Zeichenkette mit dem Text übereinstimmt. Durch eine zusätzliche Option kann bei rangeOfString die Groß- und Kleinschreibung ignoriert werden. Im nächsten Listing wird ein Optional Binding verwendet, um das Optional range in den Range-Typ validRange zu konvertieren. Das geschieht allerdings nur, wenn eine Übereinstimmung gefunden wurde, range also nicht nil ist. ... var range = place.rangeOfString(search, options: NSStringCompareOptions.CaseInsensitiveSearch) if let validRange = range { print("Startindex: \(validRange.startIndex)") print("Endindex: \(validRange.endIndex)") } else { print("Die gesuchte Zeichenfolge ist nicht enthalten.") } Sollen Buchstaben oder Wörter in einer Zeichenkette durch andere Zeichen ersetzt werden, kann man die Methode stringByReplacingOccurrencesOfString von String verwenden, die für genau diese Aufgabe entworfen wurde. 55 Kapitel 2 Zeichenketten Der Austausch findet jedoch nicht »an Ort und Stelle« in der bestehenden Zeichenkette statt, sondern wir erhalten als Rückgabe des Methodenaufrufs einen neuen Text. Diesen können wir derselben Variablen zuweisen. Im nachfolgenden Beispiel wird bei dem Text der Variablen greetings der Begriff Objective-C durch das Wort Swift ersetzt. // Den Begriff Objective-C in der Zeichenkette durch // das Wort Swift ersetzen. var greetings = "Hallo Objective-C-Entwickler!" greetings = greetings.stringByReplacingOccurrencesOfString ("Objective-C", withString: "Swift") Das englische Wort »Occurrences« kann mit »Vorkommnisse« oder »Auftreten« übersetzt werden und die Methode ersetzt nicht nur eines, sondern sämtliche Vorkommnisse in der Zeichenkette durch den angegebenen Parameter. Den Begriff Objective-C finden wir nur einmal im Text, aber versuchen Sie einmal, sämtliche e durch i zu ersetzen. Wird als Parameter ein leerer String übergeben, also "", wird der gesuchte Text aus der ursprünglichen Zeichenkette entfernt. Die Methode stringByReplacingOccurrencesOfString ist sehr leistungsfähig und so ist es nicht überraschend, dass man bei ihr ebenfalls die Groß- und Kleinschreibung ignorieren kann. Der Methodenaufruf muss lediglich um einen zusätzlichen Parameter erweitert werden. ... // Bei der Textersetzung die Groß- und Kleinschreibung im // Text ignorieren. greetings = greetings.stringByReplacingOccurrencesOfString ("hallo", withString: "Guten Tag", options: NSStringCompareOptions.CaseInsensitiveSearch) Handelt es sich bei Strings um Eingaben eines Anwenders, sollte man bemüht sein, Whitespaces (Leerraum) aus den Zeichenketten zu entfernen. Bei Whitespaces handelt es sich um nicht sichtbare Zeichen wie Tabulatoren, Zeilenumbrüche oder zusätzliche Leerzeichen, die vom Nutzer versehentlich mit dem Text eingegeben werden. Auf der grafischen Oberfläche sind diese Zeichen nicht ohne Mühen zu erkennen, innerhalb der Programmlogik können sie aber schnell zu Problemen führen. Ein String, der ein solches Zeichen enthält, unterscheidet sich in der Länge und vom Inhalt her von dem gleichen Text ohne Whitespace. Schon 56 2.4 Zeichenketten in Zahlentypen konvertieren ein einfacher Vergleich würde daher immer fehlschlagen. Glücklicherweise gibt es in Swift eine Methode, mit der man diesem Problem entgegenwirken kann. Der Aufruf von stringByTrimmingCharactersInSet mit dem NSCharaterSet whitespaceCharacterSet entfernt sämtliche Whitespaces und liefert einen bereinigten String zurück. // Sämtliche Whitespaces aus einem String entfernen var whitespaceString = " Mein Name ist Andreas. " var normalString = whitespaceString.stringByTrimmingCharactersInSet (NSCharacterSet.whitespaceAndNewlineCharacterSet()) Möchten Sie aus einer Zeichenkette sämtliche Zahlen entfernen, sollten Sie stringByTrimmingCharactersInSet mit dem NSCharacterSet.decimalDigitCharacter Set ausführen. Soll die Zahl innerhalb eines Strings freigestellt werden, kann man den gleichen Aufruf um die Eigenschaft invertedSet erweitern. Jetzt werden sämtliche Buchstaben entfernt. Punkte und Kommata bleiben enthalten. // Alle Buchstaben aus einem String entfernen var whitespaceAmount = "Der Rechnungsbetrag ist: 199,44" var normalAmount = whitespaceAmount.stringByTrimmingCharactersInSet (NSCharacterSet.decimalDigitCharacterSet().invertedSet) 2.4 Zeichenketten in Zahlentypen konvertieren Mit der String Interpolation oder mit Format Specifiern ist es in Swift leicht, Zahlenwerte in Zeichenketten umzuwandeln, und mit einer gut gewählten Formatierung können die Werte sogar gerundet werden. Eine Konvertierung in die andere Richtung, Zeichenketten zu Zahlentypen, ist hingegen schwieriger und nicht immer erfolgreich. Einen Text in eine ganze Zahl umzuwandeln ist noch recht einfach, wie der folgende Code zeigt. 57 Kapitel 2 Zeichenketten // Eine Zeichenkette in eine ganze Zahl umwandeln var numericStringValue = "223" var numericIntValue = Int(numericStringValue) Obwohl man es auf den ersten Blick nicht erkennen kann, haben wir es hier mit einem Optional zu tun, denn numericIntValue kann nil sein. Diesen Zustand erhalten wir, wenn sich die Zeichenkette nicht in eine Zahl umwandeln lässt, denn Buchstaben und andere Zeichen in numericStringValue machen die Konvertierung unmöglich. Deshalb ist es auch nicht möglich, die Variable numericIntValue als Int zu deklarieren. Wenn ein Typ angegeben werden soll, muss es ein Int-Optional sein. // Ungültige Zuweisung, denn die Variable muss ein // Optional sein. var numericIntValue:Int = Int(numericStringValue) // Gültige Zuweisung var numericIntValue:Int? = Int(numericStringValue) Auf ähnlichem Weg können Zeichenketten in Double-Typen konvertiert werden, wobei man allerdings schnell Gefahr läuft, nicht das korrekte Ergebnis zu erhalten. Der Punkt und das Koma in einer Dezimalzahl haben, je nach Region, unterschiedliche Bedeutungen. In Deutschland verwenden wir das Komma, um die Dezimalstellen vom ganzzahligen Anteil der Zahl zu trennen, in vielen englischsprachigen Ländern wird diese Aufgabe von einem Punkt übernommen. Für eine erfolgreiche und korrekte Umwandlung muss der Compiler wissen, welche Landeseinstellungen er verwenden soll. Die Konvertierung sollte deshalb nicht mehr von einer einzelnen Methode übernommen werden. Man benötigt ein Objekt der Klasse NSNumberFormatter. Im folgenden Beispiel wird der Text numberValue zunächst in einen Zahlenwert umgewandelt, um anschließend zurück in eine Zeichenkette konvertiert zu werden. Neben der Eigenschaft locale, mit der die Region gesetzt wird, können wir mit minimumFractionDigits und maximumFractionDigits die minimale und maximale Anzahl der Dezimalstellen bestimmen. Die Methode numberFromString liefert als Rückgabewert einen NSNumber-Optional, der nil ist, wenn die Umwandlung misslingt. Bei erfolgreicher Konvertierung wird der Wert im Listing mit stringFromNumber wieder in einen String konvertiert. Das Beispiel tut also nicht anderes, als eine Dezimalzahl, die als Zeichenkette vorliegt, auf zwei Nachkommastellen zu begrenzen. // Der ursprüngliche Werte als Zeichenkette var numberValue:String = "120,3456" 58 2.4 Zeichenketten in Zahlentypen konvertieren // Einen NSNumberFormatter erzeugen var formatter = NSNumberFormatter() formatter.locale = NSLocale(localeIdentifier: "de_DE") // Minimal und maximal zwei Nachkommastellen formatter.minimumFractionDigits = 2 formatter.maximumFractionDigits = 2 // Ein NSNumber-Typ als Optional, denn die // Umwandlung kann fehlschlagen. // Die explizite Angabe des Typs ist nicht zwingend, weil // numberFromString immer einen Optional zurückgibt. var number:NSNumber? = formatter.numberFromString(numberValue) if let validNumber = number { var newNumberValue = formatter.stringFromNumber(validNumber) } Auf den ersten Blick scheint die Konvertierung aufwendig, doch es ist nicht ungewöhnlich, dass Zahlen in einem Programm zunächst als Texte vorliegen und auf diese Art verarbeitet werden müssen. Denken Sie beispielsweise an Internetseiten, die ausschließlich aus Texten bestehen. Außerdem sollte man nicht vergessen, dass die Dezimalstellen der Zeichenkette nicht abgeschnitten werden, sondern die Zahl zusätzlich gerundet wird. Wenn die Gefahr besteht, dass die Zeichenkette zusätzlich Buchstaben enthält, sollten Sie den String zuvor mit stringByTrimmingCharactersInSet bereinigen. Unverzichtbar wird der NSNumberFormatter, wenn die Unterschiede verschiedener Länder und deren Schreibweisen beachtet werden müssen. So erhalten wir aus der Zeichenkette var numberValue:String = "120.3456" unter Verwendung der locale formatter.locale = NSLocale(localeIdentifier: "en_US") ebenfalls eine gerundete Zahl mit nur zwei Dezimalstellen, jetzt allerdings in der englischen/amerikanischen Schreibweise. Die im Betriebssystem gewählte Region 59 Kapitel 2 Zeichenketten kann in einem Projekt als currentLocale abgefragt werden. Im Zusammenspiel mit einem NSNumberFormatter sähe das so aus: // Die aktuellen Einstellungen für die Region // des Betriebssystems für eine Umwandlung verwenden formatter.locale = NSLocale.currentLocale() 2.5 Die Klasse NSNumber Der im letzten Beispiel verwendete Typ NSNumber wird in Objective-C oft als Hilfsmittel verwendet, weil die einfachen Datentypen der Sprache C (dort int, float, double und bool) nicht in einem Objective-C-Array verwendet werden können. Das funktioniert ausschließlich mit Objekten, weil diese mit Speicherzeigern arbeiten. In Swift haben wir die Einschränkung nicht, trotzdem ist NSNumber für uns eine vielseitige Klasse. Wie Sie gesehen haben, ist sie nützlich, um eine Zeichenkette in eine Zahl zu konvertieren. Eine Umwandlung funktioniert auch mit anderen Typen. Wird einem NSNumber-Objekt eine Dezimalzahl zugewiesen, kann man über die Eigenschaft integerValue an den ganzzahligen Anteil des Werts gelangen. Gerundet wird dabei nicht. // NSNumber mit einer Dezimalzahl anlegen var numValue:NSNumber = 4.501 // Die ganze Zahl des Wertes abrufen var intValue:Int = numValue.integerValue Eine Umwandlung in die andere Richtung ist ebenso leicht. Im nächsten Listing wird aus dem Int 4 der Double 4.0. Die Angabe der Typen Int und Double dient in beiden Beispielen der besseren Lesbarkeit. Erforderlich sind diese nicht. // NSNumber als eine ganze Zahl anlegen var numValue2:NSNumber = 4 // Den Wert als Dezimalzahl abrufen var doubleValue:Double = numValue2.doubleValue Beachtenswert ist die Konvertierung einer Zahl zu einem booleschen Wert, die mit NSNumber ebenfalls realisiert werden kann. Der Wert 0 ergibt false, jede andere Zahl ergibt true. // NSNumber als eine ganze Zahl anlegen var numValue3:NSNumber = 0 // Den Wert als booleschen Typ abrufen var boolValue:Bool = numValue3.boolValue 60 2.6 Texte teilen 2.6 Texte teilen Wenn es darum geht, Zeichenketten zu zerlegen oder Textabschnitte freizustellen, ist die Programmierung mit Swift sehr leicht, denn der Typ String bringt Methoden mit, die uns Entwicklern viel Arbeit abnehmen. Als nützlich erweisen sich substringFromIndex, mit der man eine Zeichenkette ab einer bestimmen Position ausschneiden kann, und substringToIndex, die uns einen Textabschnitt vom Anfang bis zu einer angegebenen Position zurückgibt. Verwendet man die Methoden zusammen mit einer Range, können Textabschnitte unkompliziert erstellt werden. Nehmen wir an, Sie möchten aus dem HTML-Quelltext einer Webseite den Titel der Seite ermitteln. Der Titel befindet sich zwar im Kopf des Dokuments, ist dort aber nicht zwangsläufig die erste Zeile. Erkennen kann man ihn jedoch immer an den Tags <title> und </title>, von denen er umschlossen wird. Wie man diesen Text auslesen könnte, zeigt das folgende Listing. Um den Programmcode flexibel zu halten, werden die zu suchenden Tags in Konstanten abgelegt. So können mit geringen Anpassungen andere Teile aus dem Text extrahiert werden. Statt einer kompletten Webseite verwenden wir im Beispiel einen kürzeren Text. Solange die beiden gesuchten Tags enthalten sind, haben wir mit dem Beispiel keine Schwierigkeiten. let startTag = "<title>" let endTag = "</title>" var pageTitle = "" var htmlSource = "Html Quelltext <title>Meine Webseite</title> htmlSource + " Mehr Html-Quelltext" var startRange = htmlSource.rangeOfString(startTag, options: NSStringCompareOptions.CaseInsensitiveSearch) if startRange != nil { // Text nach dem öffnenden Tag extrahieren pageTitle = htmlSource.substringFromIndex(startRange!.endIndex) var endRange = pageTitle.rangeOfString(endTag, options: NSStringCompareOptions.CaseInsensitiveSearch) if endRange != nil 61 Kapitel 2 Zeichenketten { // Text bis zum schließenden Tag freistellen pageTitle = pageTitle.substringToIndex(endRange!.startIndex) } } print(pageTitle) Im ersten Schritt wird mit der Methode rangeOfString die Range für das Tag <title> innerhalb der Zeichenkette htmlSource gesucht. Ist startRange nicht nil, wurde das Tag im Text gefunden. Abschließend wird durch den Aufruf der Methode substringFromIndex eine neue Zeichenkette gebildet. Sie beginnt dort, wo in htmlSource das gesuchte <title>-Tag endet. Das wird durch die Angabe von startRange!.endIndex realisiert. Jetzt haben wir den Anfang der Webseite abgeschnitten. Enden soll die Zeichenkette an der Stelle, an der das schließende Tag </title> zu finden ist. Nun benötigen wir die Methode substringToIndex und von dem Bereich endRange die Eigenschaft startIndex, denn das Tag selbst soll nicht Teil des Ergebnisses sein. Zögern Sie nicht, mit dem Beispiel zu spielen und mit den Methoden substringFromIndex und substringToIndex andere Dinge auszuprobieren. Vertauschen Sie bei den Bereichen die Eigenschaften startIndex und endIndex. Eine andere Methode bei der Arbeit mit Zeichenketten ist distanceTo, mit der die Länge einer Range ermittelt werden kann. Über die Eigenschaft indices erhalten wir einen Bereich, der die komplette Länge eines Strings umfasst. var myProfession = "Ich bin Softwareentwickler." // Eine Range anlegen, die den gesamten String beinhaltet var myRange = myProfession.characters.indices // Wie lang ist der Bereich von start bis end? var lengthOfString = myRange.startIndex.distanceTo(myRange.endIndex) 62 2.7 Subscripting mit Unicode print("Die Range ist \(lengthOfString) Zeichen lang.") // Die Zeichen zählen können wir auch mit count var charCount = myProfession.characters.count print("Die Range ist \(charCount) Zeichen lang.") 2.7 Subscripting mit Unicode Benötigt man eine Variable oder Konstante, die genau ein Zeichen aufnehmen kann, bietet sich der Typ Character an. Allerdings muss man bei der Deklaration eines Character angeben, dass man diesen Typ verwenden möchte. Die Type Inference des Compilers würde ansonsten, auch für Zeichenketten, die nur ein Zeichen lang sind, den Typ String einsetzen. // Das ist ein String var x1 = "X" // Das ist ein Character var x2: Character = "X" Swift verwendet für sämtliche Texte Unicode und so können neben dem lateinischen Alphabet zusätzlich viele andere Symbole in Zeichenketten benutzt werden. Leider gestaltet sich die Eingabe von Unicode-Zeichen mit einer regulären Tastatur am Mac schwierig, spezielle Tastenlayouts gibt es nur für iOS-Geräte. UnicodeZeichen können alternativ über ihren Code angegeben werden. var planet = "Earth \u{1F30D}" Unicode-Zeichen sind nicht alle gleich lang, oder genauer ausgedrückt: Nicht alle Zeichen benötigen gleich viel Speicherplatz. Während man bei den gebräuchlichsten Zeichen mit 16 Bit auskommt, kann ein Emoji 32 Bit belegen. Aus diesem Grund ist es nicht möglich, einfach über die Indexposition auf ein Zeichen innerhalb der Zeichenkette zuzugreifen. Das bei vielen anderen Programmiersprachen beliebte Subscripting, um einen Character aus einem Text auszulesen, funktioniert in Swift nicht. Zumindest nicht mit einem Integer. var planet = "Earth \u{1F30D}" // Subscripting funktioniert nicht mit Integern // var symbol = planet[2] 63 Kapitel 2 Zeichenketten Möchte man ein Zeichen an einer beliebigen Position aus einem Text auslesen, benötigt man keinen Int-Typ, sondern einen String.Index, den man am einfachsten aus einer Range der betreffenden Zeichenkette erstellt. Die Eigenschaft startIndex eines Bereichs verweist auf das erste Zeichen im Text, während endIndex die Position nach dem letzten Zeichen angibt. Die erste Position auszulesen ist somit kein Problem, denn startIndex und endIndex sind vom benötigen Typ String.Index. var planet = "Earth \u{1F30D}" // Ein Bereich, der den gesamten String umfasst var planetRange = planet.characters.indices // Das erste Zeichen der Zeichenkette var planetFirst = planet[planetRange.startIndex] Soll das sechste Zeichen, gezählt vom Anfang der Zeichenkette, ausgelesen werden, kommt die Methode advancedBy ins Spiel. Sie ermöglicht es, einen String.Index zu erzeugen, der eine beliebige Anzahl von Zeichen von startIndex verschoben ist. Dort können wir für die Zeichenanzahl mit einer ganzen Zahl arbeiten. ... // Eine Position, sechs Zeichen entfernt vom StartIndex var offset = 6 var stringIndex = planetRange.startIndex.advancedBy(offset) var symbol:Character = planet[stringIndex] Die Funktion advancedBy aus dem Listing kann man als »vorgerückt um« übersetzen. Wir erhalten eine Indexposition, die um sechs Zeichen vom Start des String vorgerückt ist. Möchten Sie eine Position vom Ende der Zeichenkette her zählen, können Sie advancedBy in Verbindung mit endIndex und einer negativen ganzen Zahl verwenden. Das Subscripting mit dem Typ String.Index funktioniert nicht nur mit einzelnen Zeichen, Abschnitte können ausgeschnitten werden, wenn man zwei Positionen angibt. Die drei Punkte im nächsten Listing kennzeichnen den Bereich als Closed Range, einen Abschnitt, in dem die beiden angegebenen Positionen mit enthalten sind. Es gibt auch eine Half-Open Range, doch dazu mehr in Kapitel 4. var blindText = "Franz jagt im komplett verwahrlosten" blindText + " Taxi quer durch Bayern." var blindRange = blindText.characters.indices 64 2.8 Anfang und Ende von Zeichenketten // Der Text zwischen den Positionen 23 und 35 var substring = blindText[blindRange.startIndex.advancedBy(23) ... blindRange.startIndex.advancedBy(35)] 2.8 Anfang und Ende von Zeichenketten Zwei weitere String-Methoden, mit denen wir uns in diesem Kapitel beschäftigen wollen, sind prefix und suffix. Mit ihnen können ebenfalls Abschnitte aus einer Zeichenkette extrahiert werden. prefix liefert eine Anzahl von Zeichen vom Anfang einer Zeichenkette und suffix Zeichen vom Ende des Textes. Obwohl sie ähnlich arbeiten, sind sie nicht zu den Methoden substringFromIndex und substringToIndex kompatibel. prefix und suffix benötigen für die Anzahl der Zeichen einen Int-Typ als Parameter, die zwei Methoden der Klasse String einen String.Index-Typ, wie er bei einer Range verwendet wird. Außerdem sind prefix und suffix in der Anwendung toleranter. Wird für die Anzahl der Zeichen ein Wert angegeben, der größer als die Länge des gesamten Strings ist, erhält man den kompletten String. Der Versuch, eine der substring-Funktionen mit einer Range anzuwenden, die über den String hinausgeht, führt hingegen zu einer Fehlermeldung. var demoText = "Falsches Üben von Xylophonmusik" demoText + " quält jeden größeren Zwerg." // Die ersten acht Zeichen var pre = String(demoText.characters.prefix(8)) // Die letzten sieben Zeichen var sub = String(demoText.characters.suffix(7)) Benötigt man von einer Zeichenkette nur das erste oder nur das letzte Zeichen, bieten sich die Eigenschaften first und last an. Rückgabewerte beider Funktionen sind Character-Optionals, denn wenn die ursprüngliche Zeichenkette leer ist, gibt es kein erstes und kein letztes Zeichen und der Optional ist nil. var xylophon = "Xylophonmusik" var firstChar:Character? = xylophon.characters.first var lastChar:Character? = xylophon.characters.last 65 Kapitel 2 Zeichenketten Soll von einem Text das erste oder das letzte Zeichen entfernt werden, sind dies Aufgaben für dropFirst und dropLast. // Das erste Zeichen entfernen var firstDropped = String(xylophon.characters.dropFirst()) // Das letzte Zeichen entfernen var lastDropped = String(xylophon.characters.dropLast()) Kombiniert man die Methoden, können mit geringem Aufwand alltägliche Aufgaben erledigt werden. Im nachfolgenden Code wird die Groß- und Kleinschreibung eines Wortes korrigiert. Der erste Buchstabe soll groß sein, alle anderen Buchstaben klein. var reform = "reChtSchreiBrEforM" var firstChr = reform.characters.first reform = String(reform.characters.dropFirst()) // Zeichenkette neu zusammensetzen reform = "\(String(firstChr!).uppercaseString)" + "\(reform.lowercaseString)" Ist Ihnen aufgefallen, dass sämtliche Zuweisungen und Vergleiche ausgeführt werden, obwohl Ihr Programm über keine main-Methode oder main-Funktion verfügt? Mit einem Programm der Sprache C wäre das nicht möglich. Bei einem Playground werden die Anweisungen ständig eingelesen, analysiert und die Ergebnisse ausgegeben. Dieses Verfahren wird als Read-Eval-Print Loop (REPL) bezeichnet und ist für andere Entwicklungsumgebungen schon länger gebräuchlich. Welche Informationen uns der Playground im Ausgabebereich noch anzeigen kann, werden Sie im nächsten Kapitel erfahren. 66 Kapitel 3 Arrays und Dictionaries Mit einzelnen Variablen und Konstanten können in der Softwareentwicklung viele Probleme gelöst werden, aber genauso oft benötigt man in einem Programm Listen, mit denen mehrere Werte gemeinsam verwaltet werden können. Swift bietet uns für diese Aufgabe Arrays und Dictionaries, mit denen wir uns in diesem Kapitel beschäftigen wollen. Wenn Sie die gezeigten Beispiele ausprobieren wollen, sollten Sie in Ihrer Entwicklungsumgebung einen Playground mit dem Namen Arrays anlegen. Sie finden die Beispiele auch unter dem gleichen Namen auf der Webseite zum Buch. 3.1 Arrays – Listen von Elementen Eine Möglichkeit, eine Liste von Werten umzusetzen, führt in Swift über die Definition eines Arrays. Im einfachsten Fall handelt es sich um eine durch Kommata getrennte Aneinanderreihung von Werten, die von eckigen Klammern eingefasst werden. Ein Array aus Werten des Typs String mit Städtenamen würde so aussehen: // Definition eines Arrays aus Zeichenketten var cities = ["Berlin", "Düsseldorf", "Frankfurt", "München", "Kiel"] Über den Variablennamen cities können die einzelnen Zeichenketten nicht angesprochen werden, man benötigt zusätzlich die Angabe der Position, an der sich der gewünschte Wert in der Auflistung befindet. Die Position, oft als Index bezeichnet, wird in eckigen Klammern hinter dem Array-Bezeichner angegeben, sodass wir es erneut mit der Subscript-Syntax zu tun haben, die wir zuvor bei den Zeichenketten verwendet haben. Wichtig beim Subscripting ist der Umstand, dass sich das erste Element der Auflistung nicht an Position 1 befindet, sondern an Position 0. Wie das folgende Beispiel zeigt, können Variablen und Konstanten ebenfalls einem Array hinzugefügt werden. Es muss nicht zwingend aus Werten gebildet werden, die direkt im Programmcode stehen. 67 Kapitel 3 Arrays und Dictionaries var city1 = "Hamburg" let city2 = "Dortmund" // Ein Array aus festen Werten, einer Variablen // und einer Konstanten. var cities = ["Berlin", "Düsseldorf", "Frankfurt", "München", "Kiel", city1, city2] // Ausgabe der Stadt an der Position 5 im Array // Zugriff auf ein Element der Liste mit Subscripting print("Meine Stadt ist \(cities[5]).") Wird ein Array als Variable mit var deklariert, können der Auflistung nach der ersten Initialisierung zusätzliche Werte angehängt werden. Ist das Array eine Konstante, funktioniert das nicht, dann sind die Elemente und die Anzahl der Elemente in der Liste unveränderlich. Wichtig Die englische Dokumentation verwendet für Listen, die nachträglich manipuliert werden können, den Begriff »mutable« für »veränderlich«. In Swift wird ein Array unveränderlich, sobald es als Konstante definiert wird. Objective-C verwendet stattdessen zwei verschiedene Klassen: NSArray und NSMutableArray. Im nächsten Listing wird die Methode append und als Alternative der +=-Operator verwendet, um das Array zu erweitern. Beide Anweisungen fügen Elemente am Ende der Liste an. Mit dem +=-Operator ist es zusätzlich möglich, mehrere Werte mit nur einer Anweisung anzuhängen. Faktisch wird das Array um ein weiteres Array erweitert. Mit der append-Methode funktioniert das nicht, sie akzeptiert nur einen einzelnen Wert. 68 3.1 Arrays – Listen von Elementen // Weitere Städte zum Array hinzufügen cities.append("Bonn") cities += ["Köln"] cities += ["Rostock", "Aachen"] Soll einem Array ein Element nicht am Ende der Liste, sondern an einer anderen Position hinzugefügt werden, können Sie die Methode insert verwenden. Sie benötigt neben dem zuzufügenden Wert zusätzlich die Position, an der er eingefügt werden soll. Die nachfolgenden Elemente werden um eine Position verschoben. // Eine weitere Stadt an Position 2 einfügen cities.insert("Ulm", atIndex: 2) Muss ein Element in der Auflistung ersetzt werden, ist es ausreichend, die gewünschte Position zu wählen und ihr einen anderen Wert zuzuweisen. In Swift können auf diesem Weg sogar Abschnitte eines Arrays ersetzt werden, wobei die Anzahl der Werte nicht identisch sein muss. Im nächsten Listing wird dem Array an Position 1 ein Element zugewiesen und die beiden Werte an den Positionen 2 und 3 werden durch ein neues Array ersetzt, das jedoch nur ein Element enthält. Das cities-Array wird somit kürzer. Die aktuelle Anzahl der Objekte in der Auflistung kann bei einem Array über dessen Eigenschaft count ermittelt werden. // Ein Element im Array ersetzen cities[1] = "Leipzig" var count1 = cities.count // Zwei Elemente im Array durch ein neues Element ersetzen cities[2...3] = ["Oberhausen"] var count2 = cities.count 69