Xcode11 OSX Swift URLSession Datenrückgabe

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Xcode11 OSX Swift URLSession Datenrückgabe

    Hallo,

    wie kann ich aus einer neuen Session, welche ich via URLSession.shared.dataTask() erzeugt habe, Daten zurückgeben?

    Mit einem "einfachen" return (String) funktioniert dies bei mir nicht und wenn ich den completionHandler verwende wird mir auch kein Ergebnis zurückgegeben.

    Anbei mal ein kleines Beispiel mit dem ich die Datenrückgabe ausprobiere:

    Quellcode

    1. func cRequest(completionHandler: @escaping (String) -> Void) -> Void
    2. {
    3. let location = "https://httpbin.org/json"
    4. let requestURL = URL(string: location)
    5. let request = URLRequest(url: requestURL!)
    6. let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
    7. guard error == nil && data != nil else{
    8. print("error=\(String(describing: error))")
    9. completionHandler("ERROR")
    10. return}
    11. completionHandler("OKAY")}
    12. task.resume()}
    Alles anzeigen
    Bin für jeden Hinweis dankbar, auch weil mir der Debugger in wirklich weiterhilft.
  • MCDan schrieb:

    Ups, der Completion Handler braucht ja auch keinen Rückgabewert, sondern nur Parameter.

    Somit scheint der erste Code ok zu sein.

    Was kommt denn in dem Completion Handler an?

    Hmmm, irgendwie bin ich hier glaube ich auf dem Holzweg. Mit task = URLSession.shared.dataTask(with: request) erstelle ich doch einen neuen Task der unabhängig vom MainTask abgearbeitet wird.
    Dies wird doch empfohlen wenn Daten via URL von einem Server abgeholt werden, damit der MainTask nicht hängen bleibt. Soweit dachte ich, hätte ich die Materie verstanden.
    Doch wie bekomme ich nun die Daten zurück in den MainTask? Eine Rückgabe via RETURN funktioniert nicht. Ich dachte dann verwende ich einfach die Parameter des completionHandlers.

    So und nun stellst Du mir die Frage was im CompletionHandler ankommt? :/ Der enthält keinen Wert. Der soll mir doch Daten vom Server zurückgeben. Ich habe dies mal vereinfacht und befülle diesen im Erfolgsfall - also wenn Daten vom Server empfangen wurden - mit dem String "OKAY". Ich dachte nun, dass mir dieser String im MainTask zur Verfügung gestellt wird. Aber das funktioniert nicht - ich bin hier am Verzweifeln ;( .

    Kann mir hier jemand mal kurz die Schritte hierfür mitteilen oder mir konkret mitteilen was ich in obigem Code nicht berücksichtige?


    Danke mal vorab

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von OSXDev ()

  • MCDan schrieb:

    Wie sieht denn der Aufruf von cRequest() aus?

    Und warum ist der Parameter completionHandler von cRequest als @escaping (String) definiert?
    Weil ich in meiner Literatur hierfür kein wirklich gutes Beispiel gefunden habe. D.h. ich habe es einfach so übernommen um überhaupt den Ablauf mal nachvollziehen zu können. Doch ich erhalte weder Compilerfehler noch sonst ein Ergebnis. ;(

    Den Aufruf habe ich so realisiert: print("Ergebnis: ", cRequest(completionHandler: { (sSprichMitMir) in }))
  • Die Funktion cRequest(completionHandler:) schafft hier ja eine weitere Verschachtelung von Completion-Handlern. Die URLSession-Methode dataTask(with:completionHandler:) bekommt ja schon einen Completion-Handler, in dem dann der Completion-Handler der cRequest-Funktion aufgerufen wird. Hier mal eine cRequest-Funktionsvariante, die diese Verschachtelung auflöst:

    Quellcode

    1. import Foundation
    2. func cRequest(completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> Void
    3. {
    4. let location = "https://httpbin.org/json"
    5. let requestURL = URL(string: location)
    6. let request = URLRequest(url: requestURL!)
    7. let task = URLSession.shared.dataTask(with: request, completionHandler: completionHandler)
    8. task.resume()
    9. }
    10. cRequest { (data: Data?, response: URLResponse?, error: Error?) in
    11. guard error == nil else {
    12. print("error=\(String(describing: error))")
    13. return
    14. }
    15. guard let receivedData = data else {
    16. print("Keine Daten erhalten")
    17. return
    18. }
    19. let receivedDataAsString = String(data: receivedData, encoding: .utf8) ?? ""
    20. print(receivedDataAsString)
    21. }
    Alles anzeigen
  • Tecalto schrieb:

    So müsste es gehen:

    Quellcode

    1. class Cl {
    2. func cRequest(completionHandler: @escaping (String) -> Void) -> Void {
    3. completionHandler("Hallo")
    4. }
    5. }
    6. Cl().cRequest(completionHandler: { (sSprichMitMir) in print("Ergebnis:", sSprichMitMir) })
    Danke. Es funktioniert - nur ist es nicht ganz, dass was ich benötige. Nachdem die Daten von der Webseite erfolgreich abgerufen wurden, benötige ich diese für die weitere Verarbeitung im Main Thread.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von OSXDev ()

  • Michael schrieb:

    Die Funktion cRequest(completionHandler:) schafft hier ja eine weitere Verschachtelung von Completion-Handlern. Die URLSession-Methode dataTask(with:completionHandler:) bekommt ja schon einen Completion-Handler, in dem dann der Completion-Handler der cRequest-Funktion aufgerufen wird. Hier mal eine cRequest-Funktionsvariante, die diese Verschachtelung auflöst:

    Quellcode

    1. import Foundation
    2. func cRequest(completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> Void
    3. {
    4. let location = "https://httpbin.org/json"
    5. let requestURL = URL(string: location)
    6. let request = URLRequest(url: requestURL!)
    7. let task = URLSession.shared.dataTask(with: request, completionHandler: completionHandler)
    8. task.resume()
    9. }
    10. cRequest { (data: Data?, response: URLResponse?, error: Error?) in
    11. guard error == nil else {
    12. print("error=\(String(describing: error))")
    13. return
    14. }
    15. guard let receivedData = data else {
    16. print("Keine Daten erhalten")
    17. return
    18. }
    19. let receivedDataAsString = String(data: receivedData, encoding: .utf8) ?? ""
    20. print(receivedDataAsString)
    21. }
    Alles anzeigen
    Bevor ich nun ins jubeln gerate, hätte ich noch eine Bitte :D . Könntest Du mir erklären, was genau passiert und wie ich meine Daten letztendlich im Main Thread weiter verarbeiten kann bzw. wie Du auf diese Lösung gekommen bist. Hast Du evtl. einen Link zur Literatur für dieses Thema.

    @Michael: Vielen Dank

    Nachtrag: Habe mit dem Debugger die Abarbeitung nachvollziehen können. Dieser Teil meiner Frage hat sich erledigt. Bleibt nur noch offen wie ich die abgerufenen Daten in den Main Thread transportiere - zur weiteren Bearbeitung?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von OSXDev ()

  • Quellcode

    1. var data: Data?
    2. cRequest { (data: Data?, response: URLResponse?, error: Error?) in
    3. guard error == nil else {
    4. print("error=\(String(describing: error))")
    5. return
    6. }
    7. guard let receivedData = data else {
    8. print("Keine Daten erhalten")
    9. return
    10. }
    11. self.data = receivedData
    12. }
    13. while data == nil {
    14. }
    15. print(data)
    Alles anzeigen
  • OSXDev schrieb:

    MCDan schrieb:

    In den Main Thread kommt Du bei Swift mit:

    Quellcode

    1. DispatchQueue.main.async{
    2. // Code für Main Thread
    3. }
    In den Main Thread gelange ich doch automatisch nach Beendigung von cRequest. ?( Nur wie bekomme ich meine aufbereiteten Daten aus cRequest in den Main Thread? Irgendwie sehe ich, den Wald vor lauter Bäumen nicht. :/
    Wenn Du cRequest() im Main Thread aufrufst, dann bist Du nach dem Aufruf natürlich weiterhin im Main Thread.

    Dies nützt Dir allerdings nichts, da der Aufruf von URLSession.shared.dataTask() innerhalb von cRequest() asynchron ist. D.h. der Completion Handler von URLSession.shared.dataTask() wird irgendwann später aufgerufen. Du musst also innerhalb des Completion Handlers per DispatchQueue.main.async{} den gewünschten Code im Main Thread ausführen, wenn dies erforderlich ist.

    Und bitte nicht

    Quellcode

    1. while data == nil {
    2. }
    verwenden, da dies den Main Thread blockiert und ggf. dazu führen kann, dass die App von iOS beendet wird.

    Wenn Du unbedingt auf eine "synchrone Verarbeitung" mit einem asynchronen Request angewiesen bist, dann solltest Du lieber Semaphores verwenden.
  • MCDan schrieb:


    ...

    Wenn Du unbedingt auf eine "synchrone Verarbeitung" mit einem asynchronen Request angewiesen bist, dann solltest Du lieber Semaphores verwenden.
    In meiner Literatur finde ich nichts zu dem Thema Semaphores. Bevor ich mich hier auf die Suche begebe, wollte ich mal nachfragen ob Du mir eine gute Seite hierfür empfehlen kannst bzw. evtl. den Link posten könntest.

    Danke mal vorab.