Parsing XML in Swift 4
Solution 1
Your code never actually initializes results
so the first time you try to use it you are trying to force-unwrap an nil
optional value. That's bad. And there's no reason to declare it as an implicitly unwrapped optional.
You need to change:
var results: [[String: String]]!
to:
var results = [[String: String]]()
You will also need to remove the line:
results = nil
from your parser(_:parseErrorOccurred:)
method.
If you would rather have results
be optional then you can make the following changes to your code:
Change the declaration of results
to:
var results: [[String: String]]? = [[String: String]]()
Change:
results.append(currentDictionary)
to:
results?.append(currentDictionary)
And you leave the results = nil
line in parser(_:parseErrorOccurred:)
.
Solution 2
rmaddy has correctly diagnosed the problem (+1). The results
was never initialized.
I would suggest leaving your code largely as is, but merely add a parserDidStartDocument
method that initializes results
as follows:
func parserDidStartDocument(_ parser: XMLParser) {
results = [[:]]
}
Ricky
Updated on August 22, 2020Comments
-
Ricky almost 4 years
I'm new to XML parsing in Swift and I found this code on Parsing XML from URL in Swift but I get a
EXC_BAD_INSTRUCTION
error when I try running the code. The description of the error reads:fatal error: unexpectedly found nil while unwrapping an Optional value
This is my simple XML file:
<xml> <book> <title>Book Title</title> <author>Book Author</author> </book> </xml>
The following code creates an
XMLParser
object and parses the XML file located in my Documents.// get xml file path from Documents and parse let filePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last?.appendingPathComponent("example.xml") let parser = XMLParser(contentsOf: filePath!) parser?.delegate = self if (parser?.parse())! { print(self.results) }
Here I implement the
XMLParserDelegate
methods and define my dictionaries:// a few constants that identify what element names we're looking for inside the XML let recordKey = "book" let dictionaryKeys = ["title","author"] // a few variables to hold the results as we parse the XML var results: [[String: String]]! // the whole array of dictionaries var currentDictionary: [String: String]! // the current dictionary var currentValue: String? // the current value for one of the keys in the dictionary // start element // // - If we're starting a "record" create the dictionary that will hold the results // - If we're starting one of our dictionary keys, initialize `currentValue` (otherwise leave `nil`) 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() } } // found characters // // - If this is an element we care about, append those characters. // - If `currentValue` still `nil`, then do nothing. func parser(_ parser: XMLParser, foundCharacters string: String) { currentValue? += string } // end element // // - If we're at the end of the whole dictionary, then save that dictionary in our array // - If we're at the end of an element that belongs in the dictionary, then save that value in the dictionary 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 } } // Just in case, if there's an error, report it. (We don't want to fly blind here.) func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) { print(parseError) currentValue = nil currentDictionary = nil results = nil }
The error is found on the
didEndElement
method when thecurrentDictionary
is appended to theresults
dictionary.func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { if elementName == recordKey { results.append(currentDictionary) // Line with Error currentDictionary = nil } else if dictionaryKeys.contains(elementName) { currentDictionary[elementName] = currentValue currentValue = nil } }
Please help me solve this issue. I'm using the exact same code provided on Parsing XML from URL in Swift and they don't seem to have any issues. Am I doing anything wrong?