Final solution
Copy and paste this into a playground to test drive. If you want to learn how this all works check out this step by step tutorial
import UIKit
struct RSSPodcastItem {
var title: String
var description: String
var date: String
var category: [String]
// 1. Start off with an empty struct
init() {
self.title = ""
self.description = ""
self.date = ""
self.category = []
}
// 2. Reset all the properites
mutating func resetAllProperties() {
self = RSSPodcastItem()
}
}
class RSSPodcastParser: NSObject, XMLParserDelegate {
// What will be available to the consumer
var parsedData = [RSSPodcastItem]()
// ---------------------------------------------
private var xmlParser: XMLParser!
// we track currentElement so we know what element the XMLParserDelegate is currently on
private var currentElement = ""
// RSS Items that are tracked internally
private var rssItems = [RSSPodcastItem]()
// RSS Template object that is used to to build up our RSSItem
private var rssPodcastItem = RSSPodcastItem.init()
// 0. This is the function where we pass a URL and get a completion handler
func startParsingWithContentsOfURL(rssURL: URL, with completion: (Bool)-> ()) {
let parser = XMLParser(contentsOf: rssURL)
parser?.delegate = self
if let flag = parser?.parse() {
completion(flag)
}
}
// 1. This gets called when the opening elementName is the currentElement name == "item"
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
currentElement = elementName
if currentElement == "item" {
self.rssPodcastItem.resetAllProperties() // Starting a brand new item with all properties reset to ""
}
}
// 2. This gets parsed when the currentElement is found. currentElement got assigned in the method above
// - this going through word by word and adding them together into a sentence
func parser(_ parser: XMLParser, foundCharacters string: String) {
let string = string.trimmingCharacters(in: .whitespacesAndNewlines)
switch currentElement {
case "title":
self.rssPodcastItem.title += string
case "description":
self.rssPodcastItem.description += string
case "pubDate":
self.rssPodcastItem.date += string
case "category":
if (!string.isEmpty) {
self.rssPodcastItem.category.append(string)
}
default: break
}
}
// 3. Closing Tag. When the element tag ends
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
if elementName == "item" {
let item = self.rssPodcastItem
self.rssItems.append(item)
}
}
// 4. Notifies Delegate when completed. At this point we just assigned it the parsedData property
func parserDidEndDocument(_ parser: XMLParser) {
self.parsedData = self.rssItems
}
//5. Errors
func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
print(parseError.localizedDescription)
}
}
// ------------------------------- //
// Run the examples //
// ------------------------------- //
var model: [RSSPodcastItem]?
func parseRSSFeedSetUp() {
let feed = "https://rss.itunes.apple.com/api/v1/us/apple-music/coming-soon/all/10/explicit.rss"
let feedURL = URL.init(string: feed)
let parser = RSSPodcastParser()
parser.startParsingWithContentsOfURL(rssURL: feedURL!) { (success) in
if success {
model = parser.parsedData
print("Success")
} else {
print("Not successful")
}
}
}
// Run the function
parseRSSFeedSetUp()
// Review the model in the console
model?.forEach({ (item) in
print(item);
})
// 1. Run the playground
// 2. Check your console screen
console
You should see something like this in your console (see image)

error
If your getting “Not Successful” in your console it could be that feedURL is not working anymore. Just verify it works.