(Expansion) Call a Class into views

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:
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)
            })
        }
    }
}

Accepted Reply

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()
//...
}
}


Replies

Do you remember I proposed to create an ObservableObject CardsInfo, which has an @Published array and a method called loadCards().

You can do it in the same way:
Code Block
class ArticlesInfo: ObservableObject {
@Published var articles: ...
//...
func loadArticles() {
// Use `ArticlesParser` here
//...
}
}


You can use it as in your CardsView.
Sorry for the late reply.
A bit confused of how I'm supposed to use ArticlesParser in the ObservableObject. Here's the code so if I missed something, it's here:
Code Block
class ArticlesInfo: ObservableObject {
    @Published var articles: [Article] = []
    
    func loadArticles() {
        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: Article? = nil
            override init(data: Data) {
                super.init(data: data)
                self.delegate = self // Cannot assign value of type 'ArticlesParser' to type 'XMLParserDelegate?'
            }
        }
        extension ArticlesParser: XMLParserDelegate { // Declaration is only valid at file scope
            
            // 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) // 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)
            }
        }
    }
}

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()
//...
}
}


You have no need to (and should not, in this case) make ArticlesParser a local class nested in a function.

Good to know. I just need to know where to put this. I will make this a separate thread because this is pretty much the parser I needed.

Edit:
New thread: https://developer.apple.com/forums/thread/675586