Skip to content
Browse files

Initial commit

  • Loading branch information...
0 parents commit ed810b051c43cbd64496cbec1a01c8f4733c03af @mwaterfall mwaterfall committed May 16, 2010
24 .gitignore
@@ -0,0 +1,24 @@
+# Ignore some Xcode auto-generated files
+build/
+*~.nib/
+
+# Ignore user specific Xcode files
+# As only 1 developer, don't ignore them (breakpoints, env. vars etc)
+#*.perspective
+#*.perspectivev3
+#*.pbxuser
+#*.mode1v3
+#*.mode2v3
+
+# OS X noise
+.DS_Store
+profile
+
+# Backups created by text editors
+*~
+*.bak
+
+# Object/generated files
+*.o
+*.pyc
+*.pyo
23 Classes/MWFeedInfo.h
@@ -0,0 +1,23 @@
+//
+// MWFeedInfo.h
+// XML
+//
+// Created by Michael Waterfall on 10/05/2010.
+// Copyright 2010 d3i. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface MWFeedInfo : NSObject {
+
+ NSString *title;
+ NSString *link;
+ NSString *summary;
+
+}
+
+@property (nonatomic, copy) NSString *title;
+@property (nonatomic, copy) NSString *link;
+@property (nonatomic, copy) NSString *summary;
+
+@end
23 Classes/MWFeedInfo.m
@@ -0,0 +1,23 @@
+//
+// MWFeedInfo.m
+// XML
+//
+// Created by Michael Waterfall on 10/05/2010.
+// Copyright 2010 d3i. All rights reserved.
+//
+
+#import "MWFeedInfo.h"
+
+@implementation MWFeedInfo
+
+@synthesize title, link, summary;
+
+- (NSString *)description {
+ NSMutableString *string = [[NSMutableString alloc] initWithString:@"\nMWFeedInfo\n"];
+ if (title) [string appendFormat:@"Title: %@\n", title];
+ if (link) [string appendFormat:@"Link: %@\n", link];
+ if (summary) [string appendFormat:@"Summary: %@", summary];
+ return [string autorelease];
+}
+
+@end
27 Classes/MWFeedItem.h
@@ -0,0 +1,27 @@
+//
+// MWFeedItem.h
+// XML
+//
+// Created by Michael Waterfall on 10/05/2010.
+// Copyright 2010 d3i. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface MWFeedItem : NSObject {
+
+ NSString *title;
+ NSString *link;
+ NSString *summary; // Description of item
+ NSString *content; // More detailed content (if available)
+ NSDate *date;
+
+}
+
+@property (nonatomic, copy) NSString *title;
+@property (nonatomic, copy) NSString *link;
+@property (nonatomic, copy) NSString *summary;
+@property (nonatomic, copy) NSString *content;
+@property (nonatomic, copy) NSDate *date;
+
+@end
25 Classes/MWFeedItem.m
@@ -0,0 +1,25 @@
+//
+// MWFeedItem.m
+// XML
+//
+// Created by Michael Waterfall on 10/05/2010.
+// Copyright 2010 d3i. All rights reserved.
+//
+
+#import "MWFeedItem.h"
+
+@implementation MWFeedItem
+
+@synthesize title, link, summary, content, date;
+
+- (NSString *)description {
+ NSMutableString *string = [[NSMutableString alloc] initWithString:@"\nMWFeedItem\n"];
+ if (title) [string appendFormat:@"Title: %@\n", title];
+ if (link) [string appendFormat:@"Link: %@\n", link];
+ if (date) [string appendFormat:@"Date: %@\n", date];
+ if (summary) [string appendFormat:@"Summary: %@\n", summary];
+ if (content) [string appendFormat:@"Content: %@", content];
+ return [string autorelease];
+}
+
+@end
101 Classes/MWFeedParser.h
@@ -0,0 +1,101 @@
+//
+// MWFeedParser.h
+// XML
+//
+// Created by Michael Waterfall on 08/05/2010.
+// Copyright 2010 d3i. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "MWFeedInfo.h"
+#import "MWFeedItem.h"
+
+// Debug Logging
+#if 0
+#define MWLog(x, ...) NSLog(x, ## __VA_ARGS__);
+#else
+#define MWLog(x, ...)
+#endif
+
+// Class
+@class MWFeedParser;
+
+// Types
+typedef enum { ConnectionTypeAsynchronously, ConnectionTypeSynchronously } ConnectionType;
+typedef enum { ParseTypeFull, ParseTypeItemsOnly, ParseTypeInfoOnly } ParseType;
+typedef enum { FeedTypeUnknown, FeedTypeRSS, FeedTypeAtom } FeedType;
+
+// Delegate
+@protocol MWFeedParserDelegate <NSObject>
+@optional
+- (void)feedParserDidStart:(MWFeedParser *)parser;
+- (void)feedParser:(MWFeedParser *)parser didParseFeedInfo:(MWFeedInfo *)info;
+- (void)feedParser:(MWFeedParser *)parser didParseFeedItem:(MWFeedItem *)item;
+- (void)feedParserDidFinish:(MWFeedParser *)parser;
+- (void)feedParser:(MWFeedParser *)parser didFailWithError:(NSError *)error;
+@end
+
+// Class
+@interface MWFeedParser : NSObject {
+
+ // Required
+ id <MWFeedParserDelegate> delegate;
+ NSString *url;
+
+ // Connection
+ NSURLConnection *urlConnection;
+ NSMutableData *asyncData;
+ ConnectionType connectionType;
+
+ // Parsing
+ ParseType feedParseType;
+ NSXMLParser *feedParser;
+ FeedType feedType;
+ NSDateFormatter *dateFormatterRFC822, *dateFormatterRFC3339;
+ BOOL hasEncounteredItems; // Whether the parser has started parsing items
+ BOOL aborted; // Whether parse stopped due to abort
+
+ // Parsing Data
+ NSString *currentPath;
+ NSMutableString *currentText;
+ NSDictionary *currentElementAttributes;
+ MWFeedItem *item;
+ MWFeedInfo *info;
+
+}
+
+// Properties
+@property (nonatomic, assign) id <MWFeedParserDelegate> delegate;
+@property (nonatomic, copy) NSString *url;
+
+// Feed Downloading Properties
+@property (nonatomic, retain) NSURLConnection *urlConnection;
+@property (nonatomic, retain) NSMutableData *asyncData;
+@property (nonatomic) ConnectionType connectionType;
+
+// Parsing Properties
+@property (nonatomic) ParseType feedParseType;
+@property (nonatomic, retain) NSXMLParser *feedParser;
+@property (nonatomic, retain) NSString *currentPath;
+@property (nonatomic, retain) NSMutableString *currentText;
+@property (nonatomic, retain) NSDictionary *currentElementAttributes;
+@property (nonatomic, retain) MWFeedItem *item;
+@property (nonatomic, retain) MWFeedInfo *info;
+
+// NSObject Methods
+- (id)initWithFeedURL:(NSString *)feedURL;
+
+// Parsing Methods
+- (void)reset;
+- (void)parse;
+- (void)startParsingData:(NSData *)data;
+
+// Misc
+- (void)finishParsing;
+- (NSString *)linkFromAtomLinkAttributes:(NSDictionary *)attributes;
+
+// Dates
+- (NSDate *)dateFromRFC822String:(NSString *)dateString;
+- (NSDate *)dateFromRFC3339String:(NSString *)dateString;
+
+@end
469 Classes/MWFeedParser.m
@@ -0,0 +1,469 @@
+//
+// MWFeedParser.m
+// XML
+//
+// Created by Michael Waterfall on 08/05/2010.
+// Copyright 2010 d3i. All rights reserved.
+//
+
+#import "MWFeedParser.h"
+#import "NSString+XMLEntities.h"
+
+@implementation MWFeedParser
+
+// Properties
+@synthesize delegate, url;
+@synthesize urlConnection, asyncData, connectionType;
+@synthesize feedParseType, feedParser, currentPath, currentText, currentElementAttributes, item, info;
+
+#pragma mark -
+#pragma mark NSObject
+
+- (id)initWithFeedURL:(NSString *)feedURL {
+ if (self = [super init]) {
+
+ // URL
+ self.url = [feedURL stringByReplacingOccurrencesOfString:@"feed://" withString:@"http://"];
+
+ // Defaults
+ feedParseType = ParseTypeFull;
+ connectionType = ConnectionTypeSynchronously;
+
+ // Date Formatters
+ NSLocale *en_US_POSIX = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
+ dateFormatterRFC822 = [[NSDateFormatter alloc] init];
+ dateFormatterRFC3339 = [[NSDateFormatter alloc] init];
+ [dateFormatterRFC822 setLocale:en_US_POSIX];
+ [dateFormatterRFC3339 setLocale:en_US_POSIX];
+ [dateFormatterRFC822 setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
+ [dateFormatterRFC3339 setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
+ [en_US_POSIX release];
+
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [urlConnection release];
+ [url release];
+ [feedParser release];
+ [dateFormatterRFC822 release];
+ [dateFormatterRFC3339 release];
+ [currentPath release];
+ [currentText release];
+ [currentElementAttributes release];
+ [item release];
+ [info release];
+ [super dealloc];
+}
+
+#pragma mark -
+#pragma mark Parsing
+
+// Reset data variables before processing
+- (void)reset {
+ self.asyncData = nil;
+ self.feedParser = nil;
+ self.urlConnection = nil;
+ feedType = FeedTypeUnknown;
+ self.currentPath = @"/";
+ self.currentText = [[NSMutableString alloc] init];
+ self.item = nil;
+ self.info = nil;
+ hasEncounteredItems = NO;
+ aborted = NO;
+ self.currentElementAttributes = nil;
+}
+
+// Begin downloading & parsing of feed
+- (void)parse {
+
+ // Checks
+ if (!url || !delegate) {
+
+ // Error
+ return;
+
+ }
+
+ // Reset
+ [self reset];
+
+ // Request
+ NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]
+ cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
+ timeoutInterval:180];
+ [request setValue:@"MWFeedParser" forHTTPHeaderField:@"User-Agent"];
+
+ // Connection
+ if (connectionType == ConnectionTypeAsynchronously) {
+
+ // Async
+ urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
+ if (urlConnection) {
+
+ // Create data
+ asyncData = [[NSMutableData alloc] init];
+
+ } else {
+
+ // Error
+
+
+ }
+
+ } else {
+
+ // Sync
+ NSURLResponse *response = nil;
+ NSError *error = nil;
+ NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
+ if (data && !error) {
+
+ // Process
+ [self startParsingData:data];
+
+ } else {
+
+ // Error
+
+
+ }
+
+ }
+
+ // Cleanup
+ [request release];
+
+}
+
+// Begin XML parsing
+- (void)startParsingData:(NSData *)data {
+ if (data && !feedParser) {
+
+ // Create feed info
+ MWFeedInfo *i = [[MWFeedInfo alloc] init];
+ self.info = i;
+ [i release];
+
+ // Parse!
+ feedParser = [[NSXMLParser alloc] initWithData:data];
+ feedParser.delegate = self;
+ [feedParser parse];
+
+ }
+}
+
+#pragma mark -
+#pragma mark NSURLConnection Delegate
+
+- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
+ [asyncData setLength:0];
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
+ [asyncData appendData:data];
+}
+
+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
+
+ // Failed
+ [urlConnection release];
+ [asyncData release];
+
+ // inform the user
+ MWLog(@"Connection failed! Error - %@ %@", [error localizedDescription], [[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
+
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
+
+ // Succeed
+ MWLog(@"Succeeded! Received %d bytes of data", [asyncData length]);
+ [self startParsingData:asyncData];
+
+ // Cleanup
+ [urlConnection release];
+ [asyncData autorelease];
+
+}
+
+-(NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
+ return nil; // Don't cache
+}
+
+#pragma mark -
+#pragma mark XML Parsing
+
+- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
+ MWLog(@"XMLParser: didStartElement: %@", elementName);
+
+ // Adjust path
+ self.currentPath = [currentPath stringByAppendingPathComponent:elementName];
+ self.currentElementAttributes = attributeDict;
+ [self.currentText setString:@""];
+
+ // Determine feed type
+ if (feedType == FeedTypeUnknown) {
+ if ([elementName isEqualToString:@"rss"]) feedType = FeedTypeRSS;
+ else if ([elementName isEqualToString:@"feed"]) feedType = FeedTypeAtom;
+ return;
+ }
+
+ // Entering new feed element
+ if (feedParseType != ParseTypeItemsOnly) { // Check whether to ignore feed info
+ if ((feedType == FeedTypeRSS && [currentPath isEqualToString:@"/rss/channel"]) ||
+ (feedType == FeedTypeAtom && [currentPath isEqualToString:@"/feed"])) {
+ return;
+ }
+ }
+
+ // Entering new item element
+ if ((feedType == FeedTypeRSS && [currentPath isEqualToString:@"/rss/channel/item"]) ||
+ (feedType == FeedTypeAtom && [currentPath isEqualToString:@"/feed/entry"])) {
+
+ // Send off feed info to delegate
+ if (feedParseType != ParseTypeItemsOnly) { // Check whether to ignore feed info
+ if (!hasEncounteredItems) {
+
+ // Inform delegate
+ if ([delegate respondsToSelector:@selector(feedParser:didParseFeedInfo:)])
+ [delegate feedParser:self didParseFeedInfo:[[info retain] autorelease]];
+
+ // Finish
+ self.info = nil;
+ hasEncounteredItems = YES;
+
+ // Stop parsing if only requiring meta data
+ if (feedParseType == ParseTypeInfoOnly) {
+
+ // Finish
+ [self finishParsing];
+
+ }
+
+ }
+ }
+
+ // New item
+ self.item = [[MWFeedItem alloc] init];
+ return;
+ }
+
+}
+
+- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
+ MWLog(@"XMLParser: didEndElement: %@", elementName);
+
+ // Store data
+ BOOL processed = NO;
+ if (currentText) {
+
+ // Decode entities
+ //[currentText setString:[currentText stringByDecodingXMLEntities]];
+
+ // Use
+ switch (feedType) {
+ case FeedTypeRSS: {
+
+ // Item
+ if (!processed) {
+ if ([currentPath isEqualToString:@"/rss/channel/item/title"]) { if (currentText.length > 0) item.title = currentText; processed = YES; }
+ else if ([currentPath isEqualToString:@"/rss/channel/item/link"]) { if (currentText.length > 0) item.link = currentText; processed = YES; }
+ else if ([currentPath isEqualToString:@"/rss/channel/item/description"]) { if (currentText.length > 0) item.summary = currentText; processed = YES; }
+ else if ([currentPath isEqualToString:@"/rss/channel/item/content:encoded"]) { if (currentText.length > 0) item.content = currentText; processed = YES; }
+ else if ([currentPath isEqualToString:@"/rss/channel/item/pubDate"]) {
+ if (currentText.length > 0)
+ item.date = [self dateFromRFC822String:currentText];
+ processed = YES; }
+ }
+
+ // Info
+ if (!processed && feedParseType != ParseTypeItemsOnly) {
+ if ([currentPath isEqualToString:@"/rss/channel/title"]) { if (currentText.length > 0) info.title = currentText; processed = YES; }
+ else if ([currentPath isEqualToString:@"/rss/channel/description"]) { if (currentText.length > 0) info.summary = currentText; processed = YES; }
+ else if ([currentPath isEqualToString:@"/rss/channel/link"]) { if (currentText.length > 0) info.link = currentText; processed = YES; }
+ }
+
+ break;
+ }
+ case FeedTypeAtom: {
+
+ // Item
+ if (!processed) {
+ if ([currentPath isEqualToString:@"/feed/entry/title"]) { if (currentText.length > 0) item.title = currentText; processed = YES; }
+ else if ([currentPath isEqualToString:@"/feed/entry/link"]) {
+ NSString *link = [self linkFromAtomLinkAttributes:currentElementAttributes];
+ if (link) item.link = link;
+ processed = YES;
+ }
+ else if ([currentPath isEqualToString:@"/feed/entry/summary"]) { if (currentText.length > 0) item.summary = currentText; processed = YES; }
+ else if ([currentPath isEqualToString:@"/feed/entry/content"]) { if (currentText.length > 0) item.content = currentText; processed = YES; }
+ else if ([currentPath isEqualToString:@"/feed/entry/published"]) { if (currentText.length > 0) item.date = [self dateFromRFC3339String:currentText]; processed = YES; }
+ }
+
+ // Info
+ if (!processed && feedParseType != ParseTypeItemsOnly) {
+ if ([currentPath isEqualToString:@"/feed/title"]) { if (currentText.length > 0) info.title = currentText; processed = YES; }
+ else if ([currentPath isEqualToString:@"/feed/description"]) { if (currentText.length > 0) info.summary = currentText; processed = YES; }
+ else if ([currentPath isEqualToString:@"/feed/link"]) {
+ NSString *link = [self linkFromAtomLinkAttributes:currentElementAttributes];
+ if (link) info.link = link;
+ processed = YES;
+ }
+ }
+
+ break;
+ }
+ }
+ }
+
+ // Adjust path
+ self.currentPath = [currentPath stringByDeletingLastPathComponent];
+
+ // If end of an item then tell delegate
+ if (!processed) {
+ if ((feedType == FeedTypeRSS && [elementName isEqualToString:@"item"]) ||
+ (feedType == FeedTypeAtom && [elementName isEqualToString:@"entry"])) {
+
+ // Ensure summary always contains data if available
+ if (!item.summary) { item.summary = item.content; item.content = nil; }
+
+ // Inform delegate
+ if ([delegate respondsToSelector:@selector(feedParser:didParseFeedItem:)])
+ [delegate feedParser:self didParseFeedItem:[[item retain] autorelease]];
+
+ // Finish
+ self.item = nil;
+
+ }
+ }
+
+}
+
+- (void)parser:(NSXMLParser *)parser foundAttributeDeclarationWithName:(NSString *)attributeName forElement:(NSString *)elementName type:(NSString *)type defaultValue:(NSString *)defaultValue {
+ MWLog(@"XMLParser: foundAttributeDeclarationWithName: %@", attributeName);
+}
+
+- (void)parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock {
+ MWLog(@"XMLParser: foundCDATA: [DATA]");
+
+ // Remember characters
+ NSString *string = nil;
+ @try {
+ string = [[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding];
+ if (!string) string = [[NSString alloc] initWithData:CDATABlock encoding:NSISOLatin1StringEncoding];
+ if (string) [currentText appendString:string];
+ }
+ @catch (NSException * e) {
+ }
+ @finally {
+ [string release];
+ }
+
+}
+
+- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
+ MWLog(@"XMLParser: foundCharacters: %@", string);
+
+ // Remember characters
+ [currentText appendString:string];
+
+}
+
+- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
+ MWLog(@"XMLParser: parseErrorOccurred: %@", parseError);
+
+ // Called if failed or aborted (legally)
+ if (!aborted) {
+
+ // Inform delegate
+ if ([delegate respondsToSelector:@selector(feedParser:didFailWithError:)])
+ [delegate feedParser:self didFailWithError:parseError];
+
+ }
+
+}
+
+- (void)parser:(NSXMLParser *)parser validationErrorOccurred:(NSError *)validError {
+ MWLog(@"XMLParser: validationErrorOccurred: %@", validError);
+
+ // Inform delegate
+ if ([delegate respondsToSelector:@selector(feedParser:didFailWithError:)])
+ [delegate feedParser:self didFailWithError:validError];
+
+}
+
+- (void)parserDidEndDocument:(NSXMLParser *)parser {
+ MWLog(@"XMLParser: parserDidEndDocument");
+
+ // Inform delegate
+ if ([delegate respondsToSelector:@selector(feedParserDidFinish:)])
+ [delegate feedParserDidFinish:self];
+
+}
+
+- (void)parserDidStartDocument:(NSXMLParser *)parser {
+ MWLog(@"XMLParser: parserDidStartDocument");
+
+ // Inform delegate
+ if ([delegate respondsToSelector:@selector(feedParserDidStart:)])
+ [delegate feedParserDidStart:self];
+
+}
+
+#pragma mark -
+#pragma mark Misc
+
+- (void)finishParsing {
+
+ // Abort
+ aborted = YES;
+ [feedParser abortParsing];
+
+ // Inform delegate of finish
+ if ([delegate respondsToSelector:@selector(feedParserDidFinish:)])
+ [delegate feedParserDidFinish:self];
+
+}
+
+// Determine whether to use the link from atom feed (where rel is not set to "self" etc...)
+- (NSString *)linkFromAtomLinkAttributes:(NSDictionary *)attributes {
+ if (attributes && [attributes objectForKey:@"rel"] && [[attributes objectForKey:@"rel"] isEqualToString:@"alternate"]) {
+ return [attributes objectForKey:@"href"];
+ }
+ return nil;
+}
+
+- (NSDate *)dateFromRFC822String:(NSString *)dateString {
+ NSDate *date = nil;
+ if (!date) { // Sun, 19 May 02 15:21:36 GMT
+ [dateFormatterRFC822 setDateFormat:@"EEE, d MMM yy HH:mm:ss zzz"];
+ date = [dateFormatterRFC822 dateFromString:dateString];
+ }
+ if (!date) { // Sun, 19 May 2002 15:21:36 GMT
+ [dateFormatterRFC822 setDateFormat:@"EEE, d MMM yyyy HH:mm:ss zzz"];
+ date = [dateFormatterRFC822 dateFromString:dateString];
+ }
+ if (!date) { // Sun, 19 May 2002 15:21 GMT
+ [dateFormatterRFC822 setDateFormat:@"EEE, d MMM yyyy HH:mm zzz"];
+ date = [dateFormatterRFC822 dateFromString:dateString];
+ }
+ return date;
+}
+
+- (NSDate *)dateFromRFC3339String:(NSString *)dateString {
+ NSDate *date = nil;
+ dateString = [dateString stringByReplacingOccurrencesOfString:@"Z" withString:@"-0000"];
+ if (!date) { // 1996-12-19T16:39:57-08:00
+ [dateFormatterRFC3339 setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ssZZZ"];
+ date = [dateFormatterRFC3339 dateFromString:dateString];
+ }
+ if (!date) { // 1937-01-01T12:00:27.87+00:20
+ [dateFormatterRFC3339 setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSZZZ"];
+ date = [dateFormatterRFC3339 dateFromString:dateString];
+ }
+ return date;
+}
+
+@end
21 Classes/MWFeedParserAppDelegate.h
@@ -0,0 +1,21 @@
+//
+// MWFeedParserAppDelegate.h
+// MWFeedParser
+//
+// Created by Michael Waterfall on 15/05/2010.
+// Copyright d3i 2010. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface MWFeedParserAppDelegate : NSObject <UIApplicationDelegate> {
+
+ UIWindow *window;
+ UINavigationController *navigationController;
+}
+
+@property (nonatomic, retain) IBOutlet UIWindow *window;
+@property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
+
+@end
+
44 Classes/MWFeedParserAppDelegate.m
@@ -0,0 +1,44 @@
+//
+// MWFeedParserAppDelegate.m
+// MWFeedParser
+//
+// Created by Michael Waterfall on 15/05/2010.
+// Copyright d3i 2010. All rights reserved.
+//
+
+#import "MWFeedParserAppDelegate.h"
+#import "RootViewController.h"
+
+@implementation MWFeedParserAppDelegate
+
+@synthesize window;
+@synthesize navigationController;
+
+#pragma mark -
+#pragma mark Application lifecycle
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ // Override point for customization after app launch
+
+ [window addSubview:[navigationController view]];
+ [window makeKeyAndVisible];
+ return YES;
+}
+
+
+- (void)applicationWillTerminate:(UIApplication *)application {
+ // Save data if appropriate
+}
+
+#pragma mark -
+#pragma mark Memory management
+
+- (void)dealloc {
+ [navigationController release];
+ [window release];
+ [super dealloc];
+}
+
+
+@end
+
20 Classes/NSString+XMLEntities.h
@@ -0,0 +1,20 @@
+//
+// NSString+XMLEntities.h
+// XML
+//
+// Created by Michael Waterfall on 11/05/2010.
+// Copyright 2010 d3i. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface NSString (XMLEntities)
+
+// Instance Methods
+- (NSString *)stringByStrippingTags;
+- (NSString *)stringByDecodingXMLEntities;
+- (NSString *)stringByEncodingXMLEntities;
+- (NSString *)stringWithNewLinesAsBRs;
+- (NSString *)stringByRemovingNewLinesAndWhitespace;
+
+@end
813 Classes/NSString+XMLEntities.m
@@ -0,0 +1,813 @@
+//
+// NSString+XMLEntities.m
+// XML
+//
+// Created by Michael Waterfall on 11/05/2010.
+// Copyright 2010 d3i. All rights reserved.
+//
+
+#import "NSString+XMLEntities.h"
+
+@implementation NSString (XMLEntities)
+
+#pragma mark -
+#pragma mark Class Methods
+
+#pragma mark -
+#pragma mark Instance Methods
+
+// Partially adapted and extended from examples at:
+// http://stackoverflow.com/questions/1105169/html-character-decoding-in-objective-c-cocoa-touch
+- (NSString *)stringByDecodingXMLEntities {
+
+ // Find first & and short-cut if we can
+ NSUInteger ampIndex = [self rangeOfString:@"&" options:NSLiteralSearch].location;
+ if (ampIndex == NSNotFound) {
+ return [NSString stringWithString:self]; // return copy of string as no & found
+ }
+
+ // Make result string with some extra capacity.
+ NSMutableString *result = [[NSMutableString alloc] initWithCapacity:(self.length * 1.25)];
+
+ // First iteration doesn't need to scan to & since we did that already, but for code simplicity's sake we'll do it again with the scanner.
+ NSScanner *scanner = [NSScanner scannerWithString:self];
+ [scanner setCharactersToBeSkipped:nil];
+
+ // Boundary characters for scanning unexpected &#... pattern
+ NSCharacterSet *boundaryCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@" \t\n\r;"];
+
+ // Scan
+ do {
+
+ // Scan up to the next entity or the end of the string.
+ NSString *nonEntityString;
+ if ([scanner scanUpToString:@"&" intoString:&nonEntityString]) {
+ [result appendString:nonEntityString];
+ }
+ if ([scanner isAtEnd]) break;
+
+ // Common character entity references first
+ if ([scanner scanString:@"&amp;" intoString:NULL])
+ [result appendString:@"&"];
+ else if ([scanner scanString:@"&apos;" intoString:NULL])
+ [result appendString:@"'"];
+ else if ([scanner scanString:@"&quot;" intoString:NULL])
+ [result appendString:@"\""];
+ else if ([scanner scanString:@"&lt;" intoString:NULL])
+ [result appendString:@"<"];
+ else if ([scanner scanString:@"&gt;" intoString:NULL])
+ [result appendString:@">"];
+ else if ([scanner scanString:@"&nbsp;" intoString:NULL])
+ [result appendFormat:@"%C", 160];
+ else if ([scanner scanString:@"&laquo;" intoString:NULL])
+ [result appendFormat:@"%C", 171];
+ else if ([scanner scanString:@"&raquo;" intoString:NULL])
+ [result appendFormat:@"%C", 187];
+ else if ([scanner scanString:@"&ndash;" intoString:NULL])
+ [result appendFormat:@"%C", 8211];
+ else if ([scanner scanString:@"&mdash;" intoString:NULL])
+ [result appendFormat:@"%C", 8212];
+ else if ([scanner scanString:@"&lsquo;" intoString:NULL])
+ [result appendFormat:@"%C", 8216];
+ else if ([scanner scanString:@"&rsquo;" intoString:NULL])
+ [result appendFormat:@"%C", 8217];
+ else if ([scanner scanString:@"&ldquo;" intoString:NULL])
+ [result appendFormat:@"%C", 8220];
+ else if ([scanner scanString:@"&rdquo;" intoString:NULL])
+ [result appendFormat:@"%C", 8221];
+ else if ([scanner scanString:@"&bull;" intoString:NULL])
+ [result appendFormat:@"%C", 8226];
+ else if ([scanner scanString:@"&hellip;" intoString:NULL])
+ [result appendFormat:@"%C", 8230];
+
+ // Numeric character entity references
+ else if ([scanner scanString:@"&#" intoString:NULL]) {
+
+ // Entity
+ BOOL gotNumber;
+ unsigned charCode;
+ NSString *xForHex = @"";
+
+ // Is it hex or decimal?
+ if ([scanner scanString:@"x" intoString:&xForHex]) {
+ gotNumber = [scanner scanHexInt:&charCode];
+ } else {
+ gotNumber = [scanner scanInt:(int*)&charCode];
+ }
+
+ // Process
+ if (gotNumber) {
+
+ // Append character
+ [result appendFormat:@"%C", charCode];
+ [scanner scanString:@";" intoString:NULL];
+
+ } else {
+
+ // Failed to get a number so append to result and log error
+ NSString *unknownEntity = @"";
+ [scanner scanUpToCharactersFromSet:boundaryCharacterSet intoString:&unknownEntity];
+ [result appendFormat:@"&#%@%@", xForHex, unknownEntity]; // Append exact same string
+
+ }
+
+ // Quick check for isolated & with a space after to speed up checks
+ } else if ([scanner scanString:@"& " intoString:NULL])
+ [result appendString:@"& "];
+
+ // No so common character entity references
+ else if ([scanner scanString:@"&iexcl;" intoString:NULL])
+ [result appendFormat:@"%C", 161];
+ else if ([scanner scanString:@"&cent;" intoString:NULL])
+ [result appendFormat:@"%C", 162];
+ else if ([scanner scanString:@"&pound;" intoString:NULL])
+ [result appendFormat:@"%C", 163];
+ else if ([scanner scanString:@"&curren;" intoString:NULL])
+ [result appendFormat:@"%C", 164];
+ else if ([scanner scanString:@"&yen;" intoString:NULL])
+ [result appendFormat:@"%C", 165];
+ else if ([scanner scanString:@"&brvbar;" intoString:NULL])
+ [result appendFormat:@"%C", 166];
+ else if ([scanner scanString:@"&sect;" intoString:NULL])
+ [result appendFormat:@"%C", 167];
+ else if ([scanner scanString:@"&uml;" intoString:NULL])
+ [result appendFormat:@"%C", 168];
+ else if ([scanner scanString:@"&copy;" intoString:NULL])
+ [result appendFormat:@"%C", 169];
+ else if ([scanner scanString:@"&ordf;" intoString:NULL])
+ [result appendFormat:@"%C", 170];
+ else if ([scanner scanString:@"&not;" intoString:NULL])
+ [result appendFormat:@"%C", 172];
+ else if ([scanner scanString:@"&shy;" intoString:NULL])
+ [result appendFormat:@"%C", 173];
+ else if ([scanner scanString:@"&reg;" intoString:NULL])
+ [result appendFormat:@"%C", 174];
+ else if ([scanner scanString:@"&macr;" intoString:NULL])
+ [result appendFormat:@"%C", 175];
+ else if ([scanner scanString:@"&deg;" intoString:NULL])
+ [result appendFormat:@"%C", 176];
+ else if ([scanner scanString:@"&plusmn;" intoString:NULL])
+ [result appendFormat:@"%C", 177];
+ else if ([scanner scanString:@"&sup2;" intoString:NULL])
+ [result appendFormat:@"%C", 178];
+ else if ([scanner scanString:@"&sup3;" intoString:NULL])
+ [result appendFormat:@"%C", 179];
+ else if ([scanner scanString:@"&acute;" intoString:NULL])
+ [result appendFormat:@"%C", 180];
+ else if ([scanner scanString:@"&micro;" intoString:NULL])
+ [result appendFormat:@"%C", 181];
+ else if ([scanner scanString:@"&para;" intoString:NULL])
+ [result appendFormat:@"%C", 182];
+ else if ([scanner scanString:@"&middot;" intoString:NULL])
+ [result appendFormat:@"%C", 183];
+ else if ([scanner scanString:@"&cedil;" intoString:NULL])
+ [result appendFormat:@"%C", 184];
+ else if ([scanner scanString:@"&sup1;" intoString:NULL])
+ [result appendFormat:@"%C", 185];
+ else if ([scanner scanString:@"&ordm;" intoString:NULL])
+ [result appendFormat:@"%C", 186];
+ else if ([scanner scanString:@"&frac14;" intoString:NULL])
+ [result appendFormat:@"%C", 188];
+ else if ([scanner scanString:@"&frac12;" intoString:NULL])
+ [result appendFormat:@"%C", 189];
+ else if ([scanner scanString:@"&frac34;" intoString:NULL])
+ [result appendFormat:@"%C", 190];
+ else if ([scanner scanString:@"&iquest;" intoString:NULL])
+ [result appendFormat:@"%C", 191];
+ else if ([scanner scanString:@"&Agrave;" intoString:NULL])
+ [result appendFormat:@"%C", 192];
+ else if ([scanner scanString:@"&Aacute;" intoString:NULL])
+ [result appendFormat:@"%C", 193];
+ else if ([scanner scanString:@"&Acirc;" intoString:NULL])
+ [result appendFormat:@"%C", 194];
+ else if ([scanner scanString:@"&Atilde;" intoString:NULL])
+ [result appendFormat:@"%C", 195];
+ else if ([scanner scanString:@"&Auml;" intoString:NULL])
+ [result appendFormat:@"%C", 196];
+ else if ([scanner scanString:@"&Aring;" intoString:NULL])
+ [result appendFormat:@"%C", 197];
+ else if ([scanner scanString:@"&AElig;" intoString:NULL])
+ [result appendFormat:@"%C", 198];
+ else if ([scanner scanString:@"&Ccedil;" intoString:NULL])
+ [result appendFormat:@"%C", 199];
+ else if ([scanner scanString:@"&Egrave;" intoString:NULL])
+ [result appendFormat:@"%C", 200];
+ else if ([scanner scanString:@"&Eacute;" intoString:NULL])
+ [result appendFormat:@"%C", 201];
+ else if ([scanner scanString:@"&Ecirc;" intoString:NULL])
+ [result appendFormat:@"%C", 202];
+ else if ([scanner scanString:@"&Euml;" intoString:NULL])
+ [result appendFormat:@"%C", 203];
+ else if ([scanner scanString:@"&Igrave;" intoString:NULL])
+ [result appendFormat:@"%C", 204];
+ else if ([scanner scanString:@"&Iacute;" intoString:NULL])
+ [result appendFormat:@"%C", 205];
+ else if ([scanner scanString:@"&Icirc;" intoString:NULL])
+ [result appendFormat:@"%C", 206];
+ else if ([scanner scanString:@"&Iuml;" intoString:NULL])
+ [result appendFormat:@"%C", 207];
+ else if ([scanner scanString:@"&ETH;" intoString:NULL])
+ [result appendFormat:@"%C", 208];
+ else if ([scanner scanString:@"&Ntilde;" intoString:NULL])
+ [result appendFormat:@"%C", 209];
+ else if ([scanner scanString:@"&Ograve;" intoString:NULL])
+ [result appendFormat:@"%C", 210];
+ else if ([scanner scanString:@"&Oacute;" intoString:NULL])
+ [result appendFormat:@"%C", 211];
+ else if ([scanner scanString:@"&Ocirc;" intoString:NULL])
+ [result appendFormat:@"%C", 212];
+ else if ([scanner scanString:@"&Otilde;" intoString:NULL])
+ [result appendFormat:@"%C", 213];
+ else if ([scanner scanString:@"&Ouml;" intoString:NULL])
+ [result appendFormat:@"%C", 214];
+ else if ([scanner scanString:@"&times;" intoString:NULL])
+ [result appendFormat:@"%C", 215];
+ else if ([scanner scanString:@"&Oslash;" intoString:NULL])
+ [result appendFormat:@"%C", 216];
+ else if ([scanner scanString:@"&Ugrave;" intoString:NULL])
+ [result appendFormat:@"%C", 217];
+ else if ([scanner scanString:@"&Uacute;" intoString:NULL])
+ [result appendFormat:@"%C", 218];
+ else if ([scanner scanString:@"&Ucirc;" intoString:NULL])
+ [result appendFormat:@"%C", 219];
+ else if ([scanner scanString:@"&Uuml;" intoString:NULL])
+ [result appendFormat:@"%C", 220];
+ else if ([scanner scanString:@"&Yacute;" intoString:NULL])
+ [result appendFormat:@"%C", 221];
+ else if ([scanner scanString:@"&THORN;" intoString:NULL])
+ [result appendFormat:@"%C", 222];
+ else if ([scanner scanString:@"&szlig;" intoString:NULL])
+ [result appendFormat:@"%C", 223];
+ else if ([scanner scanString:@"&agrave;" intoString:NULL])
+ [result appendFormat:@"%C", 224];
+ else if ([scanner scanString:@"&aacute;" intoString:NULL])
+ [result appendFormat:@"%C", 225];
+ else if ([scanner scanString:@"&acirc;" intoString:NULL])
+ [result appendFormat:@"%C", 226];
+ else if ([scanner scanString:@"&atilde;" intoString:NULL])
+ [result appendFormat:@"%C", 227];
+ else if ([scanner scanString:@"&auml;" intoString:NULL])
+ [result appendFormat:@"%C", 228];
+ else if ([scanner scanString:@"&aring;" intoString:NULL])
+ [result appendFormat:@"%C", 229];
+ else if ([scanner scanString:@"&aelig;" intoString:NULL])
+ [result appendFormat:@"%C", 230];
+ else if ([scanner scanString:@"&ccedil;" intoString:NULL])
+ [result appendFormat:@"%C", 231];
+ else if ([scanner scanString:@"&egrave;" intoString:NULL])
+ [result appendFormat:@"%C", 232];
+ else if ([scanner scanString:@"&eacute;" intoString:NULL])
+ [result appendFormat:@"%C", 233];
+ else if ([scanner scanString:@"&ecirc;" intoString:NULL])
+ [result appendFormat:@"%C", 234];
+ else if ([scanner scanString:@"&euml;" intoString:NULL])
+ [result appendFormat:@"%C", 235];
+ else if ([scanner scanString:@"&igrave;" intoString:NULL])
+ [result appendFormat:@"%C", 236];
+ else if ([scanner scanString:@"&iacute;" intoString:NULL])
+ [result appendFormat:@"%C", 237];
+ else if ([scanner scanString:@"&icirc;" intoString:NULL])
+ [result appendFormat:@"%C", 238];
+ else if ([scanner scanString:@"&iuml;" intoString:NULL])
+ [result appendFormat:@"%C", 239];
+ else if ([scanner scanString:@"&eth;" intoString:NULL])
+ [result appendFormat:@"%C", 240];
+ else if ([scanner scanString:@"&ntilde;" intoString:NULL])
+ [result appendFormat:@"%C", 241];
+ else if ([scanner scanString:@"&ograve;" intoString:NULL])
+ [result appendFormat:@"%C", 242];
+ else if ([scanner scanString:@"&oacute;" intoString:NULL])
+ [result appendFormat:@"%C", 243];
+ else if ([scanner scanString:@"&ocirc;" intoString:NULL])
+ [result appendFormat:@"%C", 244];
+ else if ([scanner scanString:@"&otilde;" intoString:NULL])
+ [result appendFormat:@"%C", 245];
+ else if ([scanner scanString:@"&ouml;" intoString:NULL])
+ [result appendFormat:@"%C", 246];
+ else if ([scanner scanString:@"&divide;" intoString:NULL])
+ [result appendFormat:@"%C", 247];
+ else if ([scanner scanString:@"&oslash;" intoString:NULL])
+ [result appendFormat:@"%C", 248];
+ else if ([scanner scanString:@"&ugrave;" intoString:NULL])
+ [result appendFormat:@"%C", 249];
+ else if ([scanner scanString:@"&uacute;" intoString:NULL])
+ [result appendFormat:@"%C", 250];
+ else if ([scanner scanString:@"&ucirc;" intoString:NULL])
+ [result appendFormat:@"%C", 251];
+ else if ([scanner scanString:@"&uuml;" intoString:NULL])
+ [result appendFormat:@"%C", 252];
+ else if ([scanner scanString:@"&yacute;" intoString:NULL])
+ [result appendFormat:@"%C", 253];
+ else if ([scanner scanString:@"&thorn;" intoString:NULL])
+ [result appendFormat:@"%C", 254];
+ else if ([scanner scanString:@"&yuml;" intoString:NULL])
+ [result appendFormat:@"%C", 255];
+ else if ([scanner scanString:@"&OElig;" intoString:NULL])
+ [result appendFormat:@"%C", 338];
+ else if ([scanner scanString:@"&oelig;" intoString:NULL])
+ [result appendFormat:@"%C", 339];
+ else if ([scanner scanString:@"&Scaron;" intoString:NULL])
+ [result appendFormat:@"%C", 352];
+ else if ([scanner scanString:@"&scaron;" intoString:NULL])
+ [result appendFormat:@"%C", 353];
+ else if ([scanner scanString:@"&Yuml;" intoString:NULL])
+ [result appendFormat:@"%C", 376];
+ else if ([scanner scanString:@"&fnof;" intoString:NULL])
+ [result appendFormat:@"%C", 402];
+ else if ([scanner scanString:@"&circ;" intoString:NULL])
+ [result appendFormat:@"%C", 710];
+ else if ([scanner scanString:@"&tilde;" intoString:NULL])
+ [result appendFormat:@"%C", 732];
+ else if ([scanner scanString:@"&Alpha;" intoString:NULL])
+ [result appendFormat:@"%C", 913];
+ else if ([scanner scanString:@"&Beta;" intoString:NULL])
+ [result appendFormat:@"%C", 914];
+ else if ([scanner scanString:@"&Gamma;" intoString:NULL])
+ [result appendFormat:@"%C", 915];
+ else if ([scanner scanString:@"&Delta;" intoString:NULL])
+ [result appendFormat:@"%C", 916];
+ else if ([scanner scanString:@"&Epsilon;" intoString:NULL])
+ [result appendFormat:@"%C", 917];
+ else if ([scanner scanString:@"&Zeta;" intoString:NULL])
+ [result appendFormat:@"%C", 918];
+ else if ([scanner scanString:@"&Eta;" intoString:NULL])
+ [result appendFormat:@"%C", 919];
+ else if ([scanner scanString:@"&Theta;" intoString:NULL])
+ [result appendFormat:@"%C", 920];
+ else if ([scanner scanString:@"&Iota;" intoString:NULL])
+ [result appendFormat:@"%C", 921];
+ else if ([scanner scanString:@"&Kappa;" intoString:NULL])
+ [result appendFormat:@"%C", 922];
+ else if ([scanner scanString:@"&Lambda;" intoString:NULL])
+ [result appendFormat:@"%C", 923];
+ else if ([scanner scanString:@"&Mu;" intoString:NULL])
+ [result appendFormat:@"%C", 924];
+ else if ([scanner scanString:@"&Nu;" intoString:NULL])
+ [result appendFormat:@"%C", 925];
+ else if ([scanner scanString:@"&Xi;" intoString:NULL])
+ [result appendFormat:@"%C", 926];
+ else if ([scanner scanString:@"&Omicron;" intoString:NULL])
+ [result appendFormat:@"%C", 927];
+ else if ([scanner scanString:@"&Pi;" intoString:NULL])
+ [result appendFormat:@"%C", 928];
+ else if ([scanner scanString:@"&Rho;" intoString:NULL])
+ [result appendFormat:@"%C", 929];
+ else if ([scanner scanString:@"&Sigma;" intoString:NULL])
+ [result appendFormat:@"%C", 931];
+ else if ([scanner scanString:@"&Tau;" intoString:NULL])
+ [result appendFormat:@"%C", 932];
+ else if ([scanner scanString:@"&Upsilon;" intoString:NULL])
+ [result appendFormat:@"%C", 933];
+ else if ([scanner scanString:@"&Phi;" intoString:NULL])
+ [result appendFormat:@"%C", 934];
+ else if ([scanner scanString:@"&Chi;" intoString:NULL])
+ [result appendFormat:@"%C", 935];
+ else if ([scanner scanString:@"&Psi;" intoString:NULL])
+ [result appendFormat:@"%C", 936];
+ else if ([scanner scanString:@"&Omega;" intoString:NULL])
+ [result appendFormat:@"%C", 937];
+ else if ([scanner scanString:@"&alpha;" intoString:NULL])
+ [result appendFormat:@"%C", 945];
+ else if ([scanner scanString:@"&beta;" intoString:NULL])
+ [result appendFormat:@"%C", 946];
+ else if ([scanner scanString:@"&gamma;" intoString:NULL])
+ [result appendFormat:@"%C", 947];
+ else if ([scanner scanString:@"&delta;" intoString:NULL])
+ [result appendFormat:@"%C", 948];
+ else if ([scanner scanString:@"&epsilon;" intoString:NULL])
+ [result appendFormat:@"%C", 949];
+ else if ([scanner scanString:@"&zeta;" intoString:NULL])
+ [result appendFormat:@"%C", 950];
+ else if ([scanner scanString:@"&eta;" intoString:NULL])
+ [result appendFormat:@"%C", 951];
+ else if ([scanner scanString:@"&theta;" intoString:NULL])
+ [result appendFormat:@"%C", 952];
+ else if ([scanner scanString:@"&iota;" intoString:NULL])
+ [result appendFormat:@"%C", 953];
+ else if ([scanner scanString:@"&kappa;" intoString:NULL])
+ [result appendFormat:@"%C", 954];
+ else if ([scanner scanString:@"&lambda;" intoString:NULL])
+ [result appendFormat:@"%C", 955];
+ else if ([scanner scanString:@"&mu;" intoString:NULL])
+ [result appendFormat:@"%C", 956];
+ else if ([scanner scanString:@"&nu;" intoString:NULL])
+ [result appendFormat:@"%C", 957];
+ else if ([scanner scanString:@"&xi;" intoString:NULL])
+ [result appendFormat:@"%C", 958];
+ else if ([scanner scanString:@"&omicron;" intoString:NULL])
+ [result appendFormat:@"%C", 959];
+ else if ([scanner scanString:@"&pi;" intoString:NULL])
+ [result appendFormat:@"%C", 960];
+ else if ([scanner scanString:@"&rho;" intoString:NULL])
+ [result appendFormat:@"%C", 961];
+ else if ([scanner scanString:@"&sigmaf;" intoString:NULL])
+ [result appendFormat:@"%C", 962];
+ else if ([scanner scanString:@"&sigma;" intoString:NULL])
+ [result appendFormat:@"%C", 963];
+ else if ([scanner scanString:@"&tau;" intoString:NULL])
+ [result appendFormat:@"%C", 964];
+ else if ([scanner scanString:@"&upsilon;" intoString:NULL])
+ [result appendFormat:@"%C", 965];
+ else if ([scanner scanString:@"&phi;" intoString:NULL])
+ [result appendFormat:@"%C", 966];
+ else if ([scanner scanString:@"&chi;" intoString:NULL])
+ [result appendFormat:@"%C", 967];
+ else if ([scanner scanString:@"&psi;" intoString:NULL])
+ [result appendFormat:@"%C", 968];
+ else if ([scanner scanString:@"&omega;" intoString:NULL])
+ [result appendFormat:@"%C", 969];
+ else if ([scanner scanString:@"&thetasym;" intoString:NULL])
+ [result appendFormat:@"%C", 977];
+ else if ([scanner scanString:@"&upsih;" intoString:NULL])
+ [result appendFormat:@"%C", 978];
+ else if ([scanner scanString:@"&piv;" intoString:NULL])
+ [result appendFormat:@"%C", 982];
+ else if ([scanner scanString:@"&ensp;" intoString:NULL])
+ [result appendFormat:@"%C", 8194];
+ else if ([scanner scanString:@"&emsp;" intoString:NULL])
+ [result appendFormat:@"%C", 8195];
+ else if ([scanner scanString:@"&thinsp;" intoString:NULL])
+ [result appendFormat:@"%C", 8201];
+ else if ([scanner scanString:@"&zwnj;" intoString:NULL])
+ [result appendFormat:@"%C", 8204];
+ else if ([scanner scanString:@"&zwj;" intoString:NULL])
+ [result appendFormat:@"%C", 8205];
+ else if ([scanner scanString:@"&lrm;" intoString:NULL])
+ [result appendFormat:@"%C", 8206];
+ else if ([scanner scanString:@"&rlm;" intoString:NULL])
+ [result appendFormat:@"%C", 8207];
+ else if ([scanner scanString:@"&sbquo;" intoString:NULL])
+ [result appendFormat:@"%C", 8218];
+ else if ([scanner scanString:@"&bdquo;" intoString:NULL])
+ [result appendFormat:@"%C", 8222];
+ else if ([scanner scanString:@"&dagger;" intoString:NULL])
+ [result appendFormat:@"%C", 8224];
+ else if ([scanner scanString:@"&Dagger;" intoString:NULL])
+ [result appendFormat:@"%C", 8225];
+ else if ([scanner scanString:@"&permil;" intoString:NULL])
+ [result appendFormat:@"%C", 8240];
+ else if ([scanner scanString:@"&prime;" intoString:NULL])
+ [result appendFormat:@"%C", 8242];
+ else if ([scanner scanString:@"&Prime;" intoString:NULL])
+ [result appendFormat:@"%C", 8243];
+ else if ([scanner scanString:@"&lsaquo;" intoString:NULL])
+ [result appendFormat:@"%C", 8249];
+ else if ([scanner scanString:@"&rsaquo;" intoString:NULL])
+ [result appendFormat:@"%C", 8250];
+ else if ([scanner scanString:@"&oline;" intoString:NULL])
+ [result appendFormat:@"%C", 8254];
+ else if ([scanner scanString:@"&frasl;" intoString:NULL])
+ [result appendFormat:@"%C", 8260];
+ else if ([scanner scanString:@"&euro;" intoString:NULL])
+ [result appendFormat:@"%C", 8364];
+ else if ([scanner scanString:@"&image;" intoString:NULL])
+ [result appendFormat:@"%C", 8465];
+ else if ([scanner scanString:@"&weierp;" intoString:NULL])
+ [result appendFormat:@"%C", 8472];
+ else if ([scanner scanString:@"&real;" intoString:NULL])
+ [result appendFormat:@"%C", 8476];
+ else if ([scanner scanString:@"&trade;" intoString:NULL])
+ [result appendFormat:@"%C", 8482];
+ else if ([scanner scanString:@"&alefsym;" intoString:NULL])
+ [result appendFormat:@"%C", 8501];
+ else if ([scanner scanString:@"&larr;" intoString:NULL])
+ [result appendFormat:@"%C", 8592];
+ else if ([scanner scanString:@"&uarr;" intoString:NULL])
+ [result appendFormat:@"%C", 8593];
+ else if ([scanner scanString:@"&rarr;" intoString:NULL])
+ [result appendFormat:@"%C", 8594];
+ else if ([scanner scanString:@"&darr;" intoString:NULL])
+ [result appendFormat:@"%C", 8595];
+ else if ([scanner scanString:@"&harr;" intoString:NULL])
+ [result appendFormat:@"%C", 8596];
+ else if ([scanner scanString:@"&crarr;" intoString:NULL])
+ [result appendFormat:@"%C", 8629];
+ else if ([scanner scanString:@"&lArr;" intoString:NULL])
+ [result appendFormat:@"%C", 8656];
+ else if ([scanner scanString:@"&uArr;" intoString:NULL])
+ [result appendFormat:@"%C", 8657];
+ else if ([scanner scanString:@"&rArr;" intoString:NULL])
+ [result appendFormat:@"%C", 8658];
+ else if ([scanner scanString:@"&dArr;" intoString:NULL])
+ [result appendFormat:@"%C", 8659];
+ else if ([scanner scanString:@"&hArr;" intoString:NULL])
+ [result appendFormat:@"%C", 8660];
+ else if ([scanner scanString:@"&forall;" intoString:NULL])
+ [result appendFormat:@"%C", 8704];
+ else if ([scanner scanString:@"&part;" intoString:NULL])
+ [result appendFormat:@"%C", 8706];
+ else if ([scanner scanString:@"&exist;" intoString:NULL])
+ [result appendFormat:@"%C", 8707];
+ else if ([scanner scanString:@"&empty;" intoString:NULL])
+ [result appendFormat:@"%C", 8709];
+ else if ([scanner scanString:@"&nabla;" intoString:NULL])
+ [result appendFormat:@"%C", 8711];
+ else if ([scanner scanString:@"&isin;" intoString:NULL])
+ [result appendFormat:@"%C", 8712];
+ else if ([scanner scanString:@"&notin;" intoString:NULL])
+ [result appendFormat:@"%C", 8713];
+ else if ([scanner scanString:@"&ni;" intoString:NULL])
+ [result appendFormat:@"%C", 8715];
+ else if ([scanner scanString:@"&prod;" intoString:NULL])
+ [result appendFormat:@"%C", 8719];
+ else if ([scanner scanString:@"&sum;" intoString:NULL])
+ [result appendFormat:@"%C", 8721];
+ else if ([scanner scanString:@"&minus;" intoString:NULL])
+ [result appendFormat:@"%C", 8722];
+ else if ([scanner scanString:@"&lowast;" intoString:NULL])
+ [result appendFormat:@"%C", 8727];
+ else if ([scanner scanString:@"&radic;" intoString:NULL])
+ [result appendFormat:@"%C", 8730];
+ else if ([scanner scanString:@"&prop;" intoString:NULL])
+ [result appendFormat:@"%C", 8733];
+ else if ([scanner scanString:@"&infin;" intoString:NULL])
+ [result appendFormat:@"%C", 8734];
+ else if ([scanner scanString:@"&ang;" intoString:NULL])
+ [result appendFormat:@"%C", 8736];
+ else if ([scanner scanString:@"&and;" intoString:NULL])
+ [result appendFormat:@"%C", 8743];
+ else if ([scanner scanString:@"&or;" intoString:NULL])
+ [result appendFormat:@"%C", 8744];
+ else if ([scanner scanString:@"&cap;" intoString:NULL])
+ [result appendFormat:@"%C", 8745];
+ else if ([scanner scanString:@"&cup;" intoString:NULL])
+ [result appendFormat:@"%C", 8746];
+ else if ([scanner scanString:@"&int;" intoString:NULL])
+ [result appendFormat:@"%C", 8747];
+ else if ([scanner scanString:@"&there4;" intoString:NULL])
+ [result appendFormat:@"%C", 8756];
+ else if ([scanner scanString:@"&sim;" intoString:NULL])
+ [result appendFormat:@"%C", 8764];
+ else if ([scanner scanString:@"&cong;" intoString:NULL])
+ [result appendFormat:@"%C", 8773];
+ else if ([scanner scanString:@"&asymp;" intoString:NULL])
+ [result appendFormat:@"%C", 8776];
+ else if ([scanner scanString:@"&ne;" intoString:NULL])
+ [result appendFormat:@"%C", 8800];
+ else if ([scanner scanString:@"&equiv;" intoString:NULL])
+ [result appendFormat:@"%C", 8801];
+ else if ([scanner scanString:@"&le;" intoString:NULL])
+ [result appendFormat:@"%C", 8804];
+ else if ([scanner scanString:@"&ge;" intoString:NULL])
+ [result appendFormat:@"%C", 8805];
+ else if ([scanner scanString:@"&sub;" intoString:NULL])
+ [result appendFormat:@"%C", 8834];
+ else if ([scanner scanString:@"&sup;" intoString:NULL])
+ [result appendFormat:@"%C", 8835];
+ else if ([scanner scanString:@"&nsub;" intoString:NULL])
+ [result appendFormat:@"%C", 8836];
+ else if ([scanner scanString:@"&sube;" intoString:NULL])
+ [result appendFormat:@"%C", 8838];
+ else if ([scanner scanString:@"&supe;" intoString:NULL])
+ [result appendFormat:@"%C", 8839];
+ else if ([scanner scanString:@"&oplus;" intoString:NULL])
+ [result appendFormat:@"%C", 8853];
+ else if ([scanner scanString:@"&otimes;" intoString:NULL])
+ [result appendFormat:@"%C", 8855];
+ else if ([scanner scanString:@"&perp;" intoString:NULL])
+ [result appendFormat:@"%C", 8869];
+ else if ([scanner scanString:@"&sdot;" intoString:NULL])
+ [result appendFormat:@"%C", 8901];
+ else if ([scanner scanString:@"&lceil;" intoString:NULL])
+ [result appendFormat:@"%C", 8968];
+ else if ([scanner scanString:@"&rceil;" intoString:NULL])
+ [result appendFormat:@"%C", 8969];
+ else if ([scanner scanString:@"&lfloor;" intoString:NULL])
+ [result appendFormat:@"%C", 8970];
+ else if ([scanner scanString:@"&rfloor;" intoString:NULL])
+ [result appendFormat:@"%C", 8971];
+ else if ([scanner scanString:@"&lang;" intoString:NULL])
+ [result appendFormat:@"%C", 9001];
+ else if ([scanner scanString:@"&rang;" intoString:NULL])
+ [result appendFormat:@"%C", 9002];
+ else if ([scanner scanString:@"&loz;" intoString:NULL])
+ [result appendFormat:@"%C", 9674];
+ else if ([scanner scanString:@"&spades;" intoString:NULL])
+ [result appendFormat:@"%C", 9824];
+ else if ([scanner scanString:@"&clubs;" intoString:NULL])
+ [result appendFormat:@"%C", 9827];
+ else if ([scanner scanString:@"&hearts;" intoString:NULL])
+ [result appendFormat:@"%C", 9829];
+ else if ([scanner scanString:@"&diams;" intoString:NULL])
+ [result appendFormat:@"%C", 9830];
+ else {
+
+ // Must be an isolated & with no space after
+ NSString *amp;
+ [scanner scanString:@"&" intoString:&amp]; // isolated & symbol
+ [result appendString:amp];
+
+ }
+
+ } while (![scanner isAtEnd]);
+
+ // Finish
+ NSString *resultingString = [NSString stringWithString:result];
+ [result release];
+ return resultingString;
+
+}
+
+// Needs more work to encode more entities
+- (NSString *)stringByEncodingXMLEntities {
+
+ // Scanner
+ NSScanner *scanner = [[NSScanner alloc] initWithString:self];
+ [scanner setCharactersToBeSkipped:nil];
+ NSMutableString *result = [[NSMutableString alloc] init];
+ NSString *temp;
+ NSCharacterSet *characters = [NSCharacterSet characterSetWithCharactersInString:@"&\"'<>"];
+ [scanner setCharactersToBeSkipped:nil];
+
+ // Scan
+ while (![scanner isAtEnd]) {
+
+ // Get non new line or whitespace characters
+ temp = nil;
+ [scanner scanUpToCharactersFromSet:characters intoString:&temp];
+ if (temp) [result appendString:temp];
+
+ // Replace with encoded entities
+ if ([scanner scanString:@"&" intoString:NULL])
+ [result appendString:@"&amp;"];
+ else if ([scanner scanString:@"'" intoString:NULL])
+ [result appendString:@"&apos;"];
+ else if ([scanner scanString:@"\"" intoString:NULL])
+ [result appendString:@"&quot;"];
+ else if ([scanner scanString:@"<" intoString:NULL])
+ [result appendString:@"&lt;"];
+ else if ([scanner scanString:@">" intoString:NULL])
+ [result appendString:@"&gt;"];
+
+ }
+
+ // Cleanup
+ [scanner release];
+
+ // Return
+ NSString *retString = [NSString stringWithString:result];
+ [result release];
+ return retString;
+
+}
+
+- (NSString *)stringByStrippingTags {
+
+ // Find first & and short-cut if we can
+ NSUInteger ampIndex = [self rangeOfString:@"<" options:NSLiteralSearch].location;
+ if (ampIndex == NSNotFound) {
+ return [NSString stringWithString:self]; // return copy of string as no tags found
+ }
+
+ // Scan and find all tags
+ NSScanner *scanner = [NSScanner scannerWithString:self];
+ [scanner setCharactersToBeSkipped:nil];
+ NSMutableSet *tags = [[NSMutableSet alloc] init];
+ NSString *tag;
+ do {
+
+ // Scan up to <
+ tag = nil;
+ [scanner scanUpToString:@"<" intoString:NULL];
+ [scanner scanUpToString:@">" intoString:&tag];
+
+ // Add to set
+ if (tag) {
+ NSString *t = [[NSString alloc] initWithFormat:@"%@>", tag];
+ [tags addObject:t];
+ [t release];
+ }
+
+ } while (![scanner isAtEnd]);
+
+ // Strings
+ NSMutableString *result = [[NSMutableString alloc] initWithString:self];
+ NSString *finalString;
+
+ // Replace tags
+ NSString *replacement;
+ for (NSString *t in tags) {
+
+ // Replace tag with space unless it's an inline element
+ replacement = @" ";
+ if ([t isEqualToString:@"<a>"] ||
+ [t isEqualToString:@"</a>"] ||
+ [t isEqualToString:@"<span>"] ||
+ [t isEqualToString:@"</span>"] ||
+ [t isEqualToString:@"<strong>"] ||
+ [t isEqualToString:@"</strong>"] ||
+ [t isEqualToString:@"<em>"] ||
+ [t isEqualToString:@"</em>"]) {
+ replacement = @"";
+ }
+
+ // Replace
+ [result replaceOccurrencesOfString:t
+ withString:replacement
+ options:NSLiteralSearch
+ range:NSMakeRange(0, result.length)];
+ }
+
+ // Remove multi-spaces and line breaks
+ finalString = [result stringByRemovingNewLinesAndWhitespace];
+
+ // Cleanup & return
+ [result release];
+ [tags release];
+ return finalString;
+
+}
+
+- (NSString *)stringWithNewLinesAsBRs {
+
+ // Strange New lines:
+ // Next Line, U+0085
+ // Form Feed, U+000C
+ // Line Separator, U+2028
+ // Paragraph Separator, U+2029
+
+ // Scanner
+ NSScanner *scanner = [[NSScanner alloc] initWithString:self];
+ [scanner setCharactersToBeSkipped:nil];
+ NSMutableString *result = [[NSMutableString alloc] init];
+ NSString *temp;
+ NSCharacterSet *newLineCharacters = [NSCharacterSet characterSetWithCharactersInString:
+ [NSString stringWithFormat:@"\n\r%C%C%C%C", 0x0085, 0x000C, 0x2028, 0x2029]];
+ // Scan
+ do {
+
+ // Get non new line characters
+ temp = nil;
+ [scanner scanUpToCharactersFromSet:newLineCharacters intoString:&temp];
+ if (temp) [result appendString:temp];
+ temp = nil;
+
+ // Add <br /> s
+ if ([scanner scanString:@"\r\n" intoString:nil]) {
+
+ // Combine \r\n into just 1 <br />
+ [result appendString:@"<br />"];
+
+ } else if ([scanner scanCharactersFromSet:newLineCharacters intoString:&temp]) {
+
+ // Scan other new line characters and add <br /> s
+ if (temp) {
+ for (int i = 0; i < temp.length; i++) {
+ [result appendString:@"<br />"];
+ }
+ }
+
+ }
+
+ } while (![scanner isAtEnd]);
+
+ // Cleanup & return
+ [scanner release];
+ NSString *retString = [NSString stringWithString:result];
+ [result release];
+ return retString;
+
+}
+
+- (NSString *)stringByRemovingNewLinesAndWhitespace {
+
+ // Strange New lines:
+ // Next Line, U+0085
+ // Form Feed, U+000C
+ // Line Separator, U+2028
+ // Paragraph Separator, U+2029
+
+ // Scanner
+ NSScanner *scanner = [[NSScanner alloc] initWithString:self];
+ [scanner setCharactersToBeSkipped:nil];
+ NSMutableString *result = [[NSMutableString alloc] init];
+ NSString *temp;
+ NSCharacterSet *newLineAndWhitespaceCharacters = [NSCharacterSet characterSetWithCharactersInString:
+ [NSString stringWithFormat:@" \t\n\r%C%C%C%C", 0x0085, 0x000C, 0x2028, 0x2029]];
+ // Scan
+ while (![scanner isAtEnd]) {
+
+ // Get non new line or whitespace characters
+ temp = nil;
+ [scanner scanUpToCharactersFromSet:newLineAndWhitespaceCharacters intoString:&temp];
+ if (temp) [result appendString:temp];
+
+ // Replace with a space
+ if ([scanner scanCharactersFromSet:newLineAndWhitespaceCharacters intoString:NULL]) {
+ if (result.length > 0 && ![scanner isAtEnd]) // Dont append space to beginning or end of result
+ [result appendString:@" "];
+ }
+
+ }
+
+ // Cleanup
+ [scanner release];
+
+ // Return
+ NSString *retString = [NSString stringWithString:result];
+ [result release];
+ return retString;
+
+}
+
+@end
17 Classes/RootViewController.h
@@ -0,0 +1,17 @@
+//
+// RootViewController.h
+// XML
+//
+// Created by Michael Waterfall on 07/05/2010.
+// Copyright d3i 2010. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "MWFeedParser.h"
+
+@interface RootViewController : UITableViewController <MWFeedParserDelegate> {
+ MWFeedParser *feedParser;
+ NSMutableArray *items;
+}
+
+@end
169 Classes/RootViewController.m
@@ -0,0 +1,169 @@
+//"
+// RootViewController.m
+// XML
+//
+// Created by Michael Waterfall on 07/05/2010.
+// Copyright d3i 2010. All rights reserved.
+//
+
+#import "RootViewController.h"
+#import "NSString+XMLEntities.h"
+#import "MWFeedParser.h"
+
+@implementation RootViewController
+
+#pragma mark -
+#pragma mark View lifecycle
+
+- (void)viewDidLoad {
+
+ // Super
+ [super viewDidLoad];
+
+ // Setup
+ self.title = @"Loading...";
+ items = [[NSMutableArray alloc] init];
+
+ // Create parser
+ feedParser = [[MWFeedParser alloc] initWithFeedURL:@"feed://www.shoutfilm.com/rss/staff-blog/"];
+ feedParser.delegate = self;
+ feedParser.feedParseType = ParseTypeFull; // Parse feed info and all items
+ feedParser.connectionType = ConnectionTypeAsynchronously;
+ [feedParser parse];
+
+}
+
+#pragma mark -
+#pragma mark MWFeedParserDelegate
+
+- (void)feedParserDidStart:(MWFeedParser *)parser {
+ NSLog(@"Started Parsing: %@", parser.url);
+}
+
+- (void)feedParser:(MWFeedParser *)parser didParseFeedInfo:(MWFeedInfo *)info {
+ NSLog(@"%@", info);
+ self.title = info.title;
+}
+
+- (void)feedParser:(MWFeedParser *)parser didParseFeedItem:(MWFeedItem *)item {
+ NSLog(@"%@", item);
+ if (item) [items addObject:item];
+}
+
+- (void)feedParserDidFinish:(MWFeedParser *)parser {
+ NSLog(@"Finished Parsing: %@", parser.url);
+ [self.tableView reloadData];
+}
+
+- (void)feedParser:(MWFeedParser *)parser didFailWithError:(NSError *)error {
+ NSLog(@"Finished Parsing: %@ With Error: %@", parser.url, error);
+}
+
+#pragma mark -
+#pragma mark Table view data source
+
+// Customize the number of sections in the table view.
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
+ return 1;
+}
+
+// Customize the number of rows in the table view.
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ return items.count;
+}
+
+// Customize the appearance of table view cells.
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+
+ static NSString *CellIdentifier = @"Cell";
+
+ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
+ if (cell == nil) {
+ cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
+ }
+
+ // Configure the cell.
+ MWFeedItem *item = [items objectAtIndex:indexPath.row];
+ if (item) {
+ cell.textLabel.text = item.title;
+ cell.detailTextLabel.text = [item.date description];
+ }
+
+ return cell;
+}
+
+/*
+// Override to support conditional editing of the table view.
+- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
+ // Return NO if you do not want the specified item to be editable.
+ return YES;
+}
+*/
+
+/*
+// Override to support editing the table view.
+- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
+
+ if (editingStyle == UITableViewCellEditingStyleDelete) {
+ // Delete the row from the data source.
+ [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
+ }
+ else if (editingStyle == UITableViewCellEditingStyleInsert) {
+ // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
+ }
+}
+*/
+
+/*
+// Override to support rearranging the table view.
+- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
+}
+*/
+
+
+/*
+// Override to support conditional rearranging of the table view.
+- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
+ // Return NO if you do not want the item to be re-orderable.
+ return YES;
+}
+*/
+
+#pragma mark -
+#pragma mark Table view delegate
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+
+ /*
+ <#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:@"<#Nib name#>" bundle:nil];
+ // ...
+ // Pass the selected object to the new view controller.
+ [self.navigationController pushViewController:detailViewController animated:YES];
+ [detailViewController release];
+ */
+}
+
+#pragma mark -
+#pragma mark Memory management
+
+- (void)didReceiveMemoryWarning {
+ // Releases the view if it doesn't have a superview.
+ [super didReceiveMemoryWarning];
+
+ // Relinquish ownership any cached data, images, etc that aren't in use.
+}
+
+- (void)viewDidUnload {
+ // Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
+ // For example: self.myOutlet = nil;
+}
+
+
+- (void)dealloc {
+ [items release];
+ [feedParser release];
+ [super dealloc];
+}
+
+
+@end
22 LICENSE.txt
@@ -0,0 +1,22 @@
+Copyright (c) 2010 Michael Waterfall
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
30 MWFeedParser-Info.plist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleDisplayName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.yourcompany.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>NSMainNibFile</key>
+ <string>MainWindow</string>
+</dict>
+</plist>
1,412 MWFeedParser.xcodeproj/Michael.mode1v3
@@ -0,0 +1,1412 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>ActivePerspectiveName</key>
+ <string>Project</string>
+ <key>AllowedModules</key>
+ <array>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>PBXSmartGroupTreeModule</string>
+ <key>Name</key>
+ <string>Groups and Files Outline View</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>PBXNavigatorGroup</string>
+ <key>Name</key>
+ <string>Editor</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>XCTaskListModule</string>
+ <key>Name</key>
+ <string>Task List</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>XCDetailModule</string>
+ <key>Name</key>
+ <string>File and Smart Group Detail Viewer</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>1</string>
+ <key>Module</key>
+ <string>PBXBuildResultsModule</string>
+ <key>Name</key>
+ <string>Detailed Build Results Viewer</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>1</string>
+ <key>Module</key>
+ <string>PBXProjectFindModule</string>
+ <key>Name</key>
+ <string>Project Batch Find Tool</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>XCProjectFormatConflictsModule</string>
+ <key>Name</key>
+ <string>Project Format Conflicts List</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>PBXBookmarksModule</string>
+ <key>Name</key>
+ <string>Bookmarks Tool</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>PBXClassBrowserModule</string>
+ <key>Name</key>
+ <string>Class Browser</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>PBXCVSModule</string>
+ <key>Name</key>
+ <string>Source Code Control Tool</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>PBXDebugBreakpointsModule</string>
+ <key>Name</key>
+ <string>Debug Breakpoints Tool</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>XCDockableInspector</string>
+ <key>Name</key>
+ <string>Inspector</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>PBXOpenQuicklyModule</string>
+ <key>Name</key>
+ <string>Open Quickly Tool</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>1</string>
+ <key>Module</key>
+ <string>PBXDebugSessionModule</string>
+ <key>Name</key>
+ <string>Debugger</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>1</string>
+ <key>Module</key>
+ <string>PBXDebugCLIModule</string>
+ <key>Name</key>
+ <string>Debug Console</string>
+ </dict>
+ <dict>
+ <key>BundleLoadPath</key>
+ <string></string>
+ <key>MaxInstances</key>
+ <string>n</string>
+ <key>Module</key>
+ <string>XCSnapshotModule</string>
+ <key>Name</key>
+ <string>Snapshots Tool</string>
+ </dict>
+ </array>
+ <key>BundlePath</key>
+ <string>/Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources</string>
+ <key>Description</key>
+ <string>DefaultDescriptionKey</string>
+ <key>DockingSystemVisible</key>
+ <false/>
+ <key>Extension</key>
+ <string>mode1v3</string>
+ <key>FavBarConfig</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>4CC895FD119F0CCB00ED61B6</string>
+ <key>XCBarModuleItemNames</key>
+ <dict/>
+ <key>XCBarModuleItems</key>
+ <array/>
+ </dict>
+ <key>FirstTimeWindowDisplayed</key>
+ <false/>
+ <key>Identifier</key>
+ <string>com.apple.perspectives.project.mode1v3</string>
+ <key>MajorVersion</key>
+ <integer>33</integer>
+ <key>MinorVersion</key>
+ <integer>0</integer>
+ <key>Name</key>
+ <string>Default</string>
+ <key>Notifications</key>
+ <array/>
+ <key>OpenEditors</key>
+ <array/>
+ <key>PerspectiveWidths</key>
+ <array>
+ <integer>-1</integer>
+ <integer>-1</integer>
+ </array>
+ <key>Perspectives</key>
+ <array>
+ <dict>
+ <key>ChosenToolbarItems</key>
+ <array>
+ <string>active-executable-popup</string>
+ <string>active-platform-popup</string>
+ <string>active-buildstyle-popup</string>
+ <string>NSToolbarFlexibleSpaceItem</string>
+ <string>debugger-enable-breakpoints</string>
+ <string>clean-target</string>
+ <string>build-and-go</string>
+ <string>com.apple.ide.PBXToolbarStopButton</string>
+ <string>NSToolbarFlexibleSpaceItem</string>
+ <string>com.apple.pbx.toolbar.searchfield</string>
+ </array>
+ <key>ControllerClassBaseName</key>
+ <string></string>
+ <key>IconName</key>
+ <string>WindowOfProjectWithEditor</string>
+ <key>Identifier</key>
+ <string>perspective.project</string>
+ <key>IsVertical</key>
+ <false/>
+ <key>Layout</key>
+ <array>
+ <dict>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXBottomSmartGroupGIDs</key>
+ <array>
+ <string>1C37FBAC04509CD000000102</string>
+ <string>1C37FAAC04509CD000000102</string>
+ <string>1C37FABC05509CD000000102</string>
+ <string>1C37FABC05539CD112110102</string>
+ <string>E2644B35053B69B200211256</string>
+ <string>1C37FABC04509CD000100104</string>
+ <string>1CC0EA4004350EF90044410B</string>
+ <string>1CC0EA4004350EF90041110B</string>
+ </array>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CE0B1FE06471DED0097A5F4</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>Files</string>
+ <key>PBXProjectStructureProvided</key>
+ <string>yes</string>
+ <key>PBXSmartGroupTreeModuleColumnData</key>
+ <dict>
+ <key>PBXSmartGroupTreeModuleColumnWidthsKey</key>
+ <array>
+ <real>259</real>
+ </array>
+ <key>PBXSmartGroupTreeModuleColumnsKey_v4</key>
+ <array>
+ <string>MainColumn</string>
+ </array>
+ </dict>
+ <key>PBXSmartGroupTreeModuleOutlineStateKey_v7</key>
+ <dict>
+ <key>PBXSmartGroupTreeModuleOutlineStateExpansionKey</key>
+ <array>
+ <string>29B97314FDCFA39411CA2CEA</string>
+ <string>080E96DDFE201D6D7F000001</string>
+ <string>4CC89620119F0D1E00ED61B6</string>
+ <string>4CC8960C119F0CF000ED61B6</string>
+ <string>4CC89602119F0CDE00ED61B6</string>
+ <string>1C37FABC05509CD000000102</string>
+ </array>
+ <key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
+ <array>
+ <array>
+ <integer>6</integer>
+ <integer>2</integer>
+ <integer>1</integer>
+ <integer>0</integer>
+ </array>
+ </array>
+ <key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
+ <string>{{0, 0}, {259, 1060}}</string>
+ </dict>
+ <key>PBXTopSmartGroupGIDs</key>
+ <array/>
+ <key>XCIncludePerspectivesSwitch</key>
+ <true/>
+ <key>XCSharingToken</key>
+ <string>com.apple.Xcode.GFSharingToken</string>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{{0, 0}, {276, 1078}}</string>
+ <key>GroupTreeTableConfiguration</key>
+ <array>
+ <string>MainColumn</string>
+ <real>259</real>
+ </array>
+ <key>RubberWindowFrame</key>
+ <string>0 59 1920 1119 0 0 1920 1178 </string>
+ </dict>
+ <key>Module</key>
+ <string>PBXSmartGroupTreeModule</string>
+ <key>Proportion</key>
+ <string>276pt</string>
+ </dict>
+ <dict>
+ <key>Dock</key>
+ <array>
+ <dict>
+ <key>BecomeActive</key>
+ <true/>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CE0B20306471E060097A5F4</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>RootViewController.m</string>
+ <key>PBXSplitModuleInNavigatorKey</key>
+ <dict>
+ <key>Split0</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CE0B20406471E060097A5F4</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>RootViewController.m</string>
+ <key>_historyCapacity</key>
+ <integer>0</integer>
+ <key>bookmark</key>
+ <string>4CBB32F6119FF17800F021F6</string>
+ <key>history</key>
+ <array>
+ <string>4CC89639119F0E1500ED61B6</string>
+ <string>4CC8964A119F0F1400ED61B6</string>
+ <string>4CE6546D119F1121000A93D8</string>
+ <string>4CE65483119F116D000A93D8</string>
+ <string>4CE65484119F116D000A93D8</string>
+ <string>4C46CC36119F1668005CE427</string>
+ <string>4C46CC58119F2BC9005CE427</string>
+ <string>4C46CC59119F2BC9005CE427</string>
+ <string>4C46CC5A119F2BC9005CE427</string>
+ <string>4C46CC60119F2ED4005CE427</string>
+ <string>4C46CC61119F2ED4005CE427</string>
+ <string>4C46CC65119F2EF2005CE427</string>
+ </array>
+ </dict>
+ <key>SplitCount</key>
+ <string>1</string>
+ </dict>
+ <key>StatusBarVisibility</key>
+ <true/>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{{0, 0}, {1639, 1073}}</string>
+ <key>RubberWindowFrame</key>
+ <string>0 59 1920 1119 0 0 1920 1178 </string>
+ </dict>
+ <key>Module</key>
+ <string>PBXNavigatorGroup</string>
+ <key>Proportion</key>
+ <string>1073pt</string>
+ </dict>
+ <dict>
+ <key>ContentConfiguration</key>
+ <dict>
+ <key>PBXProjectModuleGUID</key>
+ <string>1CE0B20506471E060097A5F4</string>
+ <key>PBXProjectModuleLabel</key>
+ <string>Detail</string>
+ </dict>
+ <key>GeometryConfiguration</key>
+ <dict>
+ <key>Frame</key>
+ <string>{{0, 1078}, {1639, 0}}</string>
+ <key>RubberWindowFrame</key>
+ <string>0 59 1920 1119 0 0 1920 1178 </string>
+ </dict>
+ <key>Module</key>
+ <string>XCDetailModule</string>
+ <key>Proportion</key>
+ <string>0pt</string>
+ </dict>
+ </array>
+ <key>Proportion</key>
+ <string>1639pt</string>
+ </dict>
+ </array>
+ <key>Name</key>
+ <string>Project</string>
+ <key>ServiceClasses</key>
+ <array>
+ <string>XCModuleDock</string>
+ <string>PBXSmartGroupTreeModule</string>
+ <string>XCModuleDock</string>
+ <string>PBXNavigatorGroup</string>
+ <string>XCDetailModule</string>
+ </array>
+ <key>TableOfContents</key>
+ <array>
+ <string>4CBB32E5119FF14800F021F6</string>
+ <string>1CE0B1FE06471DED0097A5F4</string>
+ <string>4CBB32E6119FF14800F021F6</string>
+ <string>1CE0B20306471E060097A5F4</string>
+ <string>1CE0B20506471E060097A5F4</string>
+ </array>
+ <key>ToolbarConfigUserDefaultsMinorVersion</key>
+ <string>2</string>
+ <key>ToolbarConfiguration</key>
+ <string>xcode.toolbar.config.defaultV3</string>
+ </dict>
+ <dict>
+ <key>ControllerClassBaseName</key>
+ <string></string>
+ <key>IconName</key>
+ <string>WindowOfProject</string>
+ <key>Identifier</key>