Check URLResponse

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

  • Check URLResponse

    Hallo zusammen,

    ich habe eine Frage zu einer Problemstellung die ich letztendlich durch ein Stück Code gelöst habe was ich bei Stackoverflow zu dem Thema gefunden habe und für mich etwas angepasst. Allerdings war das noch Swift 3 und jetzt bin ich mir nicht sicher ob das nicht doch irgendwie eleganter/besser geht?

    Problem:
    Ich hole mit JSON Daten über eine URL um diese dann zu Parsen. Wenn aber z.B. der Webservice nicht verfügbar ist, bekomme ich keinen Error aber eine andere Response, was dazu führt das der JSON Decode "auf die Bretter geht". Ich habe also nach einer Lösung gesucht den WebService Response Status zu checken bevor ich das Parsen starte.

    Hier mal meine, oder besser, die gefundene Lösung, auf meine Bedürfnisse angepasst:

    Brainfuck-Quellcode

    1. // Erweiterung zu URLResponse Klasse, damit in der Data Session auf den Response Code abgefragt werden kann.
    2. // != 200 --> Error und kein JSON Parsing ausführen.
    3. extension URLResponse {
    4. func getStatusCode() -> Int? {
    5. if let httpResponse = self as? HTTPURLResponse {
    6. return httpResponse.statusCode
    7. }
    8. return nil
    9. }
    10. }
    11. // API Endpoint: https://free.currconv.com/api/v7/convert?q=USD_EUR,EUR_USD&compact=ultra&apiKey=123456789
    12. // Reference: https://www.currencyconverterapi.com
    13. let urlString = "https://free.currconv.com/api/v7/convert?q=USD_EUR,EUR_USD&compact=ultra&apiKey=123456789"
    14. let url = URL(string: urlString)
    15. let session = URLSession.shared
    16. let dataTask = session.dataTask(with: url!) { (data, reponse, error) in
    17. // check for HTTP response code
    18. if let statusCode = reponse?.getStatusCode() {
    19. if statusCode != 200 {
    20. print("HTTP Response Error: \(statusCode)")
    21. return
    22. }
    23. }
    24. // check for errors
    25. if error == nil && data != nil {
    26. // parse json
    27. do {
    28. // Aktuelles Abfrage Datum ermitteln und formatieren
    29. // Referenz: https://www.ralfebert.de/ios/swift-dateformatter-datumsangaben-formatieren/
    30. let formatter = DateFormatter()
    31. formatter.locale = .init(identifier: "de")
    32. formatter.dateStyle = .full
    33. formatter.timeStyle = .full
    34. let dateFormattedString = formatter.string(from: Date())
    35. print("Abfrage@ \(dateFormattedString)")
    36. let currency: Currency = try! JSONDecoder().decode(Currency.self, from: data!)
    37. print ("1€ = \(currency.EUR_USD)$")
    38. print ("1$ = \(currency.USD_EUR)$")
    39. print("------------------------------------------------------------------------------")
    40. print ("This is JSON result --> \(currency)")
    41. }
    42. }
    43. }
    44. dataTask.resume()
    Alles anzeigen
    So wie ich es verstehe ist die Idee die URLResponse um eine Methode zu erweitern die den HTTP response Code liefert. Diesen überprüfe ich und das Parsen wird nur gestartet bei == 200 (OK).

    Mir ist klar das es immer mehrere Wege gibt, mich würde aber Eure Experten Meinung zu dieser Lösung interessieren, da das für einen Anfänger immer sehr hilfreich ist :)

    Gruß

    Ralf
  • Hey,

    mal als kleinen Anreiz:

    Brainfuck-Quellcode

    1. // API Endpoint: https://free.currconv.com/api/v7/convert?q=USD_EUR,EUR_USD&compact=ultra&apiKey=123456789
    2. // Reference: https://www.currencyconverterapi.com
    3. let urlString = "https://free.currconv.com/api/v7/convert?q=USD_EUR,EUR_USD&compact=ultra&apiKey=123456789"
    4. guard let url = URL(string: urlString) else {
    5. fatalError("huuuuuuuurz")
    6. }
    7. let dataTask = URLSession.shared.dataTask(with: url) { data, response, error in
    8. if let error = error {
    9. print("Whoooops \(error)")
    10. return
    11. }
    12. guard
    13. let response = response as? HTTPURLResponse,
    14. (200...299).contains(response.statusCode),
    15. let data = data
    16. else {
    17. print("Go away")
    18. return
    19. }
    20. // parse json
    21. do {
    22. // Aktuelles Abfrage Datum ermitteln und formatieren
    23. // Referenz: https://www.ralfebert.de/ios/swift-dateformatter-datumsangaben-formatieren/
    24. let formatter = DateFormatter()
    25. formatter.locale = .init(identifier: "de")
    26. formatter.dateStyle = .full
    27. formatter.timeStyle = .full
    28. let dateFormattedString = formatter.string(from: Date())
    29. print("Abfrage@ \(dateFormattedString)")
    30. let currency: Currency = try JSONDecoder().decode(Currency.self, from: data)
    31. print ("1€ = \(currency.EUR_USD)$")
    32. print ("1$ = \(currency.USD_EUR)$")
    33. print("------------------------------------------------------------------------------")
    34. print ("This is JSON result --> \(currency)")
    35. } catch {
    36. print(error)
    37. }
    38. }
    39. dataTask.resume()
    Alles anzeigen
  • Hallo Matz,

    Danke für Deinen Tipp, allerdings ist mir nicht ganz klar wie das mein Problem lösen kann?
    Wenn ich das richtig verstehe wird damit sichergestellt das es einen urlString gibt. Das ist bei mir aber auf jeden
    Fall sichergestellt da ich diesen fest kodiere und es somit auf jeden Fall der richtige/korrekte ist.

    So wie ich das sehe kann ich damit aber nicht checken ob ich gültige JSON Daten bekommen habe. Wenn z.B. der WebService
    nicht verfügbar ist bekomme ich keinen Error, ich bekomme einen HTTP Response: 503 - Service not available.
    Wenn ich das dem JSON Decode "vorwerfe" findet der das nicht lustig und bricht mit Error ab:

    Quellcode

    1. Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON."
    Habe ich da was grundsätzliches übersehen oder mal wieder nicht weit genug gedacht ?(

    Gruß

    Ralf
  • Oh je ;( sorry .. ich bin doch wirklich ein Depp ... ich habe gerade gesehen das ich Deine Antwort, sprich Deinen Code nicht komplett "aufgeklappt" habe und somit nur den "guard let url ..." gesehen habe und nicht den anderen Code ... peinlich.

    Ich werde es Morgen mal testen aber es sieht sehr gut und logisch aus und ohne die Klasse URLResponse zu erweitern.

    Danke schon mal.

    Gruß und einen schönen Abend. Mache jetzt Schluß bevor mir noch mehr von solchen Dingen passieren ;)

    Ralf
  • Hallo Matz,

    ich habe Deine Anregung jetzt mal umgesetzt und es funktioniert so wie erwartet :thumbsup:
    Ich habe dazu noch eine Frage ... ich bekomme es nicht hin an den Integer Wert des statusCode vom HTTP Response zu kommen ?(
    Ich habe einiges probiert, aber da fehlen mir anscheinend noch einige Grundlagen ...
    So wie ich das bisher verstehe sollte das ja in HTTPURLReponse (response.statusCode) vorhanden sein, ich bekomme das aber irgendwie da nicht raus.

    Wenn Du da noch ein Beispiel hättest wäre ich sehr dankbar.

    Gruß

    Ralf
  • matz schrieb:

    Vermutlich läuft er doch in den error case.
    Nein, der Code an sich läuft einwandfrei. Ich habe beim ersten Test den falschen API Key drin gelassen, und da ist er korrekt in den error case gelaufen.
    Aus meiner Sicht geht es doch um diese Stelle - oder liege ich da falsch?

    Quellcode

    1. // Use guard Keyword to check if the WebService reponse == 200
    2. guard
    3. let response = response as? HTTPURLResponse,
    4. (200...299).contains(response.statusCode),
    5. let data = data
    6. else {
    7. print("HTTP Response Error")
    8. return
    9. }
    In dem guard Statement wird doch ein Vergleich gemacht ob der resultierende Code (int) in dem Wertebereich von 200-299 liegt.

    Müsste, oder besser könnte, ich dieses guard Statement erweitern um mir den response.statusCode in einer Variablen zu merken, damit ich diesen ggf. irgendwo ausgeben kann?
    An der Stelle fehlt mir das Know-How wie ich den response.statusCode in eine Variable sichere.

    Ergänzung:

    Genau genommen benötige ich ja den Code nur im Fehlerfall, da ich dann den Fehler anstelle des korrekten JSON Output ausgeben kann.
    Sprich, ich müsste es im else Zweig machen ... ??

    Gruß

    Ralf
  • ralfb schrieb:

    Müsste, oder besser könnte, ich dieses guard Statement erweitern um mir den response.statusCode in einer Variablen zu merken, damit ich diesen ggf. irgendwo ausgeben kann?
    Argh, ich Trottel :)


    C-Quellcode

    1. guard
    2. let response = response as? HTTPURLResponse,
    3. let data = data
    4. else {
    5. print("Go away")
    6. return
    7. }
    8. let statusCode = response.statusCode
    9. if !(200...299).contains(statusCode) {
    10. // handle an error?
    11. }
    12. print(statusCode)
    Alles anzeigen
  • Hallo Matz,

    Vielen Dank - ja, so geht es und ich habe alle Daten die ich benötige :)
    Bin ein wenig enttäuscht das ich das nicht hinbekommen habe, habe einiges probiert, hat aber alles zu Fehlern in Xcode geführt.

    Ich bin daran gescheitert das ich response.statusCode nicht einfach im else verwenden/zuweisen kann.


    Quellcode

    1. Variable used within its own initial value
    Ist halt noch viel Luft mach oben ;)

    Gruß,

    Ralf