Please read the last thread: Parse XML in SwiftUI
I've got the code to parse articles from XML courtesy of OOPer...but I need to actually execute it in ArticlesView. How would I call ArticlesParser into ArticlesView and related views and run the parser onto an XML file (THS.xml)?
Here's ArticleInfo.swift and ArticlesView.
ArticleInfo.swift:
ArticlesView:
I've got the code to parse articles from XML courtesy of OOPer...but I need to actually execute it in ArticlesView. How would I call ArticlesParser into ArticlesView and related views and run the parser onto an XML file (THS.xml)?
Here's ArticleInfo.swift and ArticlesView.
ArticleInfo.swift:
Code Block // // ArticleInfo.swift // Hair Society Go // // Created by Joshua Srery on 1/13/21. // Code provided by OOPer on Apple Developer Forums // import Foundation struct Article { var title: String = "" var date: Date? var author: String? var img: URL? /// content in HTML var content: String = "" } class ArticlesParser: XMLParser { // Public property to hold the result var articles: [Article] = [] var dateTimeZone = TimeZone(abbreviation: "GMT-6") lazy var dateFormater: DateFormatter = { let df = DateFormatter() //Please set up this DateFormatter for the entry `date` df.locale = Locale(identifier: "en_US_POSIX") df.dateFormat = "MMM dd, yyyy" df.timeZone = dateTimeZone return df }() private var textBuffer: String = "" private var nextArticle: Article? = nil override init(data: Data) { super.init(data: data) self.delegate = self } } extension ArticlesParser: XMLParserDelegate { // Called when opening tag (`<elementName>`) is found func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) { switch elementName { case "posts": nextArticle = Article() case "title": textBuffer = "" case "date": textBuffer = "" case "author": textBuffer = "" case "img": textBuffer = "" case "content": textBuffer = "" default: print("Ignoring \(elementName)") break } } // Called when closing tag (`</elementName>`) is found func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { switch elementName { case "posts": if let article = nextArticle { articles.append(article) } case "title": nextArticle?.title = textBuffer case "date": print("date: \(textBuffer)") nextArticle?.date = dateFormater.date(from: textBuffer) case "author": nextArticle?.author = textBuffer case "img": print("img: \(textBuffer)") nextArticle?.img = URL(string: textBuffer) case "content": nextArticle?.content = textBuffer default: print("Ignoring \(elementName)") break } } // Called when a character sequence is found // This may be called multiple times in a single element func parser(_ parser: XMLParser, foundCharacters string: String) { textBuffer += string } // Called when a CDATA block is found func parser(_ parser: XMLParser, foundCDATA CDATABlock: Data) { guard let string = String(data: CDATABlock, encoding: .utf8) else { print("CDATA contains non-textual data, ignored") return } textBuffer += string } // For debugging func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) { print(parseError) print("on:", parser.lineNumber, "at:", parser.columnNumber) } }
ArticlesView:
Code Block // // ArticlesView.swift // Hair Society Go // // Created by Joshua Srery on 11/29/20. // import SwiftUI struct ArticlesView: View { var body: some View { NavigationView { List { ForEach(0 ..< 5) { item in NavigationLink(destination: ArticleView(title: "Replace with Title var", image: "Replace with Img var", content: "Replace with Content var", author: "Replace with Author var", date: "Replace with Date var")) { ArticleRow(image: "Replace with Img var", title: "Replace with Title var", author: "Replace with Author var", date: "Replace with Date var") } } } .navigationTitle("Articles") .toolbar(content: { Menu { Button("Date", action: {}) Button("Title", action: {}) Button("Customize…", action: {}) } label: { Label("Filter", systemImage: "line.horizontal.3.decrease.circle") } }) // Ignore the filter Menu for right now } } } struct ArticleRow: View { let image: String let title: String let author: String let date: String var body: some View { HStack { Image(image) .resizable() .frame(minWidth: 75, maxWidth: 100, maxHeight: 75) .cornerRadius(12) VStack(alignment: .leading, content: { Text(title) .font(.headline) Text("\(author) • \(date)") .font(.subheadline) }) } } }
You have no need to (and should not, in this case) make ArticlesParser a local class nested in a function.
Code Block class ArticlesParser: XMLParser { //... } extension ArticlesParser: XMLParserDelegate { //... } class ArticlesInfo: ObservableObject { @Published var articles: [Article] = [] func loadArticles() { let articlesParser = ArticlesParser() //... } }