Press "Enter" to skip to content

Create a RSS Reader in Swift

Mani Gopalakrishnan 0

The following steps are intended to let you create your own RSS Reader using Swift.

Create a class called RssItem

The RssItem is a class and each of the Rss XML attribute is a property of the class and save it as RssItem.swift.

class RssItem : NSObject {

var title:String = "";
var link:String = "";
var commentslink:String = "";
var pubdate:String = "";
var creator:String = "";
var categories:AnyObject = NSMutableArray();
var images:AnyObject = NSMutableArray();
var guid:String="";
var excerpt:String="";
var encodedcontent:String="";
var commentsrssfeedlink:String="";
var commentcount:String="";

var currentlyparsing:String="";

override init() {
super.init();
}

}

Create a class called RssItemManager

The RssItemManager is truly the parser. It loads the content from any remote URL (or) document & then parses it. Save this as RssItemManager.swift

import Foundation

class RssItemManager : NSObject, NSXMLParserDelegate {

//enum tags easier to run comparison of tag names if you want to differentiate behaviors
enum RSSTAGNAMES : String{

case ITEMTAG = "item"
case TITLETAG = "title"
case LINKTAG = "link"
case COMMENTSLINKTAG = "comments"
case PUBDATETAG = "pubDate"
case CREATORTAG = "dc:creator"
case CATEGORYTAG = "category"
case GUIDTAG = "guid"
case EXCERPTTAG = "description"
case ENCODEDCONTENTTAG = "content:encoded"
case COMMENTRSSFEEDLINKTAG = "wfw:commentRss"
case COMMENTSCOUNTTAG = "slash:comments"

};

var items:NSMutableArray = NSMutableArray();//stores all the items
var item:RssItem = RssItem();//one item
var currentlyparsing:String="";
var delegate:RssItemManagerDelegate?; //Delegate (see steps below)

init(paramurlstring: String, paramdelegate:RssItemManagerDelegate?) {

//function that starts the parsing process...
super.init();
self.delegate=paramdelegate;

var url:NSURL=NSURL(string:paramurlstring)!;
let parserobject:NSXMLParser=NSXMLParser(contentsOfURL: url)!;

parserobject.shouldResolveExternalEntities=true;
parserobject.shouldProcessNamespaces=true;
parserobject.shouldReportNamespacePrefixes=true;

parserobject.delegate=self;
parserobject.parse();
}

func parserDidStartDocument(_parser: NSXMLParser){
//NSXMLParser Delegate function called when the parsing is started
}
func parserDidEndDocument(_parser: NSXMLParser){
//NSXMLParser Delegate function called when the parsing is complete
self.delegate?.parsingCompletedSuccessfully(items);

}
func parser(_parser: NSXMLParser,didStartElement elementName: String, namespaceURI: String?, qualifiedName: String?,
attributes attributeDict: [NSObject : AnyObject]){

//NSXMLParser Delegate function called when it starts parsing an element
println(elementName);
if (qualifiedName==RSSTAGNAMES.ITEMTAG.rawValue){
self.item=RssItem();
}
self.currentlyparsing=qualifiedName!;

}
func parser(_parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?,qualifiedName qName: String?){

//NSXMLParser Delegate function called when it ends parsing an element

if (qName==RSSTAGNAMES.ITEMTAG.rawValue){
self.items.addObject(item);
}
if (qName==RSSTAGNAMES.ENCODEDCONTENTTAG.rawValue){
self.extractImages(self.item.encodedcontent);
}

}
func parser(_parser: NSXMLParser, parseErrorOccurred parseError: NSError){
//NSXMLParser Delegate function called when a parsing error occurs

}
func parser(_parser: NSXMLParser,foundCharacters string: String?){
println(string);

//NSXMLParser Delegate function called when an element is being parsed and it has text nodes.
let string=string!.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
if (self.currentlyparsing==RSSTAGNAMES.TITLETAG.rawValue){
self.item.title+=string;
}
if (self.currentlyparsing==RSSTAGNAMES.COMMENTSLINKTAG.rawValue){
self.item.commentslink+=string;
}
if (self.currentlyparsing==RSSTAGNAMES.PUBDATETAG.rawValue){
//self.item.pubdate+=string;
var tempDateFormatter : NSDateFormatter = NSDateFormatter();
tempDateFormatter.timeZone=NSTimeZone.systemTimeZone();
tempDateFormatter.locale=NSLocale.currentLocale();
tempDateFormatter.dateFormat="EEE, dd MMMM yyyy HH:mm:ss Z";
tempDateFormatter.formatterBehavior=NSDateFormatterBehavior.BehaviorDefault;
let tempDate=tempDateFormatter.dateFromString(string);

if !(tempDate==nil){
self.item.pubdate=tempDateFormatter.stringFromDate(tempDate!);
}

}
if (self.currentlyparsing==RSSTAGNAMES.LINKTAG.rawValue){
self.item.link+=string;
}
if (self.currentlyparsing==RSSTAGNAMES.CREATORTAG.rawValue){
self.item.creator+=string;
}
if (self.currentlyparsing==RSSTAGNAMES.CATEGORYTAG.rawValue){
self.item.categories.addObject(string);
}
if (self.currentlyparsing==RSSTAGNAMES.GUIDTAG.rawValue){
self.item.guid+=string;
}
// if (self.currentlyparsing==RSSTAGNAMES.EXCERPTTAG.toRaw()){
// self.item.excerpt+=string;
// }
if (self.currentlyparsing==RSSTAGNAMES.ENCODEDCONTENTTAG.rawValue){
self.item.encodedcontent+=string;
}
if (self.currentlyparsing==RSSTAGNAMES.COMMENTRSSFEEDLINKTAG.rawValue){
self.item.commentsrssfeedlink+=string;
}
if (self.currentlyparsing==RSSTAGNAMES.COMMENTSCOUNTTAG.rawValue){
self.item.commentcount+=string;
}

}
func parser(_parser: NSXMLParser, foundCDATA CDATABlock: NSData){

//NSXMLParser Delegate function called when the parser encounters a CDATABlock
// self.delegate.parsingError(nil : NSError);
if (self.currentlyparsing==RSSTAGNAMES.ENCODEDCONTENTTAG.rawValue){
let tempString=NSString(data: CDATABlock, encoding: NSUTF8StringEncoding) as! String;
self.item.encodedcontent+=tempString;
// self.item.encodedcontent=self.item.encodedcontent + tempString;
// NSString *someString = [[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding];

}
if (self.currentlyparsing==RSSTAGNAMES.EXCERPTTAG.rawValue){
let tempString=NSString(data: CDATABlock, encoding: NSUTF8StringEncoding) as! String;
self.item.excerpt+=tempString;
println(self.item.excerpt);
// NSString *someString = [[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding];

}

}

func extractImages(paramhtml: NSString){
//You really don't need this function. However, since this was being written to parse a WordPress RSS Feed - I sometimes encountered a need to extract images from <img> tag within the <content> cDATA block. Your call to use it or not.

var stop:Bool;

let regex=NSRegularExpression(pattern: "(<imgs[sS]*?srcs*?=s*?['"](.*?)['"][sS]*?>)+?", options: NSRegularExpressionOptions.CaseInsensitive, error: nil);

let nsRange : NSRange = NSRange(location: 0, length: paramhtml.length)
var result : NSTextCheckingResult;

regex!.enumerateMatchesInString(paramhtml as String, options: NSMatchingOptions.ReportCompletion, range: nsRange) { (result, NSMatchingFlags flags, stop) -> Void in
if (result==nil){

}
else{
let img=paramhtml.substringWithRange(result.rangeAtIndex(2));
self.item.images.addObject(img);
println(img);
}

};

}

}

Create a class called RssItemManagerDelegate

The RssItemManagerDelegate is called when the ItemManager completes parsing either successfully or encounters errors. This delegate is used by a client using these classes to inform itself. Save this as RssItemManagerDelegate.swift

import Foundation

protocol RssItemManagerDelegate{

func parsingCompletedSuccessfully(paramObject: NSMutableArray);
func parsingError(paramError:NSError);

}

Making it all work

After you have created all these Swift files in your xCode project files, you can simply make it all work by invoking the RssItemManager. Please note that the class invoking this function should subscribe to the RssItemManagerDelegate.

You can insert the following piece of code into your app logic.

let rssitemManager:RssItemManager=RssItemManager(paramurlstring: self.urlstring!,paramdelegate:self);

You must then also insert appropriate logic into the delegate functions listed below:

func parsingCompletedSuccessfully(paramObject:NSMutableArray){
println("success");
}
func parsingError(paramError:NSError){
println("error");
}

Where can I find the files?

Click here to find them on github or using the following link to get to it: https://github.com/SmartdotWorks/RssItemManager

Credits: Image sourced under creative commons. Click here for the original source.

Leave a Reply

%d bloggers like this: