0

Im a c# developer so i very used to wrapping a whole block of code in a try catch, writing to the event log and then not worrying about it.

i just can't get my head round the catch in swift

Im using the Kanna HTML/XML pod to parse a html page and look for links inside a table and extract them. Im doing it like this

enum MyError: Error {
    case FoundNil(String)
}

if let doc = Kanna.HTML(html: html, encoding: String.Encoding.utf8) 
{
    var count = 0
    //loop through all instances of <tr> in the html
    for row in doc.xpath("//tr") {
        var linkName = ""
        do{
            //get the <a> text from inside a <div> from the 2nd <td>
            if let name =  row?.xpath("//td[2]//div//a[1]")[count]
            {
                linkName = name.text
            }
            else{
                throw MyError.FoundNil("name")
            } 
            count = count + 1
        }
        catch{
            print("Error: \(error)")

        }
    }
}

The trouble is i don't always know that there will be a link in the table cell at xpath //td[2]//div//a[1] so it works for the first few rows then it crashes out with a fatal error,

fatal error: Index out of range

it doesn't go to the catch or the else

I've also tried using guard but that doesn't throw either

guard let name =  row?.xpath("//td[2]//div//a[1]")[count] else{
    throw MyError.FoundNil("name")
}

and what if i needed to check 50 table cells would i need a try catch on all of them

Bicklp
  • 31
  • 6
  • 1
    You cannot catch "index out of range" or other runtime errors in Swift, compare http://stackoverflow.com/questions/38737880/uncaught-error-exception-handling-in-swift. – Martin R Oct 05 '16 at 10:01
  • Problem is that `fatal error: Index out of range` is not exception, it's just plain crash with message. It's artefact of Objective C/Swift 1 times, those `fatalError` are everywhere in stdlib and cannot be catched using Swift 2+ `do/catch`. So, wellcome to `iOS dev`, you will see stuff like that quite often. – user28434'mstep Oct 05 '16 at 10:01
  • thanks, that explains a lot ... back to the drawing board – Bicklp Oct 05 '16 at 14:31

3 Answers3

0

As mentioned in the comments you cannot catch runtime exceptions.

But you could use optional binding and check for valid range in one line

if let name = row?.xpath("//td[2]//div//a[1]") where count < name.count {
  linkName = name[count].text
}

In Swift 3 replace where with a comma

vadian
  • 274,689
  • 30
  • 353
  • 361
  • will try this, i re-worked a different way so I'm not using count to access the row anymore so no more out of range, but this looks like it will work if i go back to the [count] – Bicklp Oct 05 '16 at 14:38
0

I am answering only refers to try... Catch... And may be it will be work for others too. you can do like this...

enum MyError: Error {
    case FoundNil(String)
}
do{
if let doc = Kanna.HTML(html: html, encoding: String.Encoding.utf8) 
{
    var count = 0
    //loop through all instances of <tr> in the html
    for row in doc.xpath("//tr") 
    {
        var linkName = ""

            //get the <a> text from inside a <div> from the 2nd <td>
            if let name =  row?.xpath("//td[2]//div//a[1]")[count]
            {
                linkName = name.text
            }
            else{
                throw MyError.FoundNil("name")
            } 
            count = count + 1
      }

}
}catch{
            print("Error: \(error)")

        }

you still need any help feel free to ask me.

JAck
  • 854
  • 9
  • 18
0

In the end i just took out all the catches and reworked how i was accessing the data and it seems to have done the trick im not using count to move through the rows anymore, using a different xpath value and then working directly on the active row, rather than where i thought i was in the array, i think its just the . at the beginning of the xpath that sorted it all, as in work on current row so i don't need the [count] to get to it

let xpo = doc.xpath("//tr") as XPathObject
let xCount = xpo.count
var count = 0 
for row in xpo {
    if (count <= xCount){
        var linkName = ""

        for a in row.xpath(".//td[2]//div//a[1]")
        {
            linkName =  a.text
        }
    }
    count = count +1 
}
Bicklp
  • 31
  • 6