Alfred Workflows in Swift

Apple hat eine neue Sprache zur Ergänzung erstellt Ziel c: Schnell. Schnell ist immer noch in der C-Sprachklasse, verfügt jedoch über einige Funktionen einer interpretierten Sprache. Die einfachere Syntax im Vergleich zu Ziel c macht das Lernen und die Benutzung viel einfacher. Schnell ist als Interpreter oder Compiler verwendbar.

Ich stelle Ihnen diese Sprache vor, indem Sie ihnen zeigen, wie sie mit Alfred Workflows verwendet wird, und ich gehe davon aus, dass Sie mit dem Schreiben von Workflows in Alfred bereits vertraut sind. Wenn nicht, schauen Sie sich diese Tutorials an: Alfred zum Anfänger, Zwischenprodukte, Erweitert, und Alfred debuggen.

Dieses Tutorial konzentriert sich auf die Verwendung Schnell mit Alfred und keine vollständige Sprachreferenz. Es gibt eine großartige Tutorialserie zum Lernen Schnell auf Code Tuts+, Eine Einführung in Swift: Teil 1 und Eine Einführung in Swift: Teil 2. Ich gehe davon aus, dass Sie diese Artikel bereits gelesen haben.

Xcode installieren 6.1

Installieren Schnell Auf einem Mac müssen Sie XCode aus dem App Store installieren. Nach der Installation müssen Sie ein Terminalfenster öffnen und Folgendes ausführen:

xcode-select -s /Applications/Xcode.app/ --install

Dadurch werden alle XCode-Tools für die Befehlszeile verfügbar.

Swift zur Shell hinzufügen

Sie können kompilieren und damit experimentieren Schnell im XCode 6.1 und sein Spielplatz. Dieses Tutorial konzentriert sich auf die Verwendung Schnell in der Befehlszeile nur mit einem Programmiereditor und der Befehlszeile.

Vorausgesetzt, Sie haben installiert XCode 6.1 zum Anwendungen Ordner müssen Sie hinzufügen SchnellAusführbares Verzeichnis des Pfads. Zum Bash, füge dies zu deinem hinzu ~ / .bashrc Datei:

export PATH = "$ PATH: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin" 

Zum Zsh, füge dieselbe Zeile zu deinem hinzu ~ / .zshrc Datei. Zum Fisch Shell, füge diese Zeile hinzu:

set -xg PATH "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin" $ PATH 

Ich habe das mit der gefunden xcrun Befehl ohne den PATH wie oben eingestellt zu haben, funktioniert nicht immer.

Kompilieren Schnell Dateien in der Befehlszeile geben Sie ein:

swiftc 

Zu rennen Schnell In einem interaktiven Interpreter geben Sie Folgendes ein:

schnell

Swift zu einem Editor hinzufügen

Hinzufügen Schnell Unterstützung für Erhabener Text ist einfach. Installieren Sie Package Manager für Sublime Text. Nachdem Sie Package Manager installiert haben, müssen Sie das Paket installieren Schnell durch die Nutzung Umschalt-Befehl-P, Art Paket installieren, eingeben, und Schnell. Dies sollte das installieren Schnell Paket.

Auf Emacs, der Paketmanager für Emacs ist die beste Route, aber es ist nur in Emacs 24+. Zum Emacs 24+, Sie müssen herunterladen und installieren Aquamacs. Das Emacs Auf einem Standard-Mac ist Version 22. installiert Aquamacs mit HomeBrew und HomeBrew Cask, Geben Sie dies in ein Terminal ein:

braufaß installieren aquamacs 

Einmal installiert, müssen Sie dies Ihrem hinzufügen ~ / .emacs Datei:

(erfordern 'package') (package-initialize) (Add-to-List 'package-archives' ("melpa". "http://melpa.milkbox.net/packages/")) 

Lauf Aquamacs und öffnen Sie die Emacs Package Manager mit Meta-x-Paketlistenpakete.

Emacs Package Manager

Suchen Swift-Modus und klicken Sie darauf. Sie müssen möglicherweise installieren Flycheck auch.

Zum Vim, Sie müssen installieren Swift.Vim. Der einfachste Weg zur Installation Swift.Vim ist zu installieren maximal-super zuerst. Installieren maximal-super, Öffnen Sie ein Terminal in der ~ / Dokumente (oder ein anderes Verzeichnis. Es muss sich um ein Verzeichnis handeln, in dem Sie behalten möchten.). Geben Sie Folgendes ein:

git clone https://github.com/square/maximum-awesome.git cd Maximum-Awesome Rake 

Die Installation dauert eine Weile. Anschließend fügen Sie diese Zeile zu Ihrem hinzu ~ / .vimrc.bundle.local Datei:

Plugin 'Keithbsmiley / swift.vim' 

Einmal gespeichert, wiederholen Vim und Typ

: PluginInsall

Vundle, welche maximal-super installiert, installiert alle neuen Plugins.

Vundler installiert Swift.Vim

In diesem Fall, Vundler installiert die Swift.Vim Plugin. Vim wird jetzt Syntax-Highlighting für Schnell.

Schnell interpretiert vs kompiliert

Schnell kann als Interpreter oder Compiler laufen. Um dies zu veranschaulichen, erstellen Sie eine Datei hallo und fügen Sie dies hinzu:

#! / usr / bin / env xcrun -sdk macosx swift println ("Hallo Welt!") 

Dies ist das grundlegendste Hallo Welt Programm, das die Menschen als erstes Programm verwenden. Die erste Zeile teilt der Shell mit, dass Sie dieses Skript ausführen möchten xcrun mit der angegebenen Befehlszeile. xcrun ist ein Mac OSX-Programm zum Ausführen von XCode-Dienstprogrammen über die Befehlszeile.

Speichern Sie die Datei und ändern Sie ihren Ausführungsmodus mit

chmod x + a hallo 

Führen Sie die Datei mit aus

./hello.swift 

Und es wird produzieren Hallo Welt! auf dem Terminal durch Ausführen des Skripts mit Schnell als Dolmetscher Geben Sie zum Kompilieren dies auf dem Terminal ein:

swiftc hallo 

EIN Hallo Programm ist jetzt in diesem Verzeichnis vorhanden. Art

./Hallo 

und Sie werden genau dieselbe Ausgabe sehen.

Hallo Weltprogramm

Beide Methoden führten zum exakt gleichen Ergebnis. Der einzige Unterschied ist der für den interpretierten Ansatz, XCode 6 muss auf jedem System sein, auf dem es läuft. Wenn Sie das Programm kompilieren, kann es auf jedem Mac ausgeführt werden.

Eine interessante Anmerkung zum interpretierten Ansatz ist, dass er mit dem kompilierten Ansatz für das, was ausgeführt wird, identisch ist. Im interpretierten Modus kompiliert er das Programm im Speicher und läuft direkt im Speicher. Der kompilierte Ansatz wird auf die Festplatte kompiliert und Sie führen das Programm aus. Insgesamt sparen Sie keine Zeit, wenn Sie Swift-Code interpretieren.

Alfred Bibliothek

Schreibprogramme zur Verwendung in Alfred, Eine Bibliothek mit Hilfsfunktionen kann von großem Nutzen sein. Erstellen Sie eine neue Datei mit dem Namen Alfred.Schalter und geben Sie diesen Code ein:

// // Klasse: Alfred // // Beschreibung: Diese Klasse dient zur Unterstützung bei der Erstellung von Workflows für Alfred mithilfe von // Apples Swift-Sprache. // // Klassenvariablen: // // Name Beschreibung // // Cachepfad zu dem Verzeichnis, das den Cache für den Arbeitsablauf enthält // Datenpfad zu dem Verzeichnis, das die Daten für den Arbeitsablauf enthält // bundleId Die ID für die Paket, das den Pfad des Arbeitsablaufs // zum Pfad des Arbeitsablaufs darstellt, // Home-Pfad zum Heimatverzeichnis des Benutzers, // die gesammelten Ergebnisse. Dies wird in die XML-Liste für // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // H // h des US-Bundes der Vereinigten Staaten von Amerika importiert. // importiere Foundation // // verwendete Strukturen definieren. // struct AlfredResult var Uid: String = "" var Arg: String = "" var Titel: String = "" var Sub: String = "" var Symbol: String = "" var Gültig: String = "" var Auto: String = "" var Rtype: String = "" // // Klasse: Regex // // Beschreibung: Dies ist eine Hilfsklasse zum Schreiben von Tests mit regulären Ausdrücken. Basierend auf // auf Artikel: http://benscheirman.com/2014/06/regex-in-swift/ // class Regex let internalExpression: NSRegularExpression let pattern: String init (_ pattern: String) self.pattern = pattern var error: NSError? self.internalExpression = NSRegularExpression (Muster: Muster, Optionen: .CaseInsensitive, Fehler: & Fehler)!  func test (Eingabe: Zeichenfolge) -> Bool let Matches = self.internalExpression.matchesInString (Eingabe, Optionen: nil, Bereich: NSMakeRange (0, count (Eingabe))) Rückgabewerkzeuge.count> 0 // / / Class: Alfred // // Beschreibung: Diese Klasse enthält die Funktionen, die zum Schreiben von Workflows für Alfred erforderlich sind. // public class Alfred var cache: String = "" var data: String = "" var bundleId: String = "" var path: String = "" var home: String = "" var fileMGR: NSFileManager = NSFileManager () var maxResults: Int = 10 var currentResult: Int = 0 var resultiert: [AlfredResult] = [] // // Bibliotheksklasse Funktion: // // init Diese Klassenfunktion wird aufgerufen, wenn die Bibliothek verwendet wird, um // alle für das System verwendeten Variablen zu initialisieren Bibliothek, bevor // jemand eine Bibliotheksklassenfunktion aufrufen kann. // public init () // // Das Ergebnisfeld erstellen. // var resfirst: AlfredResult = AlfredResult () resfirst.Title = "Keine Übereinstimmungen gefunden…" resfirst.Uid = "default" resfirst.Valid = "no" resfirst.Arg = "" resfirst.Sub = "" resfirst.Icon = "" resfirst.Auto = "" resfirst.Rtype = "" results.append (resfirst) maxResults = 10 currentResult = 0 // // Legt die Pfad- und Ausgangsvariablen aus der Umgebung fest. // in Objective C: NSString * path = [[[[[NSProcessInfo processInfo] Umgebung] objectForKey: @ "PATH"]; // let process = NSProcessInfo.processInfo (); let edict = NSDictionary (Wörterbuch: process.environment) path = fileMGR.currentDirectoryPath home = edict ["HOME"] als! String // // Wenn die Datei info.plist vorhanden ist, lesen Sie sie für das Bundleid und legen Sie die Variable bundleId fest. // bundleId = GetBundleId () // // Erstellen Sie die Verzeichnisstruktur für die Cache- und Datenverzeichnisse. // cache = home + "/Library/Caches/com.runningwithcrayons.Alfred-2/Workflow Data /" + bundleId; data = home + "/ Library / Anwendungsunterstützung / Alfred 2 / Workflow Data /" + bundleId; // // Prüfen Sie, ob das Cache-Verzeichnis existiert. // if (! fileMGR.fileExistsAtPath (cache)) // // Es existiert nicht. Erstelle es! // fileMGR.createDirectoryAtPath (cache, withIntermediateDirectories: true, Attribute: nil, Fehler: nil) // // Prüfen Sie, ob das Datenverzeichnis vorhanden ist. // if (! fileMGR.fileExistsAtPath (data)) // // Es existiert nicht. Erstelle es! // fileMGR.createDirectoryAtPath (data, withIntermediateDirectories: true, Attribute: nil, Fehler: nil) // // class Funktion: GetBundleId // // Beschreibung: Diese Klassenfunktion liest die Workflows info.plist und gibt // zurück. das Bundleid // public func GetBundleId () -> String // // holt die Bundle-ID aus der Liste, falls sie noch nicht abgerufen wurde. // if (bundleId == "") let path = NSBundle.mainBundle (). pathForResource ("info", ofType: "plist") let dict = NSDictionary (contentsOfFile: path!)! bundleId = dict ["bundleid"] als! String // // Gibt die Bundle-ID zurück. // return (bundleId) // // class Funktion: Cache // // Beschreibung: Diese Klassenfunktion gibt das Cache-Verzeichnis für den Arbeitsablauf zurück. // public func Cache () -> String return (cache) // // class Funktion: Daten // // Beschreibung: Diese Klassenfunktion gibt das Datenverzeichnis für den Workflow zurück. // public func Data () -> String return (data) // // class Funktion: Pfad // // Beschreibung: Diese Klassenfunktion gibt den Pfad zum Workflow zurück. // public func Path () -> String return (path) // // class Funktion: Home // // Beschreibung: Diese Klassenfunktion gibt das Home-Verzeichnis für den Benutzer zurück. // public func Home () -> String return (home) // // class Funktion: ToXML // // Beschreibung: Diese Klassenfunktion nimmt das Ergebnis-Array und macht es in einen // XML-String, den Sie zurückgeben können Alfred // public func ToXML () -> String var newxml: String = ""für Ergebnis in Ergebnissen newxml + ="\ (result.Arg)\ (Ergebnis.Titel)\ (result.Sub)\ (result.Icon)" // // Schließen Sie die XML-Datei und geben Sie den XML-String zurück. // newxml + =""return (newxml) // // class Funktion: AddResult // // Beschreibung: Helper-Klassenfunktion, die es nur einfacher macht, // Werte in eine Klassenfunktion // zu übergeben und ein Array-Ergebnis zu erstellen, das an Alfred zurückgegeben wird // // Eingaben: // Die UID der UID des Ergebnisses sollte eindeutig sein // Argument das Argument, an das übergeben wird // Titel Der Titel des Ergebniselements // Sub Der Untertiteltext für das Ergebniselement // icon das für das Ergebniselement zu verwendende Symbol // gültig legt fest, ob das Ergebniselement ausgeführt werden kann // Der AutoComplete-Wert für das Ergebniselement automatisch festgelegt wird // rtype Ich habe keine Ahnung, wofür dieses Element verwendet wird. HELP! // public func AddResult (uid: String, arg: String, Titel: String, Sub: String, Symbol: String, gültig: String, auto: String, rtype: String) // // Fügen Sie das neue Ergebnis-Array hinzu, falls es nicht voll ist. / / if (currentResult < maxResults)  if(currentResult != 0)  var resfirst:AlfredResult = AlfredResult() resfirst.Title = title resfirst.Uid = uid resfirst.Valid = valid resfirst.Arg = arg resfirst.Sub = sub resfirst.Icon = icon resfirst.Auto = auto resfirst.Rtype = rtype results.append(resfirst)  else  results[0].Title = title results[0].Uid = uid results[0].Valid = valid results[0].Arg = arg results[0].Sub = sub results[0].Icon = icon results[0].Auto = auto results[0].Rtype = rtype  currentResult++   // // class Function: AddResultsSimilar // // Description: This class function will only add the results that are similar to the // input given. This is used to select input selectively from what the // user types in. // // Inputs: // inString the String to test against the titles to allow that record or not // uid the uid of the result, should be unique // arg the argument that will be passed on // title The title of the result item // sub The subtitle text for the result item // icon the icon to use for the result item // valid sets whether the result item can be actioned // auto the autocomplete value for the result item // rtype I have no idea what this one is used for. HELP! // public func AddResultsSimilar(uid: String, inString: String, arg: String, title: String, sub: String, icon: String, valid: String, auto: String, rtype: String)  // // Create the test pattern. // var matchstr = inString + ".*" // // Compare the match String to the title for the Alfred output. // if(Regex(inString + ".*").test(title))  // // A match, add it to the results. // AddResult( uid, arg: arg, title: title, sub: sub, icon: icon, valid: valid, auto: auto, rtype: rtype)   // // class Function: SetDefaultString // // Description: This class function sets a different default title // // Inputs: // title the title to use // public func SetDefaultString(title: String)  if(currentResult == 0)  // // Add only if no results have been added. // results[0].Title = title    

Dies ist ein Port meiner Alfred Go-Sprachbibliothek, den ich im Tutorial Track Project Time With Alfred Timekeeper verwende Schnell. Ich definiere eine Struktur, AlfredResult, Artikel behalten, um zu einem zurückzukehren Skriptfilter im Alfred, eine Klasse für die Verwendung der NSRegularExpression Ziel c Klasse und die Alfred Klasse für den Umgang mit allem, was ein Alfred Workflow würde brauchen.

An diesem Punkt haben Sie bemerkt, dass ich eine erwähnt habe Ziel c Klasse. Schnell entfernt nicht die Notwendigkeit zu verwenden Ziel c Bibliotheken. Das Schnell Die Standardbibliothek enthält grundlegende Sprachkonstrukte und -routinen. Nichts, das betriebssystemspezifisch ist. Alles Ziel c Bibliotheken können direkt von verwendet werden Schnell. Also, werfen Sie nicht alle aus Ziel c Referenzhandbücher. Sie werden immer noch gebraucht.

Das Alfred Die Klasse verfügt über eine Initialisierungsroutine, die die verschiedenen Verzeichnispfade abruft, die ein Workflow verwenden würde, und die Daten- und Cache-Verzeichnisse erstellt, wenn sie noch nicht vorhanden sind. Die Methoden AddResult () und AddResultsSimilar () Erstellen Sie die Ergebnisstruktur für die XML-Ausgaben an Alfred. Das AddResultsSimilar () fügt das Ergebnis nur hinzu, wenn es der Titelzeile ähnelt. Das ToXML () Funktion gibt das XML des Ergebnis-Arrays aus.

Der Workflow

Der Beispielworkflow ist ein Fallkonverter. Ich habe es einmal in PHP erstellt, aber für viele Systeme läuft es sehr langsam. Es wird eine Zeichenfolge auf der Alfred Eingabeaufforderung oder aus der OSX-Auswahl mit einem Hotkey und geben Sie eine Liste der verfügbaren Konvertierungen an. Es zeigt die resultierende Konvertierung während der Eingabe. Die vom Benutzer ausgewählte Konvertierung wird in die Zwischenablage und in die oberste Anwendung gesendet.

Alfred Case Converter-Arbeitsablauf

Erstellen Sie einen Alfred-Workflow wie oben.

Workflow-Skriptfilter

Stellen Sie das ein ScriptFilter wie du oben siehst. Wähle aus Workflow-Ordner öffnen in der rechten unteren Ecke. EIN Finder Es öffnet sich ein Fenster zum Verzeichnis für den Workflow. Kopiere das Alfred Bibliothek Datei, die zuvor in diesem Verzeichnis erstellt wurde. Dann erstellen Sie eine neue Datei mit dem Namen tcconverter.swift und fügen Sie diesen Code am Ende der Datei ein:

// // Programm: Text Case Converter (tcconverter) // // Beschreibung: Dieses Programm nimmt einen String in die Befehlszeile, konvertiert // auf verschiedene Arten und erstellt eine XML-Ausgabe, um // in einem Alfred-Skript anzuzeigen Filter. // // // Importieren von Bibliotheken: // Importieren von Alfred Import Foundation Class Converter init () // // Erstellen Sie das Alfred-Objekt. // var wf = Alfred () // // Liefert die Kommandozeilenzeichenfolge und initialisiert die Zählung. // let str: String = Process.arguments [1] var count = 0 // // Zu einem Capitalized String. // var result = str.capitalizedString wf.AddResult ("CS \ (count)", Argument: Ergebnis, Titel: "Großschreibung: \ (Ergebnis)"), Unter: "Text Case Converter", Symbol: "icon.png" , gültig: "yes", auto: "", rtype: "") count ++ // // Erstelle einen String in Kleinbuchstaben. // result = str.lowercaseString wf.AddResult ("CS \ (count)", Argument: Ergebnis, Titel: "Kleinbuchstaben: \ (Ergebnis)"), Sub: "Text Case Converter", Symbol: "icon.png", gültig: "yes", auto: "", rtype: "") count ++ // // Erzeuge einen String in Großbuchstaben. // result = str.uppercaseString wf.AddResult ("CS \ (count)", Argument: Ergebnis, Titel: "Großbuchstaben: \ (Ergebnis)"), Sub: "Text Case Converter", Symbol: "icon.png", gültig: "yes", auto: "", rtype: "") count ++ // // Erzeuge einen Titlecase-String. // result = TitleCase (str) wf.AddResult ("CS \ (count)", arg: result, title: "Titlecase: \ (result)"), sub: "Text Case Converter", Symbol: "icon.png" , gültig: "yes", auto: "", rtype: "") count ++ // // XML ausgeben. // println (wf.ToXML ()) // // Methode: TitleCase // // Beschreibung: Diese Methode konvertiert die angegebene Zeichenfolge in den Titelkoffer. // // Eingabe: // str Die zu konvertierende Zeichenfolge. // func TitleCase (str: String) -> String // // Erstellen Sie ein Array von Wörtern, um Kleinbuchstaben zu erstellen. // lass lower = ["to", "an", "und", "at", "as", "aber", "by", "for", "if", "in", "on", "oder", "ist", "mit", "ein", "das", "von", "vs", "vs.", "via", "via", "de"] // // Create ein Array von Wörtern, die groß geschrieben werden sollen. // let upper = ["I", II "," III "," IV "," V "," VI "," VII "," VIII "," IX "," X "," HTML ", "CSS", "AT & T", "PHP"] // // Den String nach Leerzeichen aufteilen. // var words = str.componentsSeparatedByString ("") // // Hilfsvariablen initialisieren. // var result = "" var first = true // // Durch jedes Wort gehen. // für ein Wort in Worten // // Erstellen Sie einen Kleinbuchstaben des Wortes und das Ergebniswort in Großschreibung. // var lword = word.lowercaseString var res = word.capitalizedString // // Durchlaufen Sie jedes Wort, das Kleinbuchstaben sein soll. // für low in lower if lword == low.lowercaseString // // Es sollte Kleinbuchstaben sein. Setzen Sie das Ergebniswort darauf und brechen Sie // aus der Schleife. // res = low break // // Durchlaufen Sie jedes Wort, das groß geschrieben werden soll. // für up in upper if lword == up.lowercaseString // // Es sollte Großbuchstaben sein. Setze das Ergebniswort und brich aus. // res = up break // // Wenn es das erste Wort ist, dann immer groß schreiben. // if first res = res.capitalizedString first = false // // Erzeuge die resultierende Zeichenfolge. // result + = "\ (res)" // // Liefert das Ergebnis. // return (Ergebnis) cv: Converter = Converter () 

Dieser Code definiert die Konverter Objekt. Dieses Objekt erstellt die verschiedenen Versionen der Zeichenfolge und weist sie der XML-Ergebnisliste zu. Sobald er vollständig erstellt wurde, sendet er das XML an die Ausgabe.

Das Konverter Objekt hat die TitleCase () Methode zum Erstellen einer Titelversion der Zeichenfolge. Es vergleicht jedes Wort in der Zeichenfolge mit dem Kleinbuchstabenfeld und dem Großbuchstabenfeld. Wenn in diesen gefunden, wird das Wort auf diesen Wert gesetzt. Es stellt immer sicher, dass das erste Wort groß geschrieben wird.

Das Kompilieren einer Bibliothek zu einer Anwendung ist etwas komplex. Führen Sie in dem Programm Terminal.app oder iTerm.app diese Befehlszeilen in dem Verzeichnis aus, das die Quellen enthält.

  1. xcrun swiftc -emit-library -emit-object Alfred.swift -sdk $ (xcrun --show-sdk-path --sdk macosx) - Modulname Alfred
  2. ar rcs libAlfred.a Alfred.o
  3. xcrun swiftc -emit-module Alfred.swift -sdk $ (xcrun --show-sdk-path --sdk macosx) - Modulname Alfred
  4. xcrun swiftc -o tcconverter -I "./" -L "./" -lAlfred -sdk $ (xcrun --show-sdk-path --sdk macosx) tcconverter.swift

Nachdem alle vier Schritte ausgeführt wurden, befindet sich das Programm tcconverter in diesem Verzeichnis. Wenn Sie den Code tcconverter.swift ändern, müssen Sie nur den letzten Befehl erneut ausführen. Wenn Sie den Alfred.swift-Code ändern, müssen Sie alle vier Zeilen erneut ausführen.

Das Programm Tcconverter sollte sich jetzt im Workflow-Verzeichnis befinden. Sie können es im testen Alfred Prompt:

Testen des Codes

Die Download-Datei enthält alle diese Dateien und den Workflow, den Sie prüfen können.

Fazit

In diesem Tutorial habe ich Ihnen gezeigt, wie Sie installieren Schnell Konfigurieren Sie auf dem Mac drei verschiedene Programmeditoren und Shells Schnell Programme in einem Terminal und verwenden Schnell bauen Alfred Arbeitsabläufe

Das ist eine Menge Wissen, das Sie üben müssen, um sich daran zu erinnern. Also, mach was tolles Alfred Arbeitsabläufe im Schnell und teile sie mit allen!