summaryrefslogtreecommitdiffhomepage
path: root/de-de/swift-de.html.markdown
blob: c4da99f5fedab078075c6725aec72029b9b270c6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
---
language: swift
contributors:
  - ["Grant Timmerman", "http://github.com/grant"]
  - ["Christopher Bess", "http://github.com/cbess"]
  - ["Joey Huang", "http://github.com/kamidox"]  
  - ["Anthony Nguyen", "http://github.com/anthonyn60"]
translators:
    - ["Jonas Wippermann", "http://vfuc.co"]
filename: learnswift-de.swift
lang: de-de
---

Swift ist eine Programmiersprache von Apple für die Entwicklung von iOS und macOS Applikationen. Swift wurde 2014 zu Apples WWDC Entwicklerkonferenz vorgestellt und wurde mit dem Ziel entwickelt, fehlerträchtigen Code zu vermeiden sowie mit Objective-C zu koexistieren. Es wird mit dem LLVM Compiler gebaut und ist ab Xcode 6+ verfügbar.

Das offizielle [Swift Programming Language](https://itunes.apple.com/us/book/swift-programming-language/id881256329) Buch von Apple ist kostenlos via Apple Books verfügbar.

Außerdem hilfreich ist Apples [Getting Started Guide](https://developer.apple.com/library/prerelease/ios/referencelibrary/GettingStarted/RoadMapiOS/index.html), ein guter Einstiegspunkt mit komplettem Swift-Tutorial.

```swift
// importiere ein Modul
import UIKit

//
// MARK: Grundlagen
//

// Xcode unterstützt "Landmarks" um Code zu gliedern, sie werden in der Jump Bar aufgelistet
// MARK: Abschnitts-Markierung
// TODO: Zu erledigen
// FIXME: Zu beheben

// In Swift 2 wurden println und print zusammengefasst in eine print-Methode. Es wird automatisch ein Zeilenumbruch angehängt.
print("Hello, world!") // println ist jetzt print
print("Hello, world!", appendNewLine: false) // printen ohne Zeilenumbruch am Ende

// Variablen (var) können nach der Initialisierung verändert werden 
// Konstanten (let) können nach der Initialisierung NICHT verändert werden 

var myVariable = 42
let øπΩ = "value" // Unicode-Variablennamen
let π = 3.1415926
let convenience = "keyword" // Kontext-abhängiger Variablenname
let weak = "keyword"; let override = "another keyword" // Instruktionen können durch ein Semikolon aufgeteilt werden
let `class` = "keyword" // Nutze "Backticks" um Schlüsselwörter als Variablennamen zu verwenden
let explicitDouble: Double = 70 // Typ explizit festgelegt
let intValue = 0007 // 7
let largeIntValue = 77_000 // 77000
let label = "some text " + String(myVariable) // Casting
let piText = "Pi = \(π), Pi 2 = \(π * 2)" // String Interpolation

// Build-spezifische Werte
// benutzt -D build configuration
#if false
    print("not printed")
    let buildValue = 3
#else
    let buildValue = 7
#endif
print("Build value: \(buildValue)") // Build value: 7

/*
    Optionals ist ein Swift-Feature, welches ermöglicht, dass eine Variable entweder einen (`Some`) oder keinen (`None`) Wert hat

    Da Swift von jeder property einen Wert erwartet, muss sogar nil explizit als Optional festgelegt werden.

    Optional<T> ist ein Enum.
*/
var someOptionalString: String? = "optional" // Kann nil sein
// Genau wie oben, aber ? ist ein postfix operator (Syntax Candy)
var someOptionalString2: Optional<String> = "optional"

if someOptionalString != nil {
    // Ich bin nicht nil
    if someOptionalString!.hasPrefix("opt") {
        print("has the prefix")
    }
    
    let empty = someOptionalString?.isEmpty
}
someOptionalString = nil

// Implizit entpackter Optionalwert
var unwrappedString: String! = "Value is expected."
// Genau wie oben, aber ! ist ein postfix operator (noch mehr Syntax Candy)
var unwrappedString2: ImplicitlyUnwrappedOptional<String> = "Value is expected."

if let someOptionalStringConstant = someOptionalString {
    // hat einen (`Some`) Wert, nicht nil
    if !someOptionalStringConstant.hasPrefix("ok") {
        // hat keinen "ok"-Prefix
    }
}

// Swift unterstützt das festlegen von Werten eines beliebigen Typens
// AnyObject == id
// Im Gegensatz zum Objective-C `id`, funktioniert AnyObject mit jeglichen Werten (Class, Int, struct, etc)
var anyObjectVar: AnyObject = 7
anyObjectVar = "Changed value to a string, not good practice, but possible."

/*
    Ein Kommentar
    
    /*
        Verschachtelte Kommentare sind ebenfalls unterstützt
    */
*/

//
// MARK: Collections
//

/*
    Array und Dictionary-Typen sind structs. 
    Deswegen implizieren `let` und `var` bei der Initialisierung auch ob sie änderbar (var) oder unveränderlich (let) sind.
*/

// Array
var shoppingList = ["catfish", "water", "lemons"]
shoppingList[1] = "bottle of water"
let emptyArray = [String]() // let == unveränderlich
let emptyArray2 = Array<String>() // genau wie oben
var emptyMutableArray = [String]() // var == änderbar


// Dictionary
var occupations = [
    "Malcolm": "Captain",
    "kaylee": "Mechanic"
]
occupations["Jayne"] = "Public Relations"
let emptyDictionary = [String: Float]() // let == unveränderlich
let emptyDictionary2 = Dictionary<String, Float>() // genau wie oben
var emptyMutableDictionary = [String: Float]() // var == änderbar


//
// MARK: Kontrollstruktur
//

// for-Schleife (array)
let myArray = [1, 1, 2, 3, 5]
for value in myArray {
    if value == 1 {
        print("One!")
    } else {
        print("Not one!")
    }
}

// for-Schleife mit Indizes (array)
for index in myArray.indices {
    print("Value with index \(index) is \(myArray[index])")
}

// for-Schleife (dictionary)
var dict = ["one": 1, "two": 2]
for (key, value) in dict {
    print("\(key): \(value)")
}

// for-Schleife (range)
for i in -1...shoppingList.count {
    print(i)
}
shoppingList[1...2] = ["steak", "peacons"]
// ..< schließt letzte Nummer aus

// while-Schleife
var i = 1
while i < 1000 {
    i *= 2
}

// do-while-Schleife
do {
    print("hello")
} while 1 == 2

// Switch
// Sehr mächtig, wie `if` statement mit Syntax Candy
// Unterstützt Strings, Objekt-Instanzen und primitive Typen (Int, Double, etc)
let vegetable = "red pepper"
switch vegetable {
case "celery":
    let vegetableComment = "Add some raisins and make ants on a log."
case "cucumber", "watercress":
    let vegetableComment = "That would make a good tea sandwich."
case let localScopeValue where localScopeValue.hasSuffix("pepper"):
    let vegetableComment = "Is it a spicy \(localScopeValue)?"
default: // notwendig (um alle möglichen Eingaben zu verarbeiten)
    let vegetableComment = "Everything tastes good in soup."
}


//
// MARK: Funktionen
//

// Funktionen sind ein sogenannter "first-class" Typ, was bedeutet, dass sie
// in Funktionen geschachtelt werden und "herumgereicht" werden können

// Funktion mit Swift header Dokumentation

/**
    Eine Grüß-Funktion

    - Ein Aufzählungspunkt
    - Ein weiterer Aufzählungspunkt in der Dokumentation

    :param: name Ein Name
    :param: day Ein Tag
    :returns: Ein String, der Name und Tag beinhält.
*/
func greet(name: String, day: String) -> String {
    return "Hello \(name), today is \(day)."
}
greet("Bob", "Tuesday")

// Ähnlich wie oben, bloß anderes Funktions-Parameter-Verhalten
func greet2(#requiredName: String, externalParamName localParamName: String) -> String {
    return "Hello \(requiredName), the day is \(localParamName)"
}
greet2(requiredName:"John", externalParamName: "Sunday")


// Funktion, welche mehrere Werte in einem Tupel zurückgibt
func getGasPrices() -> (Double, Double, Double) {
    return (3.59, 3.69, 3.79)
}
let pricesTuple = getGasPrices()
let price = pricesTuple.2 // 3.79
// Ignoriere Tupel-(oder andere)Werte mit _ (Unterstrich)
let (_, price1, _) = pricesTuple // price1 == 3.69
print(price1 == pricesTuple.1) // true
print("Gas price: \(price)")

// Variierende Argumente..
func setup(numbers: Int...) {
    // .. liegen als Array vor
    let number = numbers[0]
    let argCount = numbers.count
}

// Funktionen übergeben und zurückgeben
func makeIncrementer() -> (Int -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
increment(7)

// Übergabe via Referenz ("Pass by reference")
func swapTwoInts(inout a: Int, inout b: Int) {
    let tempA = a
    a = b
    b = tempA
}
var someIntA = 7
var someIntB = 3
swapTwoInts(&someIntA, &someIntB)
print(someIntB) // 7


//
// MARK: Closures
//
var numbers = [1, 2, 6]

// Funktionen sind besondere Closures ({})

// Closure Beispiel
// `->` teilt Parameter und Rückgabe-Typ
// `in` teilt den Closure Header vom Body
numbers.map({
    (number: Int) -> Int in
    let result = 3 * number
    return result
})


// Wenn der Typ bekannt ist, wie oben, kann folgendes getan werden
numbers = numbers.map({ number in 3 * number })
// oder sogar dies
//numbers = numbers.map({ $0 * 3 })

print(numbers) // [3, 6, 18]

// "Schleppende Closure" (Trailing Closure)
numbers = sorted(numbers) { $0 > $1 }

print(numbers) // [18, 6, 3]

// Sehr verkürzt, da sich der Typ durch den < Operator ableiten lässt

numbers = sorted(numbers, < )

print(numbers) // [3, 6, 18]

//
// MARK: Strukturen
// (häufig einfach structs)
//

// Structures und Klassen haben sehr ähnliche Fähigkeiten
struct NamesTable {
    let names = [String]()
    
    // Eigendefiniertes subscript
    subscript(index: Int) -> String {
        return names[index]
    }
}


// Strukturen haben eine automatisch generierte, designierte Initialisierungsfunktion
let namesTable = NamesTable(names: ["Me", "Them"])
let name = namesTable[1]
print("Name is \(name)") // Name is Them

//
// MARK: Klassen
//

// Klassen, Strukturen und deren Member haben drei Ebenen der Zugriffskontrolle
// Es gibt: internal (default), public, private

public class Shape {
    public func getArea() -> Int {
        return 0;
    }
}

// Alle Methoden und Properties einer Klasse sind public
// Wenn das einfache Ziel ist, Daten in einem strukturierten Objekt zu halten,
// sollte ein `struct` verwendet werden

internal class Rect: Shape {
    var sideLength: Int = 1
    
    // Eigendefinierte Getter und Setter für die Property
    private var perimeter: Int {
        get {
            return 4 * sideLength
        }
        set {
            // `newValue` ist eine implizite Variable, welche in Settern verfügbar ist
            sideLength = newValue / 4
        }
    }
    
    // "Lazy" (faules) Laden einer Property, sie bleibt uninitialisiert (nil),
    // bis sie aufgerufen wird
    lazy var subShape = Rect(sideLength: 4)
    
    // Wenn kein eigendefinierter Getter/Setter notwendig ist,
    // aber trotzdem Code vor und nach dem Setzen eines Variablenwertes laufen soll,
    // kann "willSet" und "didSet" benutzt werden
    var identifier: String = "defaultID" {
        // der `willSet` Parameter wird der Variablenname für den neuen Wert sein 
        willSet(someIdentifier) {
            print(someIdentifier)
        }
    }
    
    init(sideLength: Int) {
        self.sideLength = sideLength
        // super.init muss immer aufgerufen werden, wenn eigene Properties initialisiert werden
        super.init()
    }
    
    func shrink() {
        if sideLength > 0 {
            sideLength -= 1
        }
    }
    
    override func getArea() -> Int {
        return sideLength * sideLength
    }
}

// Eine simple `Square`-Klasse erbt von/erweitert `Rect`
class Square: Rect {
    convenience init() {
        self.init(sideLength: 5)
    }
}

var mySquare = Square()
print(mySquare.getArea()) // 25
mySquare.shrink()
print(mySquare.sideLength) // 4

// Casten der Instanz
let aShape = mySquare as Shape

// Vergleiche Instanzen, nicht äquivalent zum == , welches Objekte vergleicht ("equal to") 
if mySquare === mySquare {
    print("Yep, it's mySquare")
}

// Optionale Initialisierung
class Circle: Shape {
    var radius: Int
    override func getArea() -> Int {
        return 3 * radius * radius
    }
    
    // Ein Fragezeichen nach `init` ist eine optionale Initialisierung,
    // welche nil zurückgeben kann
    init?(radius: Int) {
        self.radius = radius
        super.init()
        
        if radius <= 0 {
            return nil
        }
    }
}

var myCircle = Circle(radius: 1)
print(myCircle?.getArea())    // Optional(3)
print(myCircle!.getArea())    // 3
var myEmptyCircle = Circle(radius: -1)
print(myEmptyCircle?.getArea())    // "nil"
if let circle = myEmptyCircle {
    // wird nicht ausgeführt, da myEmptyCircle nil ist
    print("circle is not nil")
}


//
// MARK: Enums
//

// Enums können optional einen eigenen Typen haben
// Wie Klassen auch können sie Methoden haben

enum Suit {
    case spades, hearts, diamonds, clubs
    func getIcon() -> String {
        switch self {
        case .spades: return "♤"
        case .hearts: return "♡"
        case .diamonds: return "♢"
        case .clubs: return "♧"
        }
    }
}


// Enum-Werte können vereinfacht geschrieben werden, es muss nicht der Enum-Typ
// genannt werden, wenn die Variable explizit deklariert wurde

var suitValue: Suit = .hearts

// Nicht-Integer-Enums brauchen direkt zugewiesene "Rohwerte"
enum BookName: String {
    case john = "John"
    case luke = "Luke"
}
print("Name: \(BookName.john.rawValue)")

// Enum mit assoziierten Werten
enum Furniture {
    // mit Int assoziiert
    case desk(height: Int)
    // mit String und Int assoziiert
    case chair(String, Int)

    func description() -> String {
        switch self {
        case .desk(let height):
            return "Desk with \(height) cm"
        case .chair(let brand, let height):
            return "Chair of \(brand) with \(height) cm"
        }
    }
}

var desk: Furniture = .desk(height: 80)
print(desk.description())     // "Desk with 80 cm"
var chair = Furniture.chair("Foo", 40)
print(chair.description())    // "Chair of Foo with 40 cm"


//
// MARK: Protokolle
//

// Protokolle (`protocol`s) können verlangen, dass entsprechende
// Typen spezifische Instanz-Properties, Instanz/Klassen-Methoden,
// Operatoren oder Subscripts implementieren/haben

protocol ShapeGenerator {
    var enabled: Bool { get set }
    func buildShape() -> Shape
}

// Protocols mit @objc deklariert ermöglichen optionale Funktionen,
// welche es ermöglichen, abzufragen ob ein Typ einem Protokoll entspricht
@objc protocol TransformShape {
    optional func reshaped()
    optional func canReshape() -> Bool
}

class MyShape: Rect {
    var delegate: TransformShape?
    
    func grow() {
        sideLength += 2

        // Ein Fragezeichen nach einer optionalen Property, Methode oder Subscript
        // ignoriert elegant Nil-Werte und geben nil zurück, anstatt einen Laufzeitfehler zu werfen
        // Dies wird "optional Chaining" (optionale Verkettung) genannt
        if let allow = self.delegate?.canReshape?() {
            // frage erst nach delegate, dann nach Methode
            self.delegate?.reshaped?()
        }
    }
}


//
// MARK: Sonstiges
//

// `extension`s: (Erweiterungen), erweitere Typen um zusätzliche Funktionalität

// Square entspricht jetzt dem `Printable` Protokoll
extension Square: Printable {
    var description: String {
        return "Area: \(self.getArea()) - ID: \(self.identifier)"
    }
}

print("Square: \(mySquare)")

// Standardtypen können ebenfalls erweitert werden
extension Int {
    var customProperty: String {
        return "This is \(self)"
    }
    
    func multiplyBy(num: Int) -> Int {
        return num * self
    }
}

print(7.customProperty) // "This is 7"
print(14.multiplyBy(3)) // 42


//Generics: Ähnlich zu Java und C#. Nutze das `where` keyword um die Bedingung
// des Generics festzulegen

func findIndex<T: Equatable>(array: [T], valueToFind: T) -> Int? {
    for (index, value) in enumerate(array) {
        if value == valueToFind {
            return index
        }
    }
    return nil
}
let foundAtIndex = findIndex([1, 2, 3, 4], 3)
print(foundAtIndex == 2) // true

// Operatoren:
// Eigendefinierte Operatoren können mit diesen Zeichen beginnen:
//      / = - + * % < > ! & | ^ . ~
// oder
// Unicode Mathematik, Symbole, Pfeile, Dingbat, und Linien/Box - Zeichen
prefix operator !!! {}


// Ein Prefix-Operator, welcher die Seitenlänge verdreifacht
prefix func !!! (inout shape: Square) -> Square {
    shape.sideLength *= 3
    return shape
}

// Aktueller Wert
print(mySquare.sideLength) // 4

// Wert nach verwendung des eigenen Operators
!!!mySquare
print(mySquare.sideLength) // 12
```