Here's code for ArticleInfo since it was updated to integrate the extension into the actual ObservableObject:
import Foundation
struct ArticleInfo: Identifiable, Codable {
var id: String = ""
var title: String = ""
var date: Date?
var author: String = ""
var img: URL?
var content: String = ""
}
class ArticlesInfo: ObservableObject {
@Published var articles: [ArticleInfo] = []
var dataUrl = URL(string: "https://storage.googleapis.com/thehairsociety/2021/03/2f23e2d4-ths.xml")
func loadArticles() {
do {
let data = try Data(contentsOf: dataUrl!)
let parser = ArticlesParser(data: data)
if parser.parse() {
print(parser.articles) // Value of type 'ArticlesParser' has no property 'articles'
let articles: try //...?
self.articles = articles // Unsure what I put here
} else {
if let error = parser.parserError {
print(error)
} else {
print("Failed with unknown reason")
}
}
} catch {
print(error)
}
}
}
class ArticlesParser: XMLParser {
var dateTimeZone = TimeZone(abbreviation: "GMT-6")
lazy var dateFormater: DateFormatter = {
let df = DateFormatter()
df.locale = Locale(identifier: "en_US_POSIX")
df.dateFormat = "MMM dd, yyyy"
df.timeZone = dateTimeZone
return df
}()
private var textBuffer: String = ""
private var nextArticle: ArticleInfo? = 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 = ArticleInfo()
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) // Cannot find 'articles' in scope
}
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)
}
}
Topic:
App & System Services
SubTopic:
General
Tags: