Swift: How do I add data to a variable of an object in a list of objects

Viewed 461

I have 3 layers of data I'm getting from an xml that I need to store

1: Start time and end time

2: IP and mac address of a host

3: Port number and service of each host IP

The approach I used to solve this was to create 3 classes and let each have variables for the data like so:

class Ports {
var scanPortNumber: [String] = [""]
var scanProtocol: [String] = [""]
}

class Hosts {
    var hostPorts = [Ports]()
    var scanHostIP: [String] = [""]
    var scanHostMacAddr: [String] = [""]
}

class Scan {
    var scanHosts = [Hosts]()
    var scanStartTime: [String] = [""]
    var scanEndTime: [String] = [""]
}

then I declared a global object where i'm parsing the xml like this:

var scanDetails = Scan()

so I could store the IP's I get from the xml like this:

scanDetails.scanHosts[i].scanHostIP[i] = "the IP I get from parsing the xml"

here is how I'm trying to store the data:

var scanDetails = Scan()

class XMLRead: NSObject, XMLParserDelegate {
    var hostIP = ""
    var hostMacAddr = ""
    
    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
        
        var i = 0
        
        if !elementName.isEmpty {
            
            switch elementName {
                
                case "address":
                    print("i in address = \(i)")
                    let addrType = attributeDict["addrtype"]
                    if addrType == "ipv4" {
                        let hostIPtmp = attributeDict["addr"]
                        if hostIPtmp != hostIP || hostIP == "" {
                            hostIP = hostIPtmp!
                            scanDetails.scanHosts[i].scanHostIP[i] = hostIP
                        }
                    }
                    else if addrType == "mac" {
                        let hostMacAddrtmp = attributeDict["addr"]
                        if hostMacAddrtmp != hostMacAddr || hostMacAddr == "" {
                            hostMacAddr = hostMacAddrtmp!
                            scanDetails.scanHosts[i].scanHostMacAddr[i] = hostMacAddr
                        }
                    }
                    print("hostIP = \(scanDetails.scanHosts[i].scanHostIP[i])")
                    print("hostMacAddr = \(scanDetails.scanHosts[i].scanHostMacAddr[i])")
                
                default:    break
            }
            
            i += 1
            print("i = \(i)")
        }
    }

    func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
        print("failure error: \(parseError)")
    }
} 

but this method doesn't seem to be working since it gives me the **Index out of range error** when I do this. So i tried using the append() method but it said I had to append a Hosts type and didn't allow me to append the value I get from the xml since it was a string.

I'm pretty new to swift so if someone can please tell me what am I doing wrong and how do I can fix it i'd be grateful.

4 Answers

Lets focus on your scanDetails.scanHosts[i] first:

  • Lets assume array 'scanHosts' has only 2 elements, so how many indexes does it have at this point? Answer: '0' and '1'
  • Meaning scanDetails.scanHosts[0] = "Blah blah blah" will work
  • Also meaning scanDetails.scanHosts[1] = "Blah blah blah" will also work
  • But if the value of 'i' is '2', meaning scanDetails.scanHosts[2] = "Blah blah blah", it will give you an exception. Because index '2' does not exist.

Same goes for the next part of your statement .scanHostIP[i] which is after scanDetails.scanHosts[i]

I recommend you share your for loop code here so we can help more regarding this issue of yours

To simply run the code that you have provided: I would make these following changes:

.
.
var i = 0
var host: Hosts?
.
.
if hostIPtmp != hostIP || hostIP == "" {

   hostIP = hostIPtmp!
   host = Hosts()
   host!.scanHostIP.append(hostIP)
                        
   scanDetails.scanHosts.append(host!)
}
.
.
.
if hostMacAddrtmp != hostMacAddr || hostMacAddr == "" {
   hostMacAddr = hostMacAddrtmp!

   if host == nil {
      host = Hosts()
   }
   host!.scanHostMacAddr.append(hostMacAddr)
   scanDetails.scanHosts.append(host!)
}
.
.

But i don't understand, why you are using 'i' and variables of the classes as Arrays, if you are only going to put one value inside each of them.

When You create an object like as.. var scanDetails = Scan(). The atributes of scanDetails that means scanHosts, scanStartTime, scanEndTime arrays are empty. When allocate memory or insert value in array then You access those index of array

You can create an array with initialized.like as.. var myDoubles = [Double](count: 10, repeatedValue: 0.0) This would create an array of 10 doubles, all initialized with the value of 0.0. Now you access the index 0 to 9.

struct Ports {
    var scanPortNumber: [String]
    var scanProtocol: [String]
}

struct Hosts {
    var hostPorts : [Ports]
    var scanHostIP: [String]
    var scanHostMacAddr: [String]
}

struct Scan {
    var scanHosts : [Hosts]
    var scanStartTime: [String]
    var scanEndTime: [String]
}

Try using a struct instead of a class, it will make your code a lot cleaner.

Related