Lernziel
Nach dieser Lektion kannst du eigene Funktionen schreiben, ihnen Werte übergeben (Parameter), Ergebnisse zurückgeben und Parameter mit Argument-Labels so benennen, dass sich der Aufruf wie ein Satz liest.
Das Problem: Derselbe Code an vielen Stellen
Stell dir vor, du berechnest in einem Programm an mehreren Stellen den Preis inklusive Mehrwertsteuer:
let shirt = 20.0
print(shirt * 1.2)
let shoes = 80.0
print(shoes * 1.2)
let hat = 15.0
print(hat * 1.2)Jetzt ändert sich der Steuersatz. Du musst jede einzelne Stelle finden und anpassen. Übersiehst du eine, rechnet dein Programm falsch, und niemand merkt es.
Die Lösung: Den Code einmal schreiben, ihm einen Namen geben und ihn überall aufrufen. Das ist eine Funktion.
Die einfachste Funktion
func sayHello() {
print("Hallo!")
}
sayHello() // Hallo!
sayHello() // Hallo!funcleitet eine Funktion ein.sayHelloist ihr Name, wieder in camelCase. Funktionsnamen beschreiben meist eine Tätigkeit, deshalb beginnen sie oft mit einem Verb.- Die runden Klammern
()sind Pflicht, auch wenn sie leer sind. - Der Block in geschweiften Klammern ist der Inhalt der Funktion, der Funktionskörper.
Das Anlegen nennt man Deklarieren oder Definieren. Dabei passiert noch nichts. Erst der Aufruf sayHello() führt den Code aus.
print ist übrigens auch eine Funktion, nur dass Apple sie schon für dich geschrieben hat.
Parameter: Werte übergeben
Eine Funktion, die immer dasselbe tut, ist begrenzt nützlich. Mit Parametern gibst du ihr Werte mit:
func greet(name: String) {
print("Hallo, \(name)!")
}
greet(name: "Anna") // Hallo, Anna!
greet(name: "Ben") // Hallo, Ben!In den Klammern steht der Parameter: ein Name (name) und ein Typ (String). Innerhalb der Funktion kannst du name wie eine Konstante benutzen.
Beim Aufruf schreibst du den Parameternamen mit Doppelpunkt davor: greet(name: "Anna"). Das macht Aufrufe sehr gut lesbar.
Mehrere Parameter
func greet(name: String, times: Int) {
for _ in 1...times {
print("Hallo, \(name)!")
}
}
greet(name: "Anna", times: 3)Die Reihenfolge beim Aufruf muss der Reihenfolge in der Definition entsprechen.
Rückgabewerte: Ergebnisse zurückgeben
Bisher haben unsere Funktionen etwas ausgegeben. Viel häufiger sollen sie aber etwas berechnen und das Ergebnis zurückgeben:
func priceWithTax(net: Double) -> Double {
return net * 1.2
}
let shirtPrice = priceWithTax(net: 20)
print(shirtPrice) // 24.0-> Doublenach den Klammern heißt: „Diese Funktion gibt einenDoublezurück.“ Den Pfeil tippst du als Bindestrich und Größer-Zeichen.returnbeendet die Funktion und liefert den Wert zurück an die Stelle des Aufrufs.
Das Ergebnis kannst du in einer Konstante speichern, direkt ausgeben oder weiterverwenden:
let total = priceWithTax(net: 20) + priceWithTax(net: 80)Und jetzt der eigentliche Gewinn: Ändert sich der Steuersatz, änderst du eine einzige Zeile.
Implizites return
Besteht der Funktionskörper nur aus einem einzigen Ausdruck, kannst du return weglassen:
func priceWithTax(net: Double) -> Double {
net * 1.2
}Diese Kurzform wirst du in SwiftUI überall sehen, zum Beispiel bei var body: some View { ... }. Auch dort steht kein return.
Tipp
Eine Funktion mit -> muss auf jedem Weg etwas zurückgeben. Vergisst du in einem if-Zweig das return, meldet Xcode Missing return in global function expected to return 'Double'.
Argument-Labels: Aufrufe wie Sätze
Swift legt großen Wert darauf, dass sich ein Funktionsaufruf wie ein englischer Satz liest. Dafür kann ein Parameter zwei Namen haben:
func greet(to name: String) {
print("Hallo, \(name)!")
}
greet(to: "Anna")toist das Argument-Label. Es wird beim Aufruf verwendet.nameist der Parametername. Er wird innerhalb der Funktion verwendet.
So liest sich der Aufruf als „greet to Anna“, und innerhalb der Funktion hast du trotzdem einen sinnvollen Namen.
Kein Label mit _
Manchmal ist schon aus dem Funktionsnamen klar, was übergeben wird. Dann lässt du das Label mit einem Unterstrich weg:
func square(_ number: Int) -> Int {
number * number
}
print(square(5)) // 25square(5) liest sich natürlicher als square(number: 5). print("Hallo") ist übrigens genauso aufgebaut, deshalb steht dort kein Label.
Gute Namen für Funktionen
Lies den Aufruf laut vor. Klingt er wie ein verständlicher Satz?
- ✅
move(from: a, to: b) - ✅
isValid(email) - ❌
doIt(x: a, y: b)
Standardwerte
Parameter können einen Standardwert haben. Dann darf man sie beim Aufruf weglassen:
func priceWithTax(net: Double, rate: Double = 0.2) -> Double {
net * (1 + rate)
}
priceWithTax(net: 100) // 120.0, Standardsatz
priceWithTax(net: 100, rate: 0.1) // 110.0, ermäßigter SatzDas wirst du in SwiftUI ständig sehen. VStack(spacing: 20) funktioniert genauso: spacing hat einen Standardwert, du kannst ihn angeben oder weglassen.
Jetzt mit guard
In der letzten Lektion hast du guard let kennengelernt. Jetzt kannst du es richtig einsetzen:
func double(_ text: String) -> Int? {
guard let number = Int(text) else {
return nil
}
return number * 2
}
print(double("21")) // Optional(42)
print(double("Hallo")) // nilDiese Funktion gibt ein Int? zurück, weil sie nicht immer ein Ergebnis hat. Genau so entstehen die Optionals, die du aus Int(...) und first kennst.
Lokal ist lokal
Variablen, die du in einer Funktion anlegst, existieren nur dort:
func calculate() {
let result = 42
}
calculate()
print(result)Fehlermeldung
Cannot find 'result' in scope
Das ist ein Vorteil: Jede Funktion ist ein abgeschlossener Bereich. Was darin passiert, beeinflusst nicht versehentlich den Rest deines Codes. Willst du ein Ergebnis nach außen geben, nutzt du return.
Typische Fehler
Label vergessen oder falsch
func greet(name: String) { ... }
greet("Anna")Fehlermeldung
Missing argument label 'name:' in call
Beim Aufruf muss das Label stehen: greet(name: "Anna"). Oder du definierst den Parameter mit _.
Rückgabewert nicht benutzt
priceWithTax(net: 20)Achtung
Result of call to 'priceWithTax(net:)' is unused
Die Funktion berechnet etwas, aber du machst nichts damit. Meist hast du vergessen, das Ergebnis zu speichern oder auszugeben.
Parameter ändern
func addOne(number: Int) {
number += 1
}Fehlermeldung
Left side of mutating operator isn't mutable: 'number' is a 'let' constant
Parameter sind innerhalb der Funktion Konstanten. Leg eine neue Variable an (var result = number + 1) und gib sie zurück.
Falscher Rückgabetyp
func half(_ number: Int) -> Int {
return Double(number) / 2
}Fehlermeldung
Cannot convert return expression of type 'Double' to return type 'Int'
Was du zurückgibst, muss zum Typ nach -> passen.
FAQ
Anfänger
Was bedeutet das überhaupt?Wann lohnt sich eine eigene Funktion?
Sobald du Code zum zweiten Mal schreibst, oder wenn ein Abschnitt eine klar benennbare Aufgabe erfüllt. Eine Funktion mit gutem Namen wie calculateShippingCost erklärt den Code besser als jeder Kommentar.
Wie lang darf eine Funktion sein? Es gibt keine feste Grenze. Als Faustregel: Passt sie nicht mehr auf den Bildschirm oder brauchst du das Wort „und“, um zu beschreiben, was sie tut, teil sie auf.
Wo muss die Funktion stehen, vor oder nach dem Aufruf? Im Playground muss sie vor dem Aufruf stehen, weil er von oben nach unten läuft. In normalen Projektdateien ist die Reihenfolge egal.
Fortgeschritten
Warum verhält es sich so?Kann eine Funktion mehrere Werte zurückgeben? Ja, mit einem Tupel:
func minMax(_ numbers: [Int]) -> (min: Int, max: Int)? {
guard let min = numbers.min(), let max = numbers.max() else {
return nil
}
return (min, max)
}
if let result = minMax([3, 9, 1]) {
print(result.min, result.max) // 1 9
}Für mehr als zwei oder drei Werte ist eine eigene Struct (Lektion 1.12) meist übersichtlicher.
Kann eine Funktion einen Parameter verändern, sodass es außen ankommt?
Ja, mit inout: func addOne(_ number: inout Int) { number += 1 }, aufgerufen mit addOne(&value). In App-Code brauchst du das selten. Ein Rückgabewert ist fast immer klarer.
Dürfen zwei Funktionen gleich heißen?
Ja, wenn sich ihre Parameter unterscheiden: greet(name:) und greet(name:times:) sind für Swift zwei verschiedene Funktionen. Das nennt man Überladen.
Profi
Was passiert unter der Haube?Wie lautet der „vollständige Name“ einer Funktion?
Swift bezeichnet Funktionen mit ihren Labels: greet(name:times:) oder square(_:). So steht es auch in Fehlermeldungen und in Apples Dokumentation. Wenn du dort insert(_:at:) liest, weißt du sofort: erster Parameter ohne Label, zweiter mit at:.
Was bedeutet @discardableResult?
Ein Attribut, das die Warnung „Result … is unused“ unterdrückt, wenn das Ergebnis einer Funktion nur gelegentlich interessant ist. removeLast() bei Arrays ist so markiert: Es gibt das entfernte Element zurück, aber du musst es nicht verwenden.
Übungen
Übung 1: Flächenrechner
Schreib eine Funktion area(width:height:), die die Fläche eines Rechtecks zurückgibt. Teste sie mit zwei verschiedenen Rechtecken.
Lösung Übung 1Erst selbst probieren!
func area(width: Double, height: Double) -> Double {
width * height
}
print(area(width: 4, height: 5)) // 20.0
print(area(width: 2.5, height: 3)) // 7.5Übung 2: Ist gerade?
Schreib eine Funktion isEven(_:), die für eine Zahl true oder false zurückgibt. Nutze sie dann in einer Schleife, um alle geraden Zahlen von 1 bis 20 auszugeben.
Lösung Übung 2Erst selbst probieren!
func isEven(_ number: Int) -> Bool {
number % 2 == 0
}
for number in 1...20 {
if isEven(number) {
print(number)
}
}if isEven(number) liest sich fast wie ein Satz. Genau das ist der Gewinn guter Funktionsnamen.
Übung 3: Passwort-Prüfer
Schreib eine Funktion passwordProblem(_:), die für ein Passwort nil zurückgibt, wenn es in Ordnung ist, und sonst einen Text mit dem ersten gefundenen Problem: kürzer als 8 Zeichen, keine Ziffer, oder enthält das Wort „passwort“ (egal ob groß oder klein geschrieben). Verwende guard.
Tipp: text.contains { $0.isNumber } prüft, ob eine Ziffer enthalten ist. Die Schreibweise mit $0 erklären wir in der nächsten Lektion.
Lösung Übung 3Erst selbst probieren!
func passwordProblem(_ password: String) -> String? {
guard password.count >= 8 else {
return "Mindestens 8 Zeichen nötig."
}
guard password.contains(where: { $0.isNumber }) else {
return "Mindestens eine Ziffer nötig."
}
guard !password.lowercased().contains("passwort") else {
return "Bitte nicht das Wort „passwort“ verwenden."
}
return nil
}
let checks = ["abc", "langespasswort", "Passwort123", "Sonnen4Blume"]
for check in checks {
print("\(check): \(passwordProblem(check) ?? "ok ✅")")
}Die Funktion kombiniert fast alles aus Stufe 1 bisher: Strings, Bedingungen, Optionals, guard und ??.
Zusammenfassung
func name() { ... }definiert eine Funktion,name()ruft sie auf.- Parameter übergeben Werte:
func greet(name: String). -> Typundreturngeben ein Ergebnis zurück. Bei einem einzigen Ausdruck darfreturnwegfallen.- Argument-Labels machen Aufrufe lesbar:
greet(to name: String)→greet(to: "Anna"). _lässt das Label weg:square(5).- Standardwerte machen Parameter optional beim Aufruf.
- Variablen in einer Funktion sind lokal.
Spickzettel
func sayHello() { ... }
func greet(name: String) { ... } // greet(name: "A")
func greet(to name: String) { ... } // greet(to: "A")
func square(_ n: Int) -> Int { n * n } // square(5)
func price(net: Double, rate: Double = 0.2) -> Double { ... }
func parse(_ s: String) -> Int? {
guard let n = Int(s) else { return nil }
return n
}