This code snippet is from a demo for working with XML files in Swift. Since this code is executed from playground following image shows the project structure.
The XMLapp file contains the class XMLapp
. This class contains a nested class XMLreader: NSObject, XMLParserDelegate
. Since the XMLapp
class is created in playground, it contains static function to call the code for reading XML.
public static func readXML() {
print("readXML")
let xmlReader = XMLreader()
xmlReader.parse()
}
Inside the main playground, following call is made XMLapp.readXML()
, which has this output:
readXML
begin parsing
end parsing
import Foundation
public class XMLapp {
class XMLreader: NSObject, XMLParserDelegate {
var xmlParser: XMLParser {
let url = Bundle.main.url(forResource:"sample.xml",withExtension:nil)!
return XMLParser(contentsOf: url)!
}
public func parser(_ parser: XMLParser, foundElementDeclarationWithName elementName: String, model: String) {
print("element name: \(elementName), model: \(model)")
}
public func parserDidStartDocument(_ parser: XMLParser) {
print("parserDidStartDocument")
}
public func parserDidEndDocument(_ parser: XMLParser) {
print("parserDidEndDocument")
}
// ...
// other protocol methods
public func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
print("didStartElement")
}
public func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
print("didEndElement")
}
// ...
// other protocol methods
public override init() {
super.init()
xmlParser.delegate = self
}
public func parse() {
print("begin parsing")
xmlParser.parse()
print("end parsing")
}
}
public static func readXML() {
print("readXML")
let xmlReader = XMLreader()
xmlReader.parse()
}
}
The callbacks are not getting called. Even though the delegate for the xmlParser is initialized. Please has anybody come across this issue in playground before? The following call is made to assign the delegate, still it is not working as expected.
xmlParser.delegate = self
CodePudding user response:
The xmlParser
property in XMLreader
is a computed property. This means that return XMLParser(contentsOf: url)!
gets evaluated every time you access it, so you get a new instance every time. As a result, the instance of XMLParser
you set the delegate
is a different instance from which you have called parse
:
xmlParser.delegate = self // you get one instance here
xmlParser.parse() // and a different instance here
To fix this, you can make xmlParser
a stored property:
let xmlParser: XMLParser = {
let url = Bundle.main.url(forResource:"sample.xml",withExtension:nil)!
return XMLParser(contentsOf: url)!
}()
Notice the =
. Learn more about the difference in syntax here.
Another way is to assign the xmlParser
to a local, set the delegate, and then parse
. This way you ensure that you are working with the same instance:
let parser = xmlParser
parser.delegate = self
parser.parse()