Eine praktische Einfuehrung in Task und Await
Dieser Beitrag stellt vor, wie man Code asynchron in Swift ausfuehrt. Zuerst gibt er einen allgemeinen Ueberblick ueber das Thema, dann demonstrieren zwei konkrete Code-Beispiele, wie man asynchronen Code in praktischen, realen Szenarien verwendet.
Die Code-Beispiele sind fuer Xcode Playgrounds geschrieben und koennen kopiert, eingefuegt und direkt ausgefuehrt werden.
Beide Anfragen zielen auf reqbin.com, ein Online-REST- und SOAP-API-Testtool.
Das erste Beispiel verwendet eine HTTP GET-Anfrage, die auch online ausprobiert werden kann auf ihrer Website.
Das zweite Beispiel verwendet eine HTTP POST-Anfrage mit einem JSON-Payload, gezeigt hier.
Der Beitrag behandelt auch grundlegende JSON-Kodierung und -Dekodierung.
Asynchrone Code-Ausfuehrung
Bevor wir zum Senden von HTTP-Anfragen kommen, schauen wir uns die asynchrone Code-Ausfuehrung an.
Die Swift-Dokumentation beschreibt “Asynchronen Code” wie folgt:
Asynchronous code can be suspended and resumed later, although only one piece of the program executes at a time. Suspending and resuming code in your program lets it continue to make progress on short-term operations like updating its UI while continuing to work on long-running operations like fetching data over the network or parsing files.
Um eine lang laufende Funktion asynchron zu machen, markiere sie mit dem async-Schluesselwort.
func myLongRunningFunction() async {
...
// do something that takes lots of time
...
}Um diese Funktion aufzurufen, muessen wir das await-Schluesselwort verwenden.
await myLongRunningFunction()Nehmen wir ein konkretes Beispiel von Task.sleep(). Im folgenden Code wollen wir 5 Sekunden warten.
print("Before")
try Task.sleep(nanoseconds: 5 * 1_000_000_000)
print("Done")
print("After")Dieser Code kann nicht kompiliert werden. Wir bekommen die Fehlermeldung: “‘async’ call in a function that does not support concurrency”.
Da der Haupt-Thread unseres Xcode Playgrounds synchron ist, muessen wir vorher async aufrufen.
print("Before")
async {
try await Task.sleep(nanoseconds: 5 * 1_000_000_000)
print("Done")
}
print("After")Waehrend dieser Code jetzt gut laeuft, ist es nur eine Frage der Zeit, bis er nicht mehr kompiliert.
Der Grund ist, dass async veraltet ist und durch Task.init ersetzt wird.
Lass uns das schnell aendern.
print("Before")
Task {
try await Task.sleep(nanoseconds: 5 * 1_000_000_000)
print("Done")
}
print("After")Das Ausfuehren davon wird folgende Ausgabe erzeugen.
Before
After
DoneWaehrend “Before” und “After” sofort gedruckt werden, dauert es 5 Sekunden, bis “Done” gedruckt wird.
Jetzt, da wir das grundlegende Bild der asynchronen Code-Ausfuehrung haben, schauen wir uns an, wie wir das zum Senden von HTTP-Anfragen verwenden koennen.
HTTP GET-Anfrage senden
Hier ist Beispielcode zum Senden einer HTTP GET-Anfrage.
Wir muessen drei Schritte ausfuehren:
- Die Anfrage mit der URL erstellen
- Die Anfrage senden
- Die Antwort auswerten
import PlaygroundSupport
import UIKit
struct SuccessInfo: Decodable {
let success: String
}
guard let url = URL(string: "https://reqbin.com/echo/get/json") else { fatalError("Missing URL") }
var urlRequest = URLRequest(url: url)
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
Task {
let (data, response) = try await URLSession.shared.data(for: urlRequest)
guard (response as? HTTPURLResponse)?.statusCode == 200 else { fatalError("Error while fetching data") }
let successInfo = try JSONDecoder().decode(SuccessInfo.self, from: data)
print(String(data: data, encoding:.utf8)?? "default value")
print("Success: \(successInfo.success)")
}
print("I am here already!")
PlaygroundPage.current.needsIndefiniteExecution = trueDie wichtigen Klassen sind:
“A value that identifies the location of a resource, such as an item on a remote server or the path to a local file.”
“A URL load request that is independent of protocol or URL scheme.”
“An object that coordinates a group of related, network data transfer tasks.”
“The metadata associated with the response to an HTTP protocol URL load request.”
HTTP POST-Anfrage senden
Dieser Abschnitt zeigt eine HTTP POST-Anfrage mit einem JSON-Payload. Wir fuehren immer noch die gleichen drei Schritte wie im ersten Beispiel durch; der Hauptunterschied hier ist, dass wir Daten an den Server senden.
Unsere Daten sind in MyJsonObject kodiert. Du kannst jeden beliebigen Payload verwenden - dieses Backend ignoriert den Inhalt.
Die meisten REST-Services antworten mit demselben Ressourcentyp, also haetten wir MyJsonObject als einzelnen Typ verwenden und ihn Codable konform machen koennen.
import PlaygroundSupport
import UIKit
struct MyJsonObject: Encodable {
let id: Int
let foo: String
let bar: String
}
struct SuccessInfo: Decodable {
let success: String
}
let myJsonObject = MyJsonObject(id: 1, foo: "Hello", bar: "World")
let payload = try JSONEncoder().encode(myJsonObject)
guard let url = URL(string: "https://reqbin.com/echo/post/json") else { fatalError("Missing URL") }
var urlRequest = URLRequest(url: url)
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
urlRequest.httpMethod = "POST"
Task {
let (data, response) = try await URLSession.shared.upload(for: urlRequest, from: payload)
guard (response as? HTTPURLResponse)?.statusCode == 200 else { fatalError("Error while fetching data") }
let successInfo = try JSONDecoder().decode(SuccessInfo.self, from: data)
print(String(data: data, encoding:.utf8)?? "default value")
print("Success: \(successInfo.success)")
}
print("I am here already!")
PlaygroundPage.current.needsIndefiniteExecution = trueSchauen wir uns die Unterschiede zwischen Encodable, Decodable und Codable an.
“An object that decodes instances of a data type from JSON objects.”
“A type that can encode itself to an external representation.”
“A type that can decode itself from an external representation.”
“A type that can convert itself into and out of an external representation.”
Code ausfuehren
Beim Ausfuehren des Beispielcodes koenntest du sehen, dass die Zeile “I am here already!” vor dem HTTP-Antwortkoerper gedruckt wird.
Fazit
Nach einer kurzen Einfuehrung in das Schreiben von asynchronem Swift-Code mit async und await demonstrieren zwei praktische Beispiele, wie man Daten asynchron ueber HTTP GET- und POST-Anfragen sendet und empfaengt.
Kopiere und fuege den Code gerne in einen Xcode Playground ein und experimentiere.
Danke fuers Lesen!