Search code examples
iosxmlswiftswxmlhash

how to use more efficiently SWXMLHash and how to set the class that will receive the data properly?


I'm building a project for a train schedule app, and I'm using an API that returns an XML file,to manipulate the data i'm using the library called SWXMLHash.

I need to extract the name of the departure station with its destinations and departure times.

I created a class to store all these data, but I need to improve it because I'm having trouble extracting the destinations and departure times.

I was able to loop over the xml file and get the names of the departure stations.

Here is my class :

class Station {
  var name : String 
  var destinations:[(dest:String, times:[Int])] 

init(withName name: String, destinations:[(dest:String, times:[Int])]){
  self.name = name
  self.destinations = destinations

  }
}

Here is my code to extract the names of the stations :

// This code loops over the XML file and retrieves the name of the departure stations 

// create an empty array of Station class
var stationsTest = [Station]()

// retrieve the name of the departure stations and add them to and array of Station
for elem in xml["root"]["station"].all{
  var stations = elem["name"].element!.text!
  var stationPlaceHolder = Station(withName: stations, destinations: [(dest:"",times:[1])])
  stationsTest.append(stationPlaceHolder)
}

My problem is how can I get the destinations of each station with their appropriate times of departure

I suppose the problem is in the way I implemented my class, I need to find a better solution.

Here is a sample of the XML file that I'm working with:

<?xml version="1.0" encoding="utf-8"?><root><uri><!   [CDATA[http://api.bart.gov/api/etd.aspx?  cmd=etd&orig=ALL&ramdom=1454366707766]]></uri><date>02/01/2016</date>
<time>02:44:52 PM PST</time>
<station>
  <name>Lake Merritt</name>
  <abbr>LAKE</abbr>
  <etd>
    <destination>Daly City</destination>
    <abbreviation>DALY</abbreviation>
    <estimate>
      <minutes>3</minutes>
    </estimate>
    <estimate>
      <minutes>10</minutes>
    </estimate>
    <estimate>
      <minutes>17</minutes>
    </estimate>
  </etd>
  <etd>
    <destination>Dublin/Pleasanton</destination>
    <estimate>
      <minutes>7</minutes>
    </estimate>
    <estimate>
      <minutes>22</minutes>
    </estimate>
    <estimate>
      <minutes>37</minutes>
    </estimate>
  </etd>
  <etd>
    <destination>Fremont</destination>
    <estimate>
      <minutes>4</minutes>
    </estimate>
    <estimate>
      <minutes>14</minutes>
    </estimate>
    <estimate>
      <minutes>19</minutes>
    </estimate>
  </etd>
  <etd>
    <destination>Richmond</destination>
    <estimate>
      <minutes>5</minutes>
    </estimate>
    <estimate>
      <minutes>19</minutes>
    </estimate>
    <estimate>
      <minutes>34</minutes>
    </estimate>
  </etd>
</station>

<station>
  <name>Fruitvale</name>
  <etd>
    <destination>Daly City</destination>
    <estimate>
      <minutes>6</minutes>
    </estimate>
    <estimate>
      <minutes>12</minutes>
     </estimate>
    <estimate>
      <minutes>22</minutes>
    </estimate>
  </etd>
  <etd>
    <destination>Dublin/Pleasanton</destination>
    <estimate>
      <minutes>10</minutes>
    </estimate>
    <estimate>
      <minutes>25</minutes>
    </estimate>
    <estimate>
      <minutes>40</minutes>
    </estimate>
  </etd>

Solution

  • I used the below code in the SWXMLHash playground and it works on my machine:

    // you need to model the ETD element as that has the destination and etd elements
    class Etd {
        var destination: String = ""
        var estimates = [String]()
    
        init(destination: String, estimates: [String]) {
            self.destination = destination
            self.estimates = estimates
        }
    }
    
    // each station has a collection of ETDs (per the XML)
    class Station {
        var name : String
        var etds = [Etd]()
    
        init(withName name: String, etds: [XMLIndexer]){
            self.name = name
    
            for etd in etds {
                self.etds.append(Etd(
                    destination: etd["destination"].element!.text!,
                    estimates: etd["estimate"].all.map { $0["minutes"].element!.text! })
                )
            }
        }
    }
    
    var stationsTest = [Station]()
    
    for elem in xml["root"]["station"] {
        var stations = elem["name"].element!.text!
        var stationPlaceHolder = Station(withName: stations, etds: elem["etd"].all)
        stationsTest.append(stationPlaceHolder)
    }
    

    Basically, it looks like what you were missing was the abstraction for the Etd class - a station doesn't contain a list of destinations, but a list of Etds. Each Etd then has both a destination and estimates by minute.