I am having trouble parsing an XML document to my project. I am getting the following error:
Terminating app due to uncaught exception 'NSUnknownKeyException',
reason: '[ setValue:forUndefinedKey:]: this class is
not key value coding-compliant for the key morph.'
Here is a snippet of my XML document:
<?xml version="1.0" encoding="utf-8"?>
<CAS version="2.1" avatar="anna">
<frames count="111" signCount="3">
<signStart index="0" gloss="mug" />
<frame index="0" isComplete="true" time="0" duration="20" boneCount="74"
morphCount="51">
<morph name="aaa" amount="0" />
<morph name="ooo" amount="0" />
<morph name="pout" amount="0" />
<morph name="eee" amount="0" />
<morph name="cgng" amount="0" />
Here is my code in my XMLParser.h file:
#import <Foundation/Foundation.h>
#class User;
#interface XMLParser : NSObject {
// an ad hoc string to hold element value
NSMutableString *currentElementValue;
// user object
User *user;
// array of user objects
NSMutableArray *times;
NSMutableArray *durations;
NSMutableArray *wheres;
}
#property (nonatomic, retain) User *user;
#property (nonatomic, retain) NSMutableArray *times;
#property (nonatomic, retain) NSMutableArray *durations;
#property (nonatomic, retain) NSMutableArray *wheres;
- (XMLParser *) initXMLParser;
#end
and XMLParser.m
#import "XMLParser.h"
#import "User.h"
#implementation XMLParser
#synthesize user, times, durations, wheres;
- (XMLParser *) initXMLParser {
self = [super init];
if(self){
// init array of user objects
times = [[NSMutableArray alloc] init];
durations = [[NSMutableArray alloc] init];
// wheres = [[NSMutableArray alloc] init];
}
return self;
}
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict {
if(![elementName isEqual:#"frame"])
return;
NSLog(#"user element found – create a new instance of User class...");
user = [[User alloc] init];
//We do not have any attributes in the user elements, but if
// you do, you can extract them here:
NSString *time = [attributeDict objectForKey:#"time"];
NSString *duration = [attributeDict objectForKey:#"duration"];
//NSString *where = [attributeDict objectForKey:#"where"];
[times addObject:time];
[durations addObject:duration];
// [wheres addObject:where];
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (!currentElementValue) {
// init the ad hoc string with the value
currentElementValue = [[NSMutableString alloc] initWithString:string];
} else {
// append value to the ad hoc string
[currentElementValue appendString:string];
}
NSLog(#"Processing value for : %#", string);
}
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"CAS"]) {
// We reached the end of the XML document
NSLog(#"names array: %#", times);
NSLog(#"prices array: %#", durations);
// NSLog(#"wheres array: %#", wheres);
return;
}
if ([elementName isEqualToString:#"frame"]) {
// We are done with user entry – add the parsed user
// object to our user array
//[users addObject:user];
//user = nil;
} else {
// The parser hit one of the element values.
// This syntax is possible because User object
// property names match the XML user element names
[user setValue:currentElementValue forKey:elementName];
}
currentElementValue = nil;
}
I assume it has something to do with my xml file, but am not 100% on the exact problem.
Any help would be much appreciated.
Cheers,
Sam
Offhand it looks like your User object doesn't actually have a property called 'morph'. Double check that you spelled it properly, and it's actually the right class at runtime.
Does your user class implement KVO for the key "morph"?
What your User class needs to have to be considered KVC
Can you post the interface and implementation of your User class? That would be handy.
More about implementing KVO
I discovered the problem was due to this line of code:
[user setValue:currentElementValue forKey:elementName];
I have temporarily removed it from my code until I understand why it is make the program throw an exception
Related
So here is the thing. I am trying to build no-ARC/no-Storyboard project without previous experience in manual memory-management.
Full source code available here.
So what I've got here is a
class, which helps me create Products object with custom initialiser
Products.h
#interface Products : NSObject
#property (nonatomic,retain)NSString *productName;
#property (nonatomic,retain)NSString *productDescription;
#property (nonatomic,retain)NSString *productImage;
-(id) initWithName: (NSString *) name
description: (NSString *) description
image: (NSString *) image;
#end
Products.m
#implementation Products
-(id) initWithName:(NSString *)name description:(NSString *)description image:(NSString *)image {
self = [super init];
if (self) {
self.productName = name;
self.productDescription = description;
self.productImage = image;
}
return self;
#end
There you can see ProductParser class, which one contains most of magic
ProductsParser.h
#interface ProductsParser : NSObject <NSXMLParserDelegate>
#property (nonatomic,retain) NSMutableArray *productArray;
-(id) initWithArray:(NSMutableArray *) productArray;
-(void) parseXMLFile;
#end
ProductParser.m
// Creating private properties
#interface ProductsParser()
#property NSXMLParser *parser;
#property NSString *element;
#property NSMutableString *currentProductName;
#property NSMutableString *currentProductDescription;
#property NSMutableString *currentProductImage;
#end
#implementation ProductsParser
-(id) initWithArray:(NSMutableArray *)productArray {
self = [super init];
if (self) {
self.productArray = productArray;
}
return self;
}
-(void) parseXMLFile {
// We will do it here instead of writing that in viewDidLoad
NSURL *xmlPath = [[NSBundle mainBundle]URLForResource:#"productsList" withExtension:#"xml" ];
self.parser = [[NSXMLParser alloc]initWithContentsOfURL:xmlPath];
self.parser.delegate = self;
[self.parser parse];
[self.parser release];
}
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
self.element = elementName;
}
-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
string = [string stringByReplacingOccurrencesOfString:#"\n" withString:#""];
string = [string stringByReplacingOccurrencesOfString:#"\t" withString:#""];
string = [string stringByReplacingOccurrencesOfString:#" " withString:#""];
if ([self.element isEqualToString:#"Name"]) {
if (self.currentProductName == nil) {
self.currentProductName = [[NSMutableString alloc] initWithString:string];
} else {
[self.currentProductName appendString:string];
}
}
if ([self.element isEqualToString:#"Description"]) {
if (self.currentProductDescription == nil) {
self.currentProductDescription = [[NSMutableString alloc] initWithString:string];
} else {
[self.currentProductDescription appendString:string];
}
} if ([self.element isEqualToString:#"Image"]) {
if (self.currentProductImage == nil) {
self.currentProductImage = [[NSMutableString alloc] initWithString:string];
} else {
[self.currentProductImage appendString:string];
}
}
}
-(void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"Product"]) {
Products *thisProduct = [[Products alloc] initWithName:self.currentProductName description:self.currentProductDescription image:self.currentProductImage];
[self.productArray addObject:thisProduct];
[self.currentProductName release];
self.currentProductName = nil;
[self.currentProductDescription release];
self.currentProductDescription = nil;
[self.currentProductImage release];
self.currentProductImage = nil;
[thisProduct release];
thisProduct = nil;
[self.element release];
self.element = nil;
}
}
#end
Later on, in the class which is suppose to handle the UITableView I am creating:
#property (retain,nonatomic)NSMutableArray *productArray;
And In viewDidLoad we have this code
self.productArray = [[NSMutableArray alloc] init];
ProductsParser *menuParser = [[ProductsParser alloc] initWithArray:self.productArray];
[menuParser parseXMLFile];
[menuParser release];
All that results in EXC_BAD_ACCESS pointing to
#property (nonatomic,retain)NSString *productName;
I've done some research about that error. Users claims that they successfully getting rid of that error by simply calling properties with self. syntax.(which is suppose to increase retain count?) In my case it is not the issue, as you may see.(Or did I missed something?)
I am also can see in debugger that productArray which is suppose to be populated with my products from .xml file, is in fact populated by trash stuff like #"/n/t" #"/n " #"/n/t" and so on.(why why why?!)
So at least point it to me, where to start. Really appreciate your guys help.
UPDATE 1: Apparently there was some defect logic, I almost got rid of the \n\t stuff by changing code in foundCharacters section.
So now instead of just \n\t \n \n\t I got actual data in my array with those afterwards. Like so:
#"Box of chocolate \n\t\t"
#"Box of chocolate, very tasty \n"
#"ImageName \n\t"
I know that \n should be like new line, and \t is apparently tabulation. Still, don't have a clear idea on how to completely get rid of those and why are they popping up.
UPDATE 2: I manage to get rid of the trashy \n\t from the array by adding stringByReplacingOccurrencesOfString. (code updated)
i have a XML file which have 5 items(each have title and url) in it i want retrieve all of them in xcode using xml parsing and then want to store all the entries in arrays so i can use them in application. let me show you all my code.
XML File
<data>
<item>
<title>Eggs</title>
<link>
http://URL/ios/category.php?cat_id=14
</link>
</item>
<item>
<title>Bakery</title>
<link>
http://URL/ios/category.php?cat_id=15
</link>
</item>
<item>
<title>Bread</title>
<link>
http://URL/ios/category.php?cat_id=16
</link>
</item>
<item>
<title>Cakes, Pies Patisserie</title>
<link>
http://URL/ios/category.php?cat_id=17
</link>
</item>
<item>
<title>Specialty Breads</title>
<link>
http://URL/ios/category.php?cat_id=119
</link>
</item>
</data>
Here is my header file
#import "XMLStringFile.h"
#interface MyViewController : UIViewController<NSXMLParserDelegate>{
NSMutableArray *rssOutputData;
NSMutableString *nodecontent;
NSXMLParser *xmlParserObject;
XMLStringFile *xmlStringFileObject;
}
now in xcode here is my viewdidload code
rssOutputData = [[NSMutableArray alloc]init];
NSData *xmlData=[[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:#"http://URL/mycategories.xml"]];
xmlParserObject =[[NSXMLParser alloc]initWithData:xmlData];
[xmlParserObject setDelegate:self];
[xmlParserObject parse];
here is all the parsing methods
#pragma mark NSXMLParser delegate
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"item"]){
xmlStringFileObject =[[XMLStringFile alloc]init];
} else {
nodecontent = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
[nodecontent appendString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
NSLog(#"node content = %#",nodecontent);
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"item"]){
[rssOutputData addObject:xmlStringFileObject];
xmlStringFileObject = nil;
} else if([elementName isEqualToString:#"title"]){
xmlStringFileObject.xmltitle= nodecontent;
nodecontent = nil;
}
else if([elementName isEqualToString:#"link"]){
xmlStringFileObject.xmllink= nodecontent;
nodecontent = nil;
}
}
Here is XMLStringFile.h and XMLStringFile.m
XMLStringFile.h
#import <Foundation/Foundation.h>
#interface XMLStringFile : NSObject {
NSString *xmllink,*xmltitle;
}
#property(nonatomic,retain)NSString *xmllink,*xmltitle;
#end
And XMLStringFile.m file
#import "XMLStringFile.h"
#implementation XMLStringFile
#synthesize xmllink,xmltitle;
-(void)dealloc
{
}
Now i want to store my both entries in two separate arrays titles and links and then i want to retrieve them from those arrays in future. I need help please do let me know how can i do this. for now xmltitle and xmllink storing retrieved data but is only nslog last entire if i use it in another function.
Download XMLReader
In ViewDidLoad method.
- (void)viewDidLoad {
NSData *xmlData=[[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:#"http://cms.proxiclients.com/choithrams/mycategories.xml"]];
NSString *XMLString = [[NSString alloc]initWithData:xmlData encoding:NSUTF8StringEncoding];
XMLString = [XMLString stringByReplacingOccurrencesOfString:#"\n" withString:#""];
NSDictionary *dict = [XMLReader dictionaryForXMLString:XMLString error:nil];
NSLog(#"== %#",dict);
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
you can get dictionary value as below.
NSString *linkText = [[[[dict valueForKey:#"data"] valueForKey:#"item"] valueForKey:#"link"] valueForKey:#"text"];
linkText = [linkText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
Your Output is Dictionary:
I have started a Master Detail application and left the generated code untouched. I created and added two additional classes: a book class(contains an NSString for a title, author, and summary) and also a data controller class(contains a mutable array to store the books).
My understanding of #property attributes after reading Apple doc and others is this:
strong - default, creates ownership of an object
weak - alternative to strong, used to avoid retain cycles
copy - creates a copy of the existing object and takes ownership of that
nonatomic - disregards any sort of thread safety
This code throws a segmentation fault in addBookToList when the #property AJKBook is declared with the copy attribute and I don't understand why.
#interface AJKBookDataController ()
// when current book uses the copy attribute code seg faults in addBookToList
#property (nonatomic) AJKBook *currentBook;
#property (nonatomic, copy) NSString *currentValue;
- (void)populateBookList;
- (void)addBookToBookList;
#end
#implementation AJKBookDataController
- (id)init
{
self = [super init];
if (self) {
_bookList = [[NSMutableArray alloc] init];
_currentBook = [[AJKBook alloc] init];
_currentValue = [[NSString alloc] init];
[self populateBookList];
return self;
}
return nil;
}
- (void)setBookList:(NSMutableArray *)bookList
{
// this bit of code ensures bookList stays mutable
if (_bookList != bookList) {
_bookList = [bookList mutableCopy];
}
}
- (void)populateBookList
{
NSURL *url = [NSURL URLWithString:#"https://sites.google.com/site/iphonesdktutorials/xml/Books.xml"];
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
[parser setDelegate:self];
[parser parse];
NSLog(#"%#", [self.bookList description]);
}
- (void)addBookToBookList
{
[self.bookList addObject:self.currentBook];
self.currentBook = [[AJKBook alloc] init];
}
...
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"title"]) {
// [self.currentBook title:self.currentValue];
self.currentBook.title = self.currentValue;
} else if ([elementName isEqualToString:#"author"]) {
self.currentBook.author = self.currentValue;
} else if ([elementName isEqualToString:#"summary"]) {
self.currentBook.summary = self.currentValue;
} else if ([elementName isEqualToString:#"Book"]) {
[self addBookToBookList];
}
self.currentValue = [NSString stringWithFormat:#""];
}
#end
if you want to use copy for your custom classes you have to implement – copyWithZone: in those classes.
But you don't have to use copy. Often strong is good enough. copy is mostly used for NSString properties because you want to prevent that a NSMutableString is assigned and later changed from outside the class.
You have to think if you really need to copy the current book. If something is named current in my opinion that's a strong indication that you do NOT want to copy. If the only assignment is from [[AJKBook alloc] init]; copy does not make sense at all.
I am trying to grab a token from a website. Upon successful authentication, an XML document would be displayed.
I created a connection as shown below:
NSString *strURLQueryString = [NSString stringWithFormat:#"%#?username=%#&password=%#", kURL_LOGIN, nameString, passwordString];
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:strURLQueryString]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
In order to display the output, I used this:
unsigned char byteBuffer[[receivedData length]];
[receivedData getBytes:byteBuffer];
NSLog(#"Output: %s", (char *)byteBuffer);
So some of the output of the returned document is as shown below:
<status>0</status><reason>User fetched.</reason><token>9cb7396dccabe68c067521db219afb83</token>
I have read many XML parsing implementation but I just could not implement it as it does not fulfil my need, and I just could not understand the complexity of its explanation.
Would appreciate if anyone could give me a good advice on how to go about.
- (XMLParserViewController *) initXMLParser {
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
return self;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"Books"]) {
appDelegate.books = [[NSMutableArray alloc] init];
}
else if([elementName isEqualToString:#"Book"])
{
aBook = [[Books alloc] init];
}
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;
if([elementName isEqualToString:#"Book"])
{
[appDelegate.books addObject:aBook];
aBook = nil;
}
else if([elementName isEqualToString:#"name"])
{
aBook.name=currentElementValue;
}
else if([elementName isEqualToString:#"address"])
{
aBook.address=currentElementValue;
}
else if([elementName isEqualToString:#"country"])
{
aBook.country=currentElementValue;
}
currentElementValue = nil;
NSLog(#"%#",aBook.name);
NSLog(#"%#",aBook.address);
NSLog(#"%#",aBook.country);
}
Try this ,, I hope this works....
The relevant NSXMLParserDelegate methods:
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName //etc
{ _inToken = [elementName isEqualToString:#"token"]; }
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName //etc
{ _inToken = NO; }
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (_inToken) {
_token = string;
[parser abortParsing];
}
}
You should use XML parser.
It is easy to use, go all below steps to get best result:
Create XMLParser class subclasses from NSXMLParser, so you have a NSXMLParser.h and NSXMLParser.m class
your .h class must be like this:
#import <Foundation/Foundation.h>
#interface XMLParser : NSXMLParser <NSXMLParserDelegate>{
NSUInteger parsedCounter;
BOOL accumulatingParsedCharacterData;
BOOL didAbortParsing;
}
#property (nonatomic, strong) NSMutableString *currentParsedCharacterData;
#property (nonatomic, strong) NSMutableArray *currentParsedCharacterArray;
#end
And .m class :
#import "XMLParser.h"
#implementation XMLParser
#pragma mark Parser constants
// Limit the number of parsed data to 100.
static const NSUInteger kMaximumNumberOfFilesToParse = 100;
static NSUInteger const kSizeOfFileBatch = 10;
// Reduce potential parsing errors by using string constants declared in a single place.
static NSString * const kEntryElementName = #"dlResult";
static NSString * const kStringElementName = #"string";
#pragma mark NSXMLParser delegate methods
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
// NSLog(#"didStart");
// If the number of parsed earthquakes is greater than kMaximumNumberOfEarthquakesToParse, abort the parse.
if (parsedCounter >= kMaximumNumberOfFilesToParse) {
// Use the flag didAbortParsing to distinguish between this deliberate stop and other parser errors.
didAbortParsing = YES;
[self abortParsing];
}
if ([elementName isEqualToString:kEntryElementName]) {
_currentParsedCharacterArray = [[NSMutableArray alloc]init];
} else if ([elementName isEqualToString:kStringElementName]) {
accumulatingParsedCharacterData = YES;
_currentParsedCharacterData = [[NSMutableString alloc]init];
}
}
// return string between tags
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
// NSLog(#"foundCh");
if (accumulatingParsedCharacterData) {
// If the current element is one whose content we care about, append 'string'
// to the property that holds the content of the current element.
[_currentParsedCharacterData appendString:string];
NSLog(#"currentParsedCharacterData:%#",_currentParsedCharacterData);
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
// NSLog(#"didEnd");
if ([elementName isEqualToString:kStringElementName]) {
[_currentParsedCharacterArray addObject:_currentParsedCharacterData];
}
accumulatingParsedCharacterData = NO;
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
if (didAbortParsing == NO) {
// Pass the error to the main thread for handling.
[self performSelectorOnMainThread:#selector(handleError:) withObject:parseError waitUntilDone:NO];
}
}
#end
In .m class there are two const string:
static NSString * const kEntryElementName = #"dlResult";
static NSString * const kStringElementName = #"string";
those are string tags,the tags that you should implement are "status", "reason" "token"
from connection class, send data connection to XMLParser class like below:
#autoreleasepool {
// It's also possible to have NSXMLParser download the data, by passing it a URL, but this is not desirable
// because it gives less control over the network, particularly in responding to connection errors.
//
XMLParser *parser = [[XMLParser alloc] initWithData:data];
parser.currentParsedCharacterArray = [NSMutableArray array];
parser.currentParsedCharacterData = [NSMutableString string];
[parser setDelegate:parser];
[parser parse];
// depending on the total number of earthquakes parsed, the last batch might not have been a "full" batch, and thus
// not been part of the regular batch transfer. So, we check the count of the array and, if necessary, send it to the main thread.
if ([parser.currentParsedCharacterArray count] > 0) {
// send parsed data to another class or ...
// parser.currentParsedCharacterArray is parsed data
}
parser.currentParsedCharacterArray = nil;
parser.currentParsedCharacterData = nil;
}
if you have any question, ask me!
I am parsing strings from a feed, converting them to URL, and storing them as a property of FeedItem. Initially, they are successfully converted to URL and stored, but later, when I access the property, it is nil.
FeedItem.h
#interface FeedItem : NSObject
#property (nonatomic, strong) NSString* author;
#property (nonatomic, strong) NSURL* imageURL;
#property (nonatomic, strong) NSString* datePublished;
#end
Parser.m
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict
{
// Custom blog object initialized here
if ([elementName isEqualToString:#"entry"]) {
self.blogEntry = [[FeedItem alloc] init];
}
// Parse image URL that accompanies some blog entries
self.blogEntry.imageURL = [NSURL URLWithString:[attributeDict objectForKey:#"url"]];
if ([[NSURL URLWithString:[attributeDict objectForKey:#"url"]] isKindOfClass:[NSURL class]]) {
NSLog( #"converted to a url" );
if ([self.blogEntry.imageURL isKindOfClass:[NSURL class]]) {
NSLog(#"property of object is url");
}else if (!self.blogEntry.imageURL) {
NSLog(#"url becomes nil");
}else{
NSLog(#"property of object is NOT url");
}
}
}
This prints "converted to a url" and "property of object is url" every time it should. However, later in the same file:
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName {
if([elementName isEqualToString:#"entry"]) {
// An individual blog has been parsed and a pointer to it is added to the parsedResults array
if ([self.blogEntry.imageURL isKindOfClass:[NSURL class]]) {
NSLog( #"URL passed" );
}else if (!self.blogEntry.imageURL) {
NSLog( #"is nil" );
}else{
NSLog(#"no luck");
}
[self.parsedResults addObject:self.blogEntry];
}
}
This prints "is nil" every time.
Here is an example of one of the URLs being parsed:
url='http://2.bp.blogspot.com/-HlNeYKf6Jyk/URKJ0NzA_kI/AAAAAAAAADY/AAkM6mNITlo/s72-c/Ananya's+Illustration.jpg'
I know there can be issues if a URL has a special character, but because it is successful at first, I figured this shouldn't be the issue.
I'm new to objective-c...what am I missing??
My guess is that self.blogEntry is getting deallocated.
This line:
self.blogEntry = [[FeedItem alloc] init];
is replacing what was previously at self.blogEntry. Is it possible your parser calls this more than once before calling the "didEndElement" method?
Or, is blogEntry specified as a strong property on self? If not, it could get deallocated at the end of the method after it's created.
Your following piece of code does not guarantee that the blogEntry object is actually created.
// Parse image URL that accompanies some blog entries
self.blogEntry.imageURL = [NSURL URLWithString:[attributeDict objectForKey:#"url"]];
if ([[NSURL URLWithString:[attributeDict objectForKey:#"url"]] isKindOfClass:[NSURL class]]) {
NSLog( #"converted to a url" );
if ([self.blogEntry.imageURL isKindOfClass:[NSURL class]]) {
NSLog(#"property of object is url");
}else if (!self.blogEntry.imageURL) {
NSLog(#"url becomes nil");
}else{
NSLog(#"property of object is NOT url");
}
}
You are creating the blogEntry object only when the start element is "entry" but you are accessing it outside that block without any check. It's NOT guarantee that when you try to set the image URL the blogEntry object is created.
Though this does not explain why your imageURL object is valid in didStartElement callback and NOT in didEndElement callback. I am just saying it's error prone.
In your parser:didStartElement:namespaceURI:qualifiedName:attributes: method you are setting self.blogEntry.imageURL for every single element. Some elements won't contain a url attribute, and when a dictionary does not contain a specific key it returns nil - wiping out any value you previously stored there.
To overcome this you need code something like this:
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict
{
// Custom blog object initialized here
if ([elementName isEqualToString:#"entry"]) {
self.blogEntry = [[FeedItem alloc] init];
}
// Only allow the element containing the url we want to effect the value
// of imageURL through
if ([elementName isEqualToString:#"media"]) {
self.blogEntry.imageURL = [NSURL URLWithString:[attributeDict objectForKey:#"url"]];
if ([[NSURL URLWithString:[attributeDict objectForKey:#"url"]] isKindOfClass:[NSURL class]]) {
NSLog( #"converted to a url" );
if ([self.blogEntry.imageURL isKindOfClass:[NSURL class]]) {
NSLog(#"property of object is url");
}else if (!self.blogEntry.imageURL) {
NSLog(#"url becomes nil");
}else{
NSLog(#"property of object is NOT url");
}
}
}
}
Consider looking into breakpoints in Xcode to help debug this type of problem.