Because your XML contains all of the values with attributes of the element, you don't need to implement foundCharacters
. Just didStartElement
, e.g., your parser delegate might look as simple as:
var song: String?
var artist: String?
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
switch elementName {
case "Song": song = attributeDict["title"]
case "Artist": artist = attributeDict["name"]
default: break
}
}
Two observations:
I'd be inclined to pull this parsing code out of the view controller, though, and put it in a dedicated object, to help prevent "view controller bloat".
I'd also use URLSession
in case the response to request happens to be a little slow. Generally, one should avoid using XMLParser(contentsOf:)
, because that performs the request synchronously.
In your case, since you’re requesting the data from localhost
, perhaps that’s less of a concern. But, still, it’s prudent to always perform HTTP requests asynchronously.
Anyway, that might yield something like:
class SongParser: NSObject {
var song: String?
var artist: String?
class func requestSong(completionHandler: @escaping (String?, String?, Error?) -> Void) {
let url = URL(string: "http://localhost/jazler/NowOnAir.xml")!
let task = URLSession.shared.dataTask(with: url) { data, _, error in
guard let data = data, error == nil else {
DispatchQueue.main.async {
completionHandler(nil, nil, error)
}
return
}
let delegate = SongParser()
let parser = XMLParser(data: data)
parser.delegate = delegate
parser.parse()
DispatchQueue.main.async {
completionHandler(delegate.song, delegate.artist, parser.parserError)
}
}
task.resume()
}
}
extension SongParser: XMLParserDelegate {
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
switch elementName {
case "Song": song = attributeDict["title"]
case "Artist": artist = attributeDict["name"]
default: break
}
}
}
And you'd use it like so:
SongParser.requestSong { song, artist, error in
guard let song = song, let artist = artist, error == nil else {
print(error ?? "Unknown error")
return
}
print("Song:", song)
print("Artist:", artist)
}