Callbacks sind in funktionalen Sprachen unabdingbar. foo bar, bla bla (Einleitung gekürzt)

Anyway heute geht es um Callbacks in Swift. Genauer gesagt um optionale Callbacks. Optionale Callbacks sind diejenigen Callbacks, welche nicht zwingend übergeben werden müssen.

fn processor ( callback ) { 
    if callback {
        callback()
    } else {
        return;
    }

nun kann prosessor() aufgerufen werden, als auch processor( fn done(){} ); Eben optional.

In Swift haben wir einige grazile Sprachelemente bekommen. Optionale Callbacks lassen sich hervorragend umsetzen!

Näherungsweise: Die Processor Funktion mit Aufruf  kann zunächst so aussehen. Sie hat jedoch noch keine Callback funktionalität. Ihr merkt, ich räume gerade etwas Code auf:

func processor(text: String){
    println("i am the processor and should say \(text)")
}
processor("hello to you")

Der Output ist erwartungsgemäß: „i am the processor and should say hello to you“

Wen nun diesem konstruierten Beispiel eine Callback funktion gegeben werden soll, könnte das so aussehen

func processor(text: String, completion: ()->Void) {
    println("i am the processor and should say \(text)")
    completion()
}
processor("hello to you") {
    println("text printed")
}

Die Signatur für eine leere Callbackmethode ()->Void wird als Type dem Parameter completion gegeben.
Dem Aufruf wird eine Funktion angehängt. In diesem Fall wird einfach nur eine Statusnachricht ausgeben.

Erwartungsgemäß ist das Ergebnis:
i am the processor and should say hello to you
text printed

So weit so gut. processor(_, _) muss ich aber nun mit zwei Parametern aufrufen. text und completion gehören zur Funktionssignatur. In vielen Fällen möchte ich jedoch optionale Callbacks, insbesondere wenn es um UI Geschichten geht. „Tue etwas, und wenn das Ok ist, dann schließe das Fenster“ – ist ein häufiger Anwendungsfall. Das „Tue etwas“ wird aber in diversen Kontexten aufgerufen, bzw. delegiert. Weshalb aus architektonischer Sicht das Schließen zu dem aufrufenden Part gehört, also in ein Callback muss. Das „Delegat“ soll zum Besipiel Daten speichern, aber nicht das Fenster beeinflussen. Das Fenster selber weiß jedoch, wann es eine Aktion (z.B. das speichern) ausführen lassen muss und in folge dessen wie es sich verhalten soll.

Hier kommen Optionale in Swift in in Spiel. Den completion Parameter kann man als Optionale angeben, heisst er kann entweder eine Funktion sein, oder nil.

(()->Void)?

und completion kann darauf hin auf ein Unwrapping geprüft werden. Die ganze Funktion sieht dann so aus:

func processor(text: String, completion: (()->Void)?) {
    println("i am the processor and should say \(text)")
    if let callback = completion{
        callback()
    }
}

Der Aufruf des Processors funktioniert nach wie vor, aber wirklich optional ist er nicht, da immer noch ein zweiter Parameter verlangt wird. Dieser kann nun jedoch nil sein:

processor("hello to you") {
    println("text printed")
}
processor("hello to me", nil)

Das ist noch nicht das was ich will! Mein erster Gedanke war eine convinience Methode zu schreiben, die processor(_, _) überlagert und nur text als Parameter verlangt:

func processor(text: String){
    processor(text, nil)
}
func processor(text: String, completion: (()->Void)?) {
    println("i am the processor and should say \(text)")
    if let callback = completion{
        callback()
    }
}

…, denn nun kann ich optional processor mit und ohne Callback aufrufen:

processor("hello to you") {
  println("text printed");
}
processor("hello to me")

Swift ist aber wie erwähnt eleganter. Es bieten sich (ähnlich wie in PHP, haha 🙂 ) Defaults an. Wenn der completion Parameter durch die Convinience-Methode zu einem nil wird, dann kann completion doch auch gleich nil als default bekommen.

func processor(text: String, completion: (()->Void)? = nil) {
    println("i am the processor and should say \(text)")
    if let callback = completion{
        callback()
    }
}
processor("hello to you") {
    println("text printed");
}
processor("hello to me")

Somit sind optionale Callbacks in Swift umgesetzt ohne den Weg über überlagerte Funktionen zu gehen! Nice! Ich fange mehr und mehr an, die Sprache richtig doll zu mögen!