summaryrefslogtreecommitdiffstats
path: root/Classes/AcidCowFeedburnerParser.m
diff options
context:
space:
mode:
Diffstat (limited to 'Classes/AcidCowFeedburnerParser.m')
-rw-r--r--Classes/AcidCowFeedburnerParser.m207
1 files changed, 207 insertions, 0 deletions
diff --git a/Classes/AcidCowFeedburnerParser.m b/Classes/AcidCowFeedburnerParser.m
new file mode 100644
index 0000000..46d7ec5
--- /dev/null
+++ b/Classes/AcidCowFeedburnerParser.m
@@ -0,0 +1,207 @@
+//
+// AcidCowFeedburnerParser.m
+// PicCast
+//
+// Created by Matthew Handler on 4/15/11.
+// Copyright 2011 Earl Industries. All rights reserved.
+//
+
+#import "AcidCowFeedburnerParser.h"
+#import "Topic.h"
+
+@implementation AcidCowFeedburnerParser
+
+@synthesize currentString, currentTopic, parseFormatter, xmlData, rssConnection, downloadAndParsePool;
+
+
+- (void)downloadAndParse:(NSURL *)url {
+ NSLog(@"downloadAndParse");
+ self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
+ done = NO;
+ self.parseFormatter = [[[NSDateFormatter alloc] init] autorelease];
+ [parseFormatter setDateStyle:NSDateFormatterLongStyle];
+ [parseFormatter setTimeStyle:NSDateFormatterNoStyle];
+ // necessary because iTunes RSS feed is not localized, so if the device region has been set to other than US
+ // the date formatter must be set to US locale in order to parse the dates
+ [parseFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"US"] autorelease]];
+ self.xmlData = [NSMutableData data];
+ //[[NSURLCache sharedURLCache] removeAllCachedResponses];
+ NSURLRequest *theRequest = [NSURLRequest requestWithURL:url];
+ // create the connection with the request and start loading the data
+ rssConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
+ [self performSelectorOnMainThread:@selector(downloadStarted) withObject:nil waitUntilDone:NO];
+ if (rssConnection != nil) {
+ do {
+ [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
+ } while (!done);
+ }
+ self.rssConnection = nil;
+ self.parseFormatter = nil;
+ self.currentTopic = nil;
+ [downloadAndParsePool release];
+ self.downloadAndParsePool = nil;
+}
+
+#pragma mark NSURLConnection Delegate methods
+
+/*
+ Disable caching so that each time we run this app we are starting with a clean slate. You may not want to do this in your application.
+ */
+//- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
+// return nil;
+//}
+
+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
+ done = YES;
+ [self performSelectorOnMainThread:@selector(parseError:) withObject:error waitUntilDone:NO];
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
+ [xmlData appendData:data];
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
+ NSLog(@"finishedLoading");
+ [self performSelectorOnMainThread:@selector(downloadEnded) withObject:nil waitUntilDone:NO];
+ [self convertXMLData];
+
+ NSXMLParser *parser = [[NSXMLParser alloc] initWithData:xmlData];
+ parser.delegate = self;
+ self.currentString = [NSMutableString string];
+ //NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
+ [parser parse];
+ //NSTimeInterval duration = [NSDate timeIntervalSinceReferenceDate] - start;
+ //[self performSelectorOnMainThread:@selector(addToParseDuration:) withObject:[NSNumber numberWithDouble:duration] waitUntilDone:NO];
+ [self performSelectorOnMainThread:@selector(parseEnded) withObject:nil waitUntilDone:NO];
+ [parser release];
+ self.currentString = nil;
+ self.xmlData = nil;
+ // Set the condition which ends the run loop.
+ done = YES;
+}
+
+// needed to convert feedburners xml into utf-8 because the iphone parser breaks on windows-1251
+- (void)convertXMLData {
+ NSString *string = [[NSString alloc] initWithData:xmlData encoding:NSWindowsCP1251StringEncoding];
+ string = [string stringByReplacingOccurrencesOfString:@"encoding=\"windows-1251\"" withString:@""];
+
+ // [xmlData release];
+ // xmlData = nil;
+ xmlData = [[NSMutableData dataWithData:[string dataUsingEncoding:NSUTF8StringEncoding]] retain];
+}
+
+#pragma mark Parsing support methods
+
+static const NSUInteger kAutoreleasePoolPurgeFrequency = 20;
+
+- (void)finishedCurrentTopic {
+ [self performSelectorOnMainThread:@selector(parsedTopic:) withObject:currentTopic waitUntilDone:NO];
+ // performSelectorOnMainThread: will retain the object until the selector has been performed
+ // setting the local reference to nil ensures that the local reference will be released
+ self.currentTopic = nil;
+ countOfParsedTopics++;
+ // Periodically purge the autorelease pool. The frequency of this action may need to be tuned according to the
+ // size of the objects being parsed. The goal is to keep the autorelease pool from growing too large, but
+ // taking this action too frequently would be wasteful and reduce performance.
+ if (countOfParsedTopics == kAutoreleasePoolPurgeFrequency) {
+ [downloadAndParsePool release];
+ self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
+ countOfParsedTopics = 0;
+ }
+}
+
+#pragma mark NSXMLParser Parsing Callbacks
+
+// Constants for the XML element names that will be considered during the parse.
+// Declaring these as static constants reduces the number of objects created during the run
+// and is less prone to programmer error.
+static NSString *kName_Item = @"item";
+static NSString *kName_Title = @"title";
+static NSString *kName_Category = @"category";
+static NSString *kName_Creator = @"dc:creator";
+static NSString *kName_Description = @"description";
+static NSString *kName_ReleaseDate = @"pubDate";
+static NSString *kName_Guid = @"guid";
+
+- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *) qualifiedName attributes:(NSDictionary *)attributeDict {
+ //NSLog(@"%@", elementName);
+ if ([elementName isEqualToString:kName_Item]) {
+ self.currentTopic = [[[Topic alloc] init] autorelease];
+ } else if ([elementName isEqualToString:kName_Title]
+ || [elementName isEqualToString:kName_Category]
+ || [elementName isEqualToString:kName_Guid]
+ || [elementName isEqualToString:kName_Creator]
+ || [elementName isEqualToString:kName_Description]
+ || [elementName isEqualToString:kName_ReleaseDate]) {
+ [currentString setString:@""];
+ storingCharacters = YES;
+ }
+}
+
+- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
+ if ([elementName isEqualToString:kName_Item]) {
+ [self finishedCurrentTopic];
+ } else if ([elementName isEqualToString:kName_Title]) {
+ NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:@"[^(]+"
+ options:NSRegularExpressionCaseInsensitive
+ error:nil];
+ NSArray *results = [regex matchesInString:currentString options:0 range:NSMakeRange(0, [currentString length])];
+ if ([results count] > 0) {
+ NSString *title = [currentString substringWithRange:[[results objectAtIndex:0] range]];
+ currentTopic.title = title;
+ }
+
+ NSRegularExpression* regex2 = [NSRegularExpression regularExpressionWithPattern:@"(?<=\\()\\d+"
+ options:NSRegularExpressionCaseInsensitive
+ error:nil];
+ NSArray *results2 = [regex2 matchesInString:currentString options:0 range:NSMakeRange(0, [currentString length])];
+ if ([results2 count] > 0) {
+ NSString *count = [currentString substringWithRange:[[results2 objectAtIndex:0] range]];
+ currentTopic.picCount = [NSNumber numberWithInt:[count intValue]];
+ //NSLog(@"%@", count);
+ }
+ else
+ currentTopic.picCount = [NSNumber numberWithInt:1];
+
+ //currentTopic.title = currentString;
+ } else if ([elementName isEqualToString:kName_Category]) {
+ currentTopic.category = currentString;
+ } else if ([elementName isEqualToString:kName_Guid]) {
+ currentTopic.guid = currentString;
+ } else if ([elementName isEqualToString:kName_Creator]) {
+ currentTopic.creator = currentString;
+ } else if ([elementName isEqualToString:kName_Description]) {
+ NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:@"http\\S+(jpg|png|gif|jpeg)"
+ options:NSRegularExpressionCaseInsensitive
+ error:nil];
+ NSArray *results = [regex matchesInString:currentString options:0 range:NSMakeRange(0, [currentString length])];
+// for (NSTextCheckingResult *res in results) {
+// NSLog(@"regexed: %@", [currentString substringWithRange:res.range]);
+// }
+ if ([results count] > 0) {
+ NSString *url = [currentString substringWithRange:[[results objectAtIndex:0] range]];
+ //NSLog(@"%@", url);
+ currentTopic.iconUrl = url;
+ }
+ currentTopic.description = currentString;
+ } else if ([elementName isEqualToString:kName_ReleaseDate]) {
+ currentTopic.releaseDate = [parseFormatter dateFromString:currentString];
+ }
+ storingCharacters = NO;
+}
+
+- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
+ //NSLog(@"foundCharacters: %@", string); // storing string is when you want the inner text
+ if (storingCharacters) [currentString appendString:string];
+}
+
+/*
+ A production application should include robust error handling as part of its parsing implementation.
+ The specifics of how errors are handled depends on the application.
+ */
+- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
+ NSLog(@"parseError: %@", parseError);
+ // Handle errors as appropriate for your application.
+}
+
+@end