Skip to content

Instantly share code, notes, and snippets.

@norsez
Last active April 30, 2018 02:38
Show Gist options
  • Save norsez/76f3a830b9f7f4da5be3b0439cf63520 to your computer and use it in GitHub Desktop.
Save norsez/76f3a830b9f7f4da5be3b0439cf63520 to your computer and use it in GitHub Desktop.
Medium.com RSS XML parser written in Swift
// Created by norsez on 30/4/18.
import Foundation
struct RssItem {
var title = "unknown"
var categories: [String] = []
var pubDate = "unknown"
var link = "http://"
var content = "<h1>Hello World</h1>"
var creator = "n.a."
}
struct RssFeed {
var title = "unknown"
var description = "n.a."
var link = "http://"
var icon = "http://"
var items: [RssItem] = []
}
class DevMediumRSSXMLParser: NSObject, XMLParserDelegate {
var stringBuf = ""
var cdataBuf = ""
var feed = RssFeed()
var currentPathStack = [String]()
var currentItem: RssItem?
//MARK: Tags
struct Tag {
static let RSS = "rss"
static let Channel = "channel"
static let Title = "title"
static let Description = "description"
static let Link = "link"
static let Image = "image"
static let Url = "url"
static let ContentEncoded = "content:encoded"
static let AtomUpdated = "atom:updated"
static let Category = "category"
static let DcCreator = "dc:creator"
static let Item = "item"
static let path_feed_title = [Tag.RSS, Tag.Channel, Tag.Title]
static let path_feed_desc = [Tag.RSS, Tag.Channel, Tag.Description]
static let path_feed_link = [Tag.RSS, Tag.Channel, Tag.Link]
static let path_feed_icon = [Tag.RSS, Tag.Channel, Image, Tag.Url]
static let path_feed_item = [Tag.RSS, Tag.Channel, Tag.Item]
static let path_feed_item_title = [Tag.RSS, Tag.Channel, Tag.Item, Tag.Title]
static let path_feed_item_link = [Tag.RSS, Tag.Channel, Tag.Item, Tag.Link]
static let path_feed_item_category = [Tag.RSS, Tag.Channel, Tag.Item, Tag.Category]
static let path_feed_item_creator = [Tag.RSS, Tag.Channel, Tag.Item, Tag.DcCreator]
static let path_feed_item_createdDate = [Tag.RSS, Tag.Channel, Tag.Item, Tag.AtomUpdated]
static let path_feed_item_content = [Tag.RSS, Tag.Channel, Tag.Item, Tag.ContentEncoded]
//Convenience method for checking the equality of two path stacks
static func pathEquals(path1: [String], path2: [String]) -> Bool {
if path1.count != path2.count {
return false
}
for index in stride(from: path1.count-1, to: 0, by: -1) {
if path1[index] != path2[index] {
return false
}
}
return true
}
}
//MARK: parser method
func processParsing() {
if Tag.pathEquals(path1: Tag.path_feed_title, path2: self.currentPathStack) {
self.feed.title = self.cdataBuf
}else if Tag.pathEquals(path1: Tag.path_feed_desc, path2: self.currentPathStack) {
self.feed.description = self.cdataBuf
}else if Tag.pathEquals(path1: Tag.path_feed_link, path2: self.currentPathStack) {
self.feed.link = self.stringBuf
}else if Tag.pathEquals(path1: Tag.path_feed_icon, path2: self.currentPathStack) {
self.feed.icon = self.stringBuf
}else if Tag.pathEquals(path1: Tag.path_feed_title, path2: self.currentPathStack) {
self.feed.title = self.cdataBuf
}else if Tag.pathEquals(path1: Tag.path_feed_item_title, path2: self.currentPathStack) {
self.currentItem?.title = self.cdataBuf
}else if Tag.pathEquals(path1: Tag.path_feed_item_link, path2: self.currentPathStack) {
self.currentItem?.link = self.stringBuf
}else if Tag.pathEquals(path1: Tag.path_feed_item_category, path2: self.currentPathStack) {
self.currentItem?.categories.append(self.cdataBuf)
}else if Tag.pathEquals(path1: Tag.path_feed_item_createdDate, path2: self.currentPathStack) {
self.currentItem?.pubDate = self.stringBuf
}else if Tag.pathEquals(path1: Tag.path_feed_item_creator, path2: self.currentPathStack) {
self.currentItem?.creator = self.cdataBuf
}else if Tag.pathEquals(path1: Tag.path_feed_item_content, path2: self.currentPathStack) {
self.currentItem?.content = self.cdataBuf
}
}
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
self.currentPathStack.append(elementName)
if elementName == Tag.Item {
self.currentItem = RssItem()
}
}
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
processParsing()
//buffer clean up
if let lastElem = self.currentPathStack.popLast() {
if lastElem != elementName {
Logger.log(withText: "warning: unmatched element stack popped \(lastElem) Vs. actual: \(elementName)")
}
}
self.stringBuf = ""
self.cdataBuf = ""
if elementName == Tag.Item {
if let item = self.currentItem {
self.feed.items.append(item)
self.currentItem = nil
}
}
}
func parser(_ parser: XMLParser, foundCDATA CDATABlock: Data) {
if let cdblock = NSString(data: CDATABlock, encoding: String.Encoding.utf8.rawValue) as String?{
self.cdataBuf.append(contentsOf: cdblock)
}
}
func parser(_ parser: XMLParser, foundCharacters string: String) {
self.stringBuf.append(contentsOf: string)
}
func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
Logger.log(withErrorText: parseError.localizedDescription)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment