I'm kind of a noob so hopefully this isn't too complex. I'm having a problem with my code that has so far stumped and confused me and some other users. I spent some time cleaning my code, and following some tutorials, but I still get past this issue. I just know it is something really simple, so here goes:
Im making an iOS app for my blog site. It goes to the site, pulls an XML document (containing the blog title, brief description, and full article url), and parses it to display in a UITableView. To manage this, there are two custom objects: "PostList", and "Post". Post is just an object that hold the individual post data (url, title, publication date, etc) and PostList has an array that holds the Post objects, as well as some getter/setter/init functions.
That all works well. The view loads and the XML is parsed and displays in the list. The problem arises when the user either taps on a link or scrolls some cells off the screen. As far as I can tell, these are both related to the same problem. In the case of the user tapping on the cell, the view controller does not pass a value to the detailed view controller (prepareForSegue), and in the case of scrolling, the cellForRowAtIndexPath does not reinitialize the cell values.
I believe that this is al due to the same problem, that once the view has been initialized, the existing values in the PostList array are somehow deleted. The debugger says the array still sees objects inside it, but they are all blank objects. I have spent forever trying to figure out why.
If you could give me some ideas why this is happening that would be AMAZING! My code is below. I posted almost all of it since Ive had issues with not posting enough before. Hope this helps. If you need more please ask!
-Thanks in advance ;)
FirstViewController.h
#import <UIKit/UIKit.h>
#import "PostList.h"
#import "Post.h"
@interface FirstViewController : UITableViewController
@property (nonatomic, strong) PostList *postController;
@property (nonatomic, strong) IBOutlet UITableView *tableView;
@property (nonatomic, strong) Post *postToSendToDetailView;
@property (nonatomic, strong) NSString *type;
@end
FirstViewController.m
#import "FirstViewController.h"
#import "DetailViewController.h"
#import "Post.h"
#import "PostList.h"
@interface FirstViewController ()
@end
@implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.type = @"blog";
self.postController = [[PostList alloc] initWithType:self.type];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.postController countOfList];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Post *postAtSighting = [self.postController objectInListAtIndex:indexPath.row];
// Configure the cell...
[[cell textLabel] setText:postAtSighting.name];
[[cell detailTextLabel] setText:postAtSighting.content];
NSLog(@"Cell Initalized");
return cell;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"pushToDetailView"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
DetailViewController *destViewController = segue.destinationViewController;
destViewController.aPost = [self.postController objectInListAtIndex:indexPath.row];
}
}
@end
PostList.h
#import <Foundation/Foundation.h>
#import "Post.h"
@interface PostList : NSObject
-(NSMutableArray *) getPostsofType:(NSString *)type;
-(NSString *) getName: (Post *)aPost;
-(NSUInteger) countOfList;
-(Post *)objectInListAtIndex:(NSUInteger)theIndex;
-(id)initWithType:(NSString *)type;
@property (nonatomic, strong) NSMutableArray *postsArray;
@property (nonatomic) NSString *urlString;
@property (nonatomic) NSUInteger countingIndex;
@end
PostList.m
#import "PostList.h"
#import "TBXML+HTTP.h"
@implementation PostList
- (id)initWithType:(NSString *)type {
self = [super init];
if (self) {
//Creates postArray to store Post objects
self.postsArray = [[NSMutableArray alloc] init];
//Create other resources
self.urlString = [[NSString alloc] init];
self.countingIndex = 0;
[self getPostsofType:type];
}
return self;
}
-(NSString *)getName:(Post *)somePost {
//Gets the Name of the Post object and returns the value
NSString *name = somePost.name;
return name;
}
-(NSMutableArray *)getPostsofType:(NSString *)type {
//Go to the Server and get the posts that are available for the post type selected.
//Determing the post type
if ([type isEqual: @"blog"]) {
self.urlString = @"http://www.biteofanapple.com/blog/feeds/blogFeed.xml";
}
if ([type isEqual: @"bbp"]) {
self.urlString = @"http://www.biteofanapple.com/blog/feeds/blogFeed.xml";
}
//Checks for an existant xmlBlogData.xml
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents directory
NSString* blogXML = [documentsDirectory stringByAppendingPathComponent:@"xmlBlogData.xml"];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:blogXML];
NSData *data = [[NSData alloc] init];
//Determines the best way to handle the xml file
if (fileExists) {
data = [NSData dataWithContentsOfFile:blogXML];
} else {
NSURL *url = [NSURL URLWithString:self.urlString];
//Goes to the Server and reads the XML file of the Posts
data = [NSData dataWithContentsOfURL:url];
if (data != nil) {
NSLog(@" NSData value is not nil");
}
//Saves the XML data to a .dat file in the document's directory
NSError *error;
BOOL succeed = [data writeToFile:[documentsDirectory stringByAppendingPathComponent:@"xmlBlogData.dat"] atomically:YES];
if (!succeed){
// Handle error here
NSLog(@"Error: File was unable to be written to the documents directory.");
[error setValue:@"-1" forKey: @"File unable to be written"];
}
}
//Parses the XML file
TBXML *tbxml = [TBXML newTBXMLWithXMLData:data error:nil];
//Prints the data Variable to NSLog
NSLog(@"%@", [NSString stringWithUTF8String:[data bytes]]);
//Make some placeholder variables
if (tbxml.rootXMLElement == nil) {
NSLog(@"XML Document does not have rootElement. Error -1");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Sorry, there was an error" message:@"Could not find root XML element. Please contact support for help with this issue." delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
[alert show];
}
TBXMLElement *element = tbxml.rootXMLElement;
TBXMLElement *nextElement = element->nextSibling;
NSLog(@"URLs have been set, preparing for parse/input. [PostList.getPosts]");
//Extracts the content of the XML file and saves it to values in the Post Class
do {
/**********
* This loop goes through the XML file looking for <item> tags that hold information
* about the blog posts. It finds <item> tags and scours them for <title>, <description>,
* <pubdate>, and <link> tags to put into the class variables for the Post Class (aPost).
**********/
NSString *stringElement = [TBXML elementName:element];
NSLog(@"%@", stringElement);
//Creates Post variable to put stuff in.
Post *aPost = [[Post alloc] init];
//Sorts through the header junk to find the first <item> tag.
if (![stringElement isEqualToString:@"item"]) {
if (!(element->firstChild)) {
if (!(element->nextSibling)) {
element = nil;
}
element = element->nextSibling;
}
element = element->firstChild;
}
//Once the first <item> tag is found, this code executes.
else {
//Now we move to the first child tag and scour its contents and its siblings
nextElement = [TBXML nextSiblingNamed:@"item" searchFromElement:element];
element = element->firstChild;
do {
//Here it loops over and over until all the parts have been collected.
stringElement = [TBXML elementName:element];
if ([stringElement isEqualToString:@"title"]) {
aPost.name = [TBXML textForElement:element];
}
if ([stringElement isEqualToString:@"description"]) {
aPost.content = [TBXML textForElement:element];
}
if ([stringElement isEqualToString:@"link"]) {
aPost.postURL = [TBXML textForElement:element];
}
if ([stringElement isEqualToString:@"pubdate"]) {
aPost.publicationDate = [TBXML textForElement:element];
}
element = element->nextSibling;
} while (element->nextSibling);
NSLog(@"%@", [self getName:aPost]);
NSLog(@"name %@ content %@", aPost.name, aPost.content);
[self.postsArray insertObject:aPost atIndex:self.countingIndex];
self.countingIndex++;
element = nextElement;
}
} while ((element != nil));
return self.postsArray;
}
-(Post *)objectInListAtIndex:(NSUInteger)theIndex {
return [self.postsArray objectAtIndex:theIndex];
}
-(NSUInteger)countOfList {
return [self.postsArray count];
}
@end
Post.h
#import <Foundation/Foundation.h>
@class Post;
@interface Post : NSObject
@property (nonatomic, weak) NSString *name;
@property (nonatomic, weak) NSString *author;
@property (nonatomic, weak) NSString *publicationDate;
@property (nonatomic, weak) NSString *content;
@property (nonatomic, weak) NSString *postURL;
@end
Post.m
#import "Post.h"
@implementation Post
- (id)init {
self = [super init];
if (self) {
self.name = @"";
self.author = @"";
self.content = @"";
self.publicationDate = @"";
self.postURL = @"";
}
return self;
}
@end