Here is a sample of the XML data I am trying to parse. Im having trouble figuring out how to parse the data inside the "" that is nested within the brackets i.e <something="How to get this string">. here for example id want to get "How to get this string". I am able to get the Data if its in between the opening and closing carrots I Got this.
<METAR>
<sky_condition sky_cover="OVC" cloud_base_ft_agl="2100"/> <---- get the OVC from sky_cover
<flight_category>IFR</flight_category>
<metar_type>METAR</metar_type>
</METAR>
Here is my XML Parser Code & Delegate functions
import Foundation
class FeedParser: NSObject, XMLParserDelegate {
private var airportItems: [FlightCategory] = [] // track parsing
private var currentAirportElement: String = "" // track current item being parsed
private var parserCompletiongHandler: (([FlightCategory]) -> Void)? // handle completion of parsing
private var currentAirportFlightCategory: String = "" {
didSet {
// trim whitespace
currentAirportFlightCategory = currentAirportFlightCategory.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
}
}
private var currenAirportSkyCondition: String = "" {
didSet {
currenAirportSkyCondition = currenAirportSkyCondition.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
}
}
// MARK: - URL Request
// Hnadle Parsing feed
func parseFeed(url: String, completionHandler: (([FlightCategory]) -> Void)?) {
self.parserCompletiongHandler = completionHandler
// URL Session Reques
let request = URLRequest(url: URL(string: url)!)
let session = URLSession.shared
let task = session.dataTask(with: request) { data, response, error in
// if no data
guard let data = data else {
if let error = error {
print(error.localizedDescription)
}
return
}
// if data
// parse xml data
let parser = XMLParser(data: data)
parser.delegate = self
parser.parse()
}
task.resume()
}
// MARK: - XML Parser Delegate
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
// track current item
currentAirportElement = elementName
if currentAirportElement == "METAR" { // it is there
// currentAirportElement = ""
currentAirportFlightCategory = ""
currenAirportSkyCondition = ""
}
}
// Once Parser get values of element Handler
func parser(_ parser: XMLParser, foundCharacters string: String) {
switch currentAirportElement {
case "flight_category": currentAirportFlightCategory += string
default: break
}
}
// Once Parser at End of element Handler
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
if elementName == "METAR" { // "flight_category"
let airportItems = FlightCategory(flight_category: currentAirportFlightCategory, sky_condition: currenAirportSkyCondition)
self.airportItems.append(airportItems)
}
}
// Call Completion Handler upon finishing Parsing
func parserDidEndDocument(_ parser: XMLParser) {
parserCompletiongHandler?(airportItems)
}
// Handle Parsing Errors
func parser(_ parser: XMLParser, parseErrorOccurred parseError: any Error) {
print(parseError.localizedDescription)
}
}
And here is how I am loading the XML
private func loadXMLData() async {
print("hmmmmm")
let acwXMLURL = "https://aviationweather.gov/api/data/dataserver?requestType=retrieve&dataSource=metars&stationString=\KVNY&startTime=2024-03-10T21%3A44%3A22Z&format=xml&mostRecent=true"
// parser feeder
let feeder = FeedParser()
feeder.parseFeed(url: acwXMLURL) { data in
self.detaileHomeAirportXML = data
}
}
private func handleXMLData() async {
detaileHomeAirportXML?.forEach { item in
currentFltCat = item.flight_category
model.airportFltCat = item.flight_category
print("XML data handled: \(currentFltCat)")
if currentFltCat == "VFR" {
color = Color(.green)
} else if currentFltCat == "MVFR" {
color = Color(.blue)
} else if currentFltCat == "IFR" {
color = Color(.red)
} else if currentFltCat == "LIFR" {
color = Color(red: 208 / 255, green: 45 / 255, blue: 208 / 255)
}
}
}
In the didStartElement
delegation function, you receive an attributes
dictionary. This contains the attributes of the tag.
You would write something like -
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
switch(elementName) {
case "METAR":
currentAirportFlightCategory = ""
currenAirportSkyCondition = ""
case "sky_condition":
skyCover = attributes["sky_cover"]
cloudBase = attributes["cloud_base_ft_agl"]
default:
print("Ignoring element \(elementName)")
}
}
Note that these will be optional strings, so you need to handle that and convert to integer as required.