I have following declarations:
//.h file:
NSMutableArray *dataArray;
NSMutableDictionary *item;
//.m file
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
currentElement = [elementName copy];
if ([elementName isEqualToString:#"table"]){
item = [[NSMutableDictionary alloc] init];
currentEid = [[NSMutableString alloc] init];
currentEname = [[NSMutableString alloc] init];
currentEurl = [[NSMutableString alloc] init];
}
//NSLog(#"didStartElement");
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"table"]) {
// save values to an item, then store that item into the array...
[item setObject:currentEname forKey:#"ename"];
//NSLog(#"%#",item);
[self.dataArray addObject:[item copy]];
NSLog(#"%#",dataArray);
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if ([currentElement isEqualToString:#"eid"]) {
[currentEid appendString:string];
} else if ([currentElement isEqualToString:#"ename"]) {
[currentEname appendString:string];
//NSLog(#"%#",currentEname);
} else if ([currentElement isEqualToString:#"eurl"]) {
[currentEurl appendString:string];
}
// NSLog(#"foundCharacters");
}
I get null values for the NSMutableArray(dataArray) when displayed. How could I solve this?
Or if you could, assist me with knowledge on how to display the NSMutableDicitonary data in table view cells.
You need to make sure that dataArray is created. Put this in your class's init method or wherever else it would be appropriate:
dataArray = [[NSMutableArray alloc] init];
Also, I hope your iPhone has a lot of memory, as your code is leaking a fair bit.
To solve the leaks dreamlax mentioned, you need to add code in front of this:
item = [[NSMutableDictionary alloc] init];
currentEid = [[NSMutableString alloc] init];
currentEname = [[NSMutableString alloc] init];
currentEurl = [[NSMutableString alloc] init];
With something like this:
[item release]; [currentEid release]; [currentEname release]; [currentEurl release];
item = [[NSMutableDictionary alloc] init];
currentEid = [[NSMutableString alloc] init];
currentEname = [[NSMutableString alloc] init];
currentEurl = [[NSMutableString alloc] init];
And also reelase them when you are done parsing...
Don't check my answer as correct though, dreamlax deserves the credit for pondering the code...
For populating a UITableView, you really need to work through some of the UITableView examples and see how they load data.
In your code, you have a number of allocs. These need to be balanced with release. You also have a copy that needs to be balanced with a release. If you don't release these objects, you will end up allocating new objects over and over, and you will eventually run out of memory.
What you need to do is figure out where in your code is a suitable place to release them. When you no longer need the objects that you allocated, you should release them. In your case, it may be sensible to add the following code in your - (void)parser:didEndElement:namespaceURI:qualifiedName: method, like this:
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"table"]) {
// save values to an item, then store that item into the array...
[item setObject:currentEname forKey:#"ename"];
//NSLog(#"%#",item);
[self.dataArray addObject:[[item copy] autorelease]];
NSLog(#"%#",dataArray);
}
[currentEid release];
[currentEname release];
[currentEurl release];
}
Note that I also added an autorelease to your copy. copy useful because it creates an immutable copy of your object, which in this particular case might be what you're after. However, when you create a copy, you are the owner of the copy and you are responsible for releasing it. Your dataArray array will take ownership of your item copy when you add it with addObject:, but you must still relinquish your ownership by releasing it. However, we can't release it before we give it to the array, otherwise the item copy will be deallocated before it is given to dataArray, so it is auto-released instead. An autoreleased object is guaranteed to survive for at least the remainder of the method.
An alternative to autoreleasing is to manually keep track of the item copy, like this:
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"table"]) {
NSDictionary *itemCopy;
// save values to an item, then store that item into the array...
[item setObject:currentEname forKey:#"ename"];
//NSLog(#"%#",item);
itemCopy = [item copy];
[self.dataArray addObject:itemCopy];
[itemCopy release];
NSLog(#"%#",dataArray);
}
[currentEid release];
[currentEname release];
[currentEurl release];
}
Related
I'm following a tutorial and it's working fine but if I wanted to parse 2 RSS feeds it seems to overwrite one array instead of saving them in the respective arrays.
This is in my delegate:
NSURL *url2 = [[NSURL alloc] initWithString:#"myRSSFEED1"];
NSXMLParser *xmlParser1 = [[NSXMLParser alloc] initWithContentsOfURL:url2];
//Initialize the delegate.
XMLParser1 *parser1 = [[XMLParser1 alloc] initXMLParser];
//Set delegate
[xmlParser1 setDelegate:parser1];
//Start parsing the XML file.
BOOL successs = [xmlParser1 parse];
if(successs)
NSLog(#"No Errors");
else
NSLog(#"Error Error Error!!!");
//VIDS
NSURL *url = [[NSURL alloc] initWithString:#"MYRSSFEED2"];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
//Initialize the delegate.
XMLParser *parser = [[XMLParser alloc] initXMLParser];
//Set delegate
[xmlParser setDelegate:parser];
//Start parsing the XML file.
BOOL success = [xmlParser parse];
if(success)
NSLog(#"No Errors");
else
NSLog(#"Error Error Error!!!");
that parses feed 1, allocates it to the array then parses 2 and seems to overwrite the first insteaf of using the second array that are defined as
#synthesize pics;
#synthesize books;
And saved in my XMLParser & XMLParser1
I can't figure out how to stop it from overwriting.
Here is my XMLParsers too:
- (void)parsers:(NSXMLParser *)parsers didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict {
if([elementName isEqualToString:#"Books"]) {
//Initialize the array.
appDelegate2.pics = [[NSMutableArray alloc] init];
}
else if([elementName isEqualToString:#"Book"]) {
//Initialize the book.
apics = [[BookPhoto alloc] init];
//Extract the attribute here.
apics.bookID = [[attributeDict objectForKey:#"id"] integerValue];
NSLog(#"Reading HAVid value :%i", apics.bookID);
}
NSLog(#"Processing Element: %#", elementName);
}
- (void)parsers:(NSXMLParser *)parsers foundCharacters:(NSString *)string {
if(!currentElementValue)
currentElementValue = [[NSMutableString alloc] initWithString:string];
else
[currentElementValue appendString:string];
NSLog(#"Processing Value: %#", currentElementValue);
}
- (void)parsers:(NSXMLParser *)parsers didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if([elementName isEqualToString:#"Books"])
return;
//There is nothing to do if we encounter the Books element here.
//If we encounter the Book element howevere, we want to add the book object to the array
// and release the object.
if([elementName isEqualToString:#"Book"]) {
[appDelegate2.pics addObject:apics];
[apics release];
apics = nil;
}
else
[apics setValue:currentElementValue forKey:elementName];
[currentElementValue release];
currentElementValue = nil;
}
And my XMLParser.m
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict {
if([elementName isEqualToString:#"Books"]) {
//Initialize the array.
appDelegate.books = [[NSMutableArray alloc] init];
}
else if([elementName isEqualToString:#"Book"]) {
//Initialize the book.
aBook = [[Book alloc] init];
//Extract the attribute here.
aBook.bookID = [[attributeDict objectForKey:#"id"] integerValue];
NSLog(#"Reading id value :%i", aBook.bookID);
}
NSLog(#"Processing Element: %#", elementName);
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(!currentElementValue)
currentElementValue = [[NSMutableString alloc] initWithString:string];
else
[currentElementValue appendString:string];
NSLog(#"Processing Value: %#", currentElementValue);
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if([elementName isEqualToString:#"Books"])
return;
//There is nothing to do if we encounter the Books element here.
//If we encounter the Book element howevere, we want to add the book object to the array
// and release the object.
if([elementName isEqualToString:#"Book"]) {
[appDelegate.books addObject:aBook];
[aBook release];
aBook = nil;
}
else
[aBook setValue:currentElementValue forKey:elementName];
[currentElementValue release];
currentElementValue = nil;
}
Any help on this would be brilliant,
I suggest putting a breakpoint at the point where the array is not getting loaded, and seeing if (1) it even gets called, and (2), whether it is saving the data into the array at all.
my bad
seems i had extra s's in parser:
- (void)parsers:(NSXMLParser *)parsers foundCharacters:(NSString *)string {
if(!currentElementValue)
currentElementValue = [[NSMutableString alloc] initWithString:string];
else
[currentElementValue appendString:string];
NSLog(#"Processing Value: %#", currentElementValue);
}
should have been:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(!currentElementValue)
currentElementValue = [[NSMutableString alloc] initWithString:string];
else
[currentElementValue appendString:string];
NSLog(#"Processing Value: %#", currentElementValue);
}
cheers for the help tho
At the moment i've got the following code to get data from an xml file. This works so far
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict{
currentElement = [elementName copy];
if ([elementName isEqualToString:#"Placemark"]) {
placemarkData = [[NSMutableDictionary alloc] init];
currentTitle = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if ([currentElement isEqualToString:#"name"])
[placemarkData addObject:string forKey:currentElement];
if ([currentElement isEqualToString:#"address"])
[placemarkData addObject:string forKey:currentElement];
if ([currentElement isEqualToString:#"coordinates"])
[placemarkData addObject:string forKey:currentElement];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"Placemark"]) {
[Placemarks addObject:[placemarkData copy]];
NSString *batsen = [placemarkData objectForKey:#"name"];
NSLog(#"adding story: %#", batsen);
}
}
This dictionary will be loaded into a array. Problem is that where this array got loaded in need to find the right entry in the array. I do it like this
TabbedCalculationAppDelegate *appDelegate = (TabbedCalculationAppDelegate *)[[UIApplication sharedApplication] delegate];
NSArray *jean = appDelegate.Placemarks;
NSDictionary *boekje = [jean objectAtIndex:1];
NSString *coordinaten = [boekje objectForKey:#"coordinates"];
NSString *naam = [boekje objectForKey:#"name"];
NSLog(#"poep: %#" , naam);
NSLog(#"wwhoppa: %#", coordinaten);
titel.text = coordinaten;
But since arrays works with index I can't find a way to get the right entry. To clarify, a user clicks in a mapview on an annotation. This annotation then gets the corresponding detailview. The detailview can check which annotation is clicked by the view.annotation.title;
So I actually need a dictionary in a dictionary with name as key in the root dictionary. Is this possible? and on what way I can implement this?
Is this a little bit clear? Else just ask!
Thnx guys
Yes, this is a good idea. Just pass the 'inner' dictionary as the object parameter to setObject:forKey: on the 'outer' dictionary. It'll just work. For example:
NSMutableDictionary *outer=[[NSMutableDictionary alloc] init];
NSDictionary *inner=[NSDictionary dictionaryWithObject:#"hello" forKey:#"world"];
[outer setObject:inner forKey:#"greetings"];
I have made a simple rss reader. The app loads an xml atom file in an array.
Now I have added categories to my atom feed, which are first loaded in the array
What is the best way to add drill down functionality programmatically.
Now only the categories are loaded into the array and displayed.
This is the implementation code
.....
loading xml file <snip>
.....
- (void)parserDidStartDocument:(NSXMLParser *)parser {
NSLog(#"found file and started parsing");
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
NSString * errorString = [NSString stringWithFormat:#"Unable to download story feed from web site (Error code %i )", [parseError code]];
NSLog(#"error parsing XML: %#", errorString);
UIAlertView * errorAlert = [[UIAlertView alloc] initWithTitle:#"Error loading content" message:errorString delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
//NSLog(#"found this element: %#", elementName);
currentElement = [elementName copy];
if ([elementName isEqualToString:#"entry"]) {
// clear out our story item caches...
Categoryentry = [[NSMutableDictionary alloc] init];
currentID = [[NSMutableString alloc] init];
currentTitle = [[NSMutableString alloc] init];
currentSummary = [[NSMutableString alloc] init];
currentContent = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
//NSLog(#"ended element: %#", elementName);
if ([elementName isEqualToString:#"entry"]) {
// save values to an entry, then store that item into the array...
[Categoryentry setObject:currentTitle forKey:#"title"];
[Categoryentry setObject:currentID forKey:#"id"];
[Categoryentry setObject:currentSummary forKey:#"summary"];
[Categoryentry setObject:currentContent forKey:#"content"];
[categories addObject:[Categoryentry copy]];
NSLog(#"adding category: %#", currentTitle);
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
//NSLog(#"found characters: %#", string);
// save the characters for the current item...
if ([currentElement isEqualToString:#"title"]) {
[currentTitle appendString:string];
} else if ([currentElement isEqualToString:#"id"]) {
[currentID appendString:string];
} else if ([currentElement isEqualToString:#"summary"]) {
[currentSummary appendString:string];
} else if ([currentElement isEqualToString:#"content"]) {
[currentContent appendString:string];
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
[activityIndicator stopAnimating];
[activityIndicator removeFromSuperview];
NSLog(#"all done!");
NSLog(#"categories array has %d entries", [categories count]);
[newsTable reloadData];
}
Apple provides a good sample app for demonstrating a drill-down tableview:
SimpleDrillDown
Write a generic UITableViewController subclass that takes an array of values. Then, when the user taps on a row, get the array of posts associated with the selected category and pass that to a new view controller. Then, push the view controller onto the stack, using [[self navigationController] pushViewController:nextViewController animated:YES];.
I've just released an open source RSS/Atom Parser for iPhone and hopefully it might be of some use.
I'd love to hear your thoughts on it too!
I am parsing an XML file for two elements: "title" and "noType". Once these are parsed, I am adding them to an object called aMaster, an instance of my own Master class that contains NSString variables.
I am then adding these instances to an NSMutableArray on a singleton, in order to call them elsewhere in the program. The problem is that when I call them, they don't seem to be on the same NSMutableArray index... each index contains either the title OR the noType element, when it should be both... can anyone see what I may be doing wrong? Below is the code for the parser. Thanks so much!!
#import "XMLParser.h"
#import "Values.h"
#import "Listing.h"
#import "Master.h"
#implementation XMLParser
#synthesize sharedSingleton, aMaster;
- (XMLParser *) initXMLParser {
[super init];
sharedSingleton = [Values sharedValues];
aMaster = [[Master init] alloc];
return self;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict {
aMaster = [[Master alloc] init];
//Extract the attribute here.
if ([elementName isEqualToString:#"intro"]) {
aMaster.intro = [attributeDict objectForKey:#"enabled"];
} else if ([elementName isEqualToString:#"item"]) {
aMaster.item_type = [attributeDict objectForKey:#"type"];
//NSLog(#"Did find item with type %#", [attributeDict objectForKey:#"type"]);
//NSLog(#"Reading id value :%#", aMaster.item_type);
} else {
//NSLog(#"No known elements");
}
//NSLog(#"Processing Element: %#", elementName); //HERE
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(!currentElementValue)
currentElementValue = [[NSMutableString alloc] initWithString:string];
else {
[currentElementValue appendString:string];//[tempString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
CFStringTrimWhitespace((CFMutableStringRef)currentElementValue);
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"item"]) {
[sharedSingleton.master addObject:aMaster];
NSLog(#"Added %# and %# to the shared singleton", aMaster.title, aMaster.noType); //Only having one at a time added... don't know why
[aMaster release];
aMaster = nil;
} else if ([elementName isEqualToString:#"title"]) {
[aMaster setValue:currentElementValue forKey:#"title"];
} else if ([elementName isEqualToString:#"noType"]) {
[aMaster setValue:currentElementValue forKey:#"noType"];
//NSLog(#"%# should load into the singleton", aMaster.noType);
}
NSLog(#"delimiter");
NSLog(#"%# should load into the singleton", aMaster.title);
NSLog(#"%# should load into the singleton", aMaster.noType);
[currentElementValue release];
currentElementValue = nil;
}
- (void) dealloc {
[aMaster release];
[currentElementValue release];
[super dealloc];
}
#end
Every time that didStartElement is called, you're setting aMaster to a new instance of the Master class. Based on the implementation of didEndElement, it looks like you should only be creating a new instance whenever a new item tag is found. This could be why each entry in the array has one value or the other, since a new instance is created for each value.
I have a simple RSS reader. Stories are downloaded, put into a UITableView, and when you click it, each story loads in a UIWebView. It works great. Now though, I'd like to incorporate an image on the left side (like you'd see in the YouTube app). However, since my app pulls from an RSS feed, I can't simply specify image X to appear in row X because row X will be row Y tomorrow, and things will get out of order, you know? I am currently pulling from a YouTube RSS feed, and can get the video title, description, publication date, but I'm stuck as to how to pull the little thumbnail besides each entry in the feed.
As you can probably tell, I'm a coding newbie (this being my first application other than a Hello World app) and I'm getting so frustrated by my own lack of knowledge.
Thanks!
BTW, here's some sample code:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
//NSLog(#"found this element: %#", elementName);
if (currentElement) {
[currentElement release];
currentElement = nil;
}
currentElement = [elementName copy];
if ([elementName isEqualToString:#"item"]) {
// clear out our story item caches...
item = [[NSMutableDictionary alloc] init];
currentTitle = [[NSMutableString alloc] init];
currentDate = [[NSMutableString alloc] init];
currentSummary = [[NSMutableString alloc] init];
currentLink = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
//NSLog(#"ended element: %#", elementName);
if ([elementName isEqualToString:#"item"]) {
// save values to an item, then store that item into the array...
[item setObject:currentTitle forKey:#"title"];
[item setObject:currentLink forKey:#"link"];
[item setObject:currentSummary forKey:#"summary"];
[item setObject:currentDate forKey:#"date"];
[stories addObject:[item copy]];
NSLog(#"adding story: %#", currentTitle);
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
//NSLog(#"found characters: %#", string);
// save the characters for the current item...
if ([currentElement isEqualToString:#"title"]) {
[currentTitle appendString:string];
} else if ([currentElement isEqualToString:#"link"]) {
[currentLink appendString:string];
} else if ([currentElement isEqualToString:#"description"]) {
[currentSummary appendString:string];
} else if ([currentElement isEqualToString:#"pubDate"]) {
[currentDate appendString:string];
}
}
I would recommend trying out TouchXML for xml parsing. I find it a lot easier to work with.
Fetch the url for the image like you fetch any other text field. Then when you create the object and add it to the story do something like:
currentImage = [UIImage imageWithData:[NSData dataWithContentsOfURL: [NSURL URLWithString: theUrl]]]
Hope this helps
Have the same problem,but solved with references from Youtube API,
consider this as u r youtube feed,
http://gdata.youtube.com/feeds/base/users/[insert UserName]/uploads
where u will get a xml with media:thumbnail tag which u can used to display thumbnail images of youtube.
Hope it will help u !!!
I would use NSScanner to scan your url from the feed item and get the youtube video id. Then I would then use the video ID to get the thumbnail image from a URL. You can pick which thumbnail URL you want from this answer here:https://stackoverflow.com/a/2068371/1484168