Tutorial: JSON-Daten mit URLSession laden und mit SwiftUI anzeigen

von @ralfebert · aktualisiert am 15. Juli 2021
SwiftUI, Xcode 12 & iOS 14
Diesen Artikel gibt es auch für:
Entwickler*innen ohne iOS-Vorkenntnisse

Dieses Tutorial für zeigt Schritt für Schritt, wie JSON-Daten im Hintergrund mit der URLSession geladen werden, mit der JSONDecoder-Klasse dekodiert werden und mit SwiftUI zur Anzeige gebracht werden.

  1. Dieses Tutorial setzt SwiftUI-Grundkenntnisse voraus. Sofern Du noch nicht mit SwiftUI gearbeitet hast, empfehle ich zuerst das SwiftUI Tutorial: Einführung in SwiftUI durchzuarbeiten.

  2. Verwende für dieses Tutorial die aktuelle Version von Xcode (dieses Tutorial wurde zuletzt getestet am 15. Juli 2021 mit Xcode 12.5).

  3. Erstelle ein neues App-Projekt Countries basierend auf SwiftUI.

  4. Füge dem Projekt eine neue Swift-Datei mit einem Datentyp Country und einigen statischen Beispieldaten hinzu:

    struct Country: Identifiable {
    
        var id: String
        var name: String
    
        static let allCountries = [
            Country(id: "be", name: "Belgien"),
            Country(id: "bg", name: "Bulgarien"),
            Country(id: "el", name: "Griechenland"),
            Country(id: "lt", name: "Litauen"),
            Country(id: "pt", name: "Portugal"),
        ]
    
    }
    
  5. Implementiere in ContentView mit einem List-View eine einfache tabellarische Darstellung der Länder:

    Beispielprojekt Countries für UITableViewController

    Lösung anzeigen

    struct CountriesView: View {
        var body: some View {
            List(Country.allCountries) { country in
                Text(country.name)
            }
        }
    }
    
  6. Rufe im Browser die Beispiel-JSON-Daten auf und mache Dich mit dem Format der Daten vertraut:

    Anzeige der JSON-Beispieldaten im Browser
  7. Erstelle eine Klasse CountriesModel, die für das Laden und Halten der Daten zuständig ist. Lasse diese von ↗ ObservableObject erben und deklariere eine ↗ @Published-Eigenschaft countries. Dadurch wird das Objekt beobachtbar - wenn Sich die Liste der der Länder später ändert, kann das View reagieren:

    class CountriesModel: ObservableObject {
        @Published var countries = Country.allCountries
    }
    
  8. Verwende dieses Objekt für die Länderliste im View. Deklariere ein Property als ↗ @StateObject, damit SwiftUI die Instanz von dem Objekt verwaltet und bei Änderungen das Objekt automatisch aktualisiert:

    struct CountriesView: View {
        @StateObject var countriesModel = CountriesModel()
    
        var body: some View {
            List( countriesModel.countries) { country in
                Text(country.name)
            }
        }
    }
    
  9. Erstelle eine Methode reload im CountriesModel. Verwende den ↗ dataTask der ↗ URLSession um einen dataTask zu erzeugen und zu starten:

    class CountriesModel: ObservableObject {
        @Published var countries = Country.allCountries
    
        func reload() {
        let url = URL(string: "https://www.ralfebert.de/examples/v2/countries.json")!
    
        let urlSession = URLSession.shared
    
        let task = urlSession.dataTask(with: url) { (data, response, error) in
    
        }
    
        task.resume()
    }
    
    }
    

    Hinweis: Verwende die Code-Vervollständigung um den Aufruf zu tippen und achte darauf, die richtige Methode mit den Parametern URL und completionHandler auszuwählen:

    Vervollständigung für NSURLSession.dataTask

    Hinweis: Bestätige Sie den Xcode-Vorschlag mit Enter. Springe mit Tab zu dem completionHandler-Parameter und bestätige den Vorschlag ebenfalls mit Enter um den Block-Code zu erzeugen. Vergib hier die Variablennamen data, response, error für die Parameter:

    Xcode Code-Vervollständigung für den completionHandler-Block
  10. Entferne in Countries.swift die Eigenschaft allCountries mit den Beispieldaten und deklariere den Typ als Codable:

    struct Country : Identifiable, Codable {
        var id: String
        var name: String
    }
    
  11. Passe die countries-Eigenschaft im CountriesModel so an, dass diese zunächst mit einer leeren Liste initialisiert wird:

    class CountriesModel: ObservableObject {
        @Published var countries : [Country] = []
    
        // ...
    }
    
  12. Implementiere den completionHandler des dataTask: Verwende einen → JSONDecoder um die geladenen Daten zu dekodieren. Aktualisiere die Darstellung (dies muss auf dem → Main-Thread erfolgen, der completionHandler wird auf dem Hintergrund-Thread ausgeführt, der die Daten geladen hat):

    let task = urlSession.dataTask(with: url) { (data, response, error) in
        // Error handling in case the data couldn't be loaded
    // For now, only display the error on the console
    guard let data = data else {
        debugPrint("Error loading \(url): \(String(describing: error))")
        return
    }
    
        // Parse JSON with JSONDecoder assuming valid JSON data
    let countries = try! JSONDecoder().decode([Country].self, from: data)
    
        // Update UI
    OperationQueue.main.addOperation {
        self.countries = countries
    }
    }
    
  13. Füge im CountriesView einen onAppear-Block hinzu um das Laden der Daten auszulösen:

    struct CountriesView: View {
        @StateObject var countriesModel = CountriesModel()
    
        var body: some View {
            List(countriesModel.countries) { country in
                Text(country.name)
            }
            .onAppear {
        self.countriesModel.reload()
    }
        }
    }
    
  14. Starte die App mit Product » Run ⌘R und prüfe, dass die Länder geladen und angezeigt werden:

    Ergebnis Länderanzeige via JSON

Weitere Informationen