I am attempting to parse XML using an URLSession, XCode 12, SwiftUI but it keeps returning [] or nil. If I print immediately after the parse(see code), all the data is there, but for some reason, it seems to be clearing it all out.
If I try it with a .xml file, the code works fine, so it must be something in my URLSession in a XMLParserDelegate class:
And then I call that with a button in my View:
If I try it with a .xml file, the code works fine, so it must be something in my URLSession in a XMLParserDelegate class:
Code Block class ParseController: NSObject, XMLParserDelegate{ var items: [Item] = [] var itemStore: [Item]? func loadData() { let url = URL(string: "website")! let request=URLRequest(url: url) let task = URLSession.shared.dataTask(with: request) { (data, response, error) in if data == nil { print("dataTaskWithRequest error: \(String(describing: error?.localizedDescription))") return } let parser = XMLParser(data: data!) parser.delegate=self parser.parse() self.itemStore=self.items print(self.itemStore) } task.resume() /* if let path = Bundle.main.url(forResource: "Items", withExtension: "xml") { if let parser = XMLParser(contentsOf: path) { parser.delegate = self parser.parse() self.itemStore=self.items } } */ }
And then I call that with a button in my View:
Code Block struct MyParserView: View { @State var itemsResult: [Item]? var body: some View { if ((itemsResult?.isEmpty) == nil) { VStack { Text("Stuff here") Button(action: { let parserControl = ParseController() parserControl.loadData() itemsResult = parserControl.itemStore }){ Text("Push") } } }else { List{ ForEach(itemResult!, id:\.id){item in Text(item.name) } } } } }
Your code does not work because it does not handle asynchronous calls properly.
When you call loadData(), the completion handler passed to dataTask (line 9. { (data, response, error) in to line 21 }) is executed after loadData() is finished.
One way of utilizing the result of async calls in SwiftUI is using @Published var in an ObservableObject.
Update your ParseController as follows:
And use it in a SwiftUI view like this:
This is not tested, so you may need to modify some parts, but please try.
When you call loadData(), the completion handler passed to dataTask (line 9. { (data, response, error) in to line 21 }) is executed after loadData() is finished.
One way of utilizing the result of async calls in SwiftUI is using @Published var in an ObservableObject.
Update your ParseController as follows:
Code Block class ParseController: NSObject, XMLParserDelegate, ObservableObject { //<- var items: [Item] = [] @Published var itemStore: [Item]? //<- func loadData() { let url = URL(string: "website")! let request=URLRequest(url: url) let task = URLSession.shared.dataTask(with: request) { (data, response, error) in if let error = error { print("dataTaskWithRequest error: \(error)") return } guard let data = data else { print("dataTaskWithRequest data is nil") return } let parser = XMLParser(data: data) parser.delegate = self parser.parse() self.itemStore = self.items print(self.itemStore) } task.resume() } //... I assume you are implementing `XMLParserDelegate` methods properly... }
And use it in a SwiftUI view like this:
Code Block struct MyParserView: View { @StateObject var parserControl = ParseController() //<- var body: some View { if let itemsResult = parserControl.itemStore, !itemsResult.isEmpty { List{ ForEach(itemsResult, id:\.id) {item in //<- Text(item.name) } } } else { VStack { Text("Stuff here") Button(action: { //Do nothing here //<- parserControl.loadData() //Do nothing here //<- }){ Text("Push") } } } } }
This is not tested, so you may need to modify some parts, but please try.