swiftui list array example load from xml feed

VonTobias Stephan

swiftui list array example load from xml feed

Hier zeige ich Dir, wie du mit Swift UI eine Liste generierst. Zuerst laden wir ein XML-File aus dem Web. Dann durchlaufen wir das Array und füllen eine neue DataSource Class mit einem identifiable struct. Wir achten darauf, dass wir nicht @State verwenden, sondern @ObservedObject. Die DataSource class hat den Typ ObservableObject. Für die Liste gibt es ein eigenes Struct, das macht die ganze sache übersichtlicher. Ich selber habe den Fehler als Swift UI Neuling hier @State zu verwenden. Bei dem Versuch die Datenquelle zu füllen, bekam ich keine Fehlermeldung, aber die Anzahl der Datensätze aus dem gelesenen XML-File blieb schlichtweg 0.

Hierfür ein Swift-File anlegen. Das ist die Klasse um XML zu parsen.
//
//  XMLParser.swift
//  Listory
//
//  Created by T. Stephan on 15.02.20.
//  Copyright © 2020 eCommerce - Tobias Stephan. All rights reserved.
//
import UIKit
import Foundation
class ListoryXMLParser: UIViewController, XMLParserDelegate {
    var parser = XMLParser()

       let recordKey = "export"
       var dictionaryKeys = [String]() //"ASIN","Titel","Hersteller","Marke","BuyBoxPreis","Logdatum","Gesamtangebote","Bulletpoints"]

       // a few variables to hold the results as we parse the XML

       public var results: [[String: String]]? = [[String: String]]()         // the whole array of dictionaries
       var currentDictionary: [String: String]!                        // the current dictionary
       var currentValue: String?

       let defaults = UserDefaults.standard

       func beginParsing(tablename: String,criterion: String){
           let sASIN = ""
           beginParsing(tablename: tablename, criterion: criterion, asin: sASIN)
       }

       func beginParsing(tablename: String,criterion: String,asin: String)
       {
           results?.removeAll()
           //posts = []
           //let defaults = UserDefaults.standard

           var sHostname = "https://www.hostname.de/"

           if (defaults.string(forKey: "httpHostname") != nil){
               sHostname = defaults.string(forKey: "httpHostname")!
           }

           var sUrl = sHostname + "/exportxml.aspx?table=" + tablename + "&criterion=" + criterion

           if asin != "" {
               sUrl = sUrl + "&asin=" + asin
           }

           if (defaults.string(forKey: "fcm") != nil){
               sUrl = sUrl + "&token=" + defaults.string(forKey: "fcm")!
           }

           parser = XMLParser(contentsOf:(NSURL(string: sUrl))! as URL)!

           parser.delegate = self
           parser.parse()

       }

       func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {

           if elementName == recordKey {

               currentDictionary = [String : String]()

           } else if dictionaryKeys.contains(elementName) {

               currentValue = String()

           }
       }

       func parser(_ parser: XMLParser, foundCharacters string: String) {

           currentValue? += string

       }

       func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {

           if elementName == recordKey {

               results?.append(currentDictionary)
               currentDictionary = nil

           } else if dictionaryKeys.contains(elementName) {

               currentDictionary[elementName] = currentValue
               currentValue = nil

           }
       }

       func parserDidStartDocument(_ parser: XMLParser) {
           //results = [[:]]
       }

       func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {

           print(parseError)

       }
}

Dann legen wir noch ein SwiftUI File ein und definieren hier das struct für die Datenquelle.

Grundlage für die Datenquelle:
import SwiftUI

struct oListEntryFields: Identifiable {
    var id = UUID()
    var rowID : Int64
    var ListKey : String
    var ListenGruppenKey : String
    var ListenKeyView : String
    var Caption: String

}

Hier bewegen wir uns nun im ContentView.swift File. Hier findet man dann auch die Klasse für die Datenquelle.

import SwiftUI
import Combine

class ListDataSource: ObservableObject {

    var ListOfListEntries = [oListEntryFields]()
    let oListoryParser = ListoryXMLParser()

    init() {
        oListoryParser.dictionaryKeys.removeAll()
        oListoryParser.dictionaryKeys.append("ID");
        oListoryParser.dictionaryKeys.append("ListenKey");
        oListoryParser.dictionaryKeys.append("ListenGruppenKey");
        oListoryParser.dictionaryKeys.append("ListenKeyView");
        oListoryParser.dictionaryKeys.append("Bezeichnung");
        oListoryParser.dictionaryKeys.append("Aenderungsdatum");
        oListoryParser.dictionaryKeys.append("Erstanlagedatum");
        oListoryParser.dictionaryKeys.append("Archiviert");
        oListoryParser.dictionaryKeys.append("Wiedervorlage");
        oListoryParser.beginParsing(tablename: "key", criterion: "bsCQ2ivUHXOyk")

        var iRow = -1
        for s in oListoryParser.results! {
            iRow += 1
            let rowID = Int64(s["ID"]!)
            let ListEntry = oListEntryFields(rowID: rowID!, ListKey: s["ListenKey"]!, ListenGruppenKey: s["ListenGruppenKey"]!, ListenKeyView: s["ListenKeyView"]!, Caption: s["Bezeichnung"]!)
            //LoadListData.append(ListEntry)
            ListOfListEntries.append(ListEntry)

        }
    }
}

public struct ContentView: View {
    @State var selection: Int = 0

    let defaults = UserDefaults.standard

    public var body: some View {

        TabView(selection: $selection){

            VStack{
                ListoryListView()
            }
            .tabItem {
                VStack {
                    Image("first")
                    Text("First")
                }
            }

            .tag(0)
            Text("Second View")
                .font(.title)
                .tabItem {
                    VStack {
                        Image("second")
                        Text("Second")
                    }
            }
            .tag(1)
        }.onAppear(){

        }
    }

    struct ListoryListView: View {

        @ObservedObject var oListDatasource = ListDataSource()

        let oListoryParser = ListoryXMLParser()
        @State var selection = Set<UUID>()
        var body: some View {
            NavigationView {
                List(){
                    ForEach(oListDatasource.ListOfListEntries)
                    { item in

                        VStack(alignment: .leading){
                            Text(item.Caption)

                        }

                        .onTapGesture {
                            print("\(item.Caption)")

                        }
                        .navigationBarTitle(Text("List"))

                    }.onDelete(perform: xdelete)
                }
            }

        }
        
        func xdelete(at offsets: IndexSet) {
            if let first = offsets.first {
                print("remove \(first)")
                oListDatasource.ListOfListEntries.remove(at: first)
            }
        }

 
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Über den Autor

Tobias Stephan administrator

2 Kommentare bisher

Giorgos KatsarosEingestellt am1:42 pm - Mai 18, 2020

Hallo Tobias,

Vielen Dank für den Post! Für mich war das sehr hilfreich.
Ich habe nur eine kleine bitte. Kannst du bitte die URL anpassen, denn ich denke die URL “https://www.hostname.de” funktioniert nicht.

Schreibe eine Antwort