NSMutableArray Add and Save Objects - ios

New to iOS and am stuck on one issue in regards to adding objects in NSMutable Array and displaying the array on another view within the App. The data displays fine on other view in TableView, but when I add another item to the Array (using code below), it just replaces what was there, not adding to the array.
- (void) postArray {
tableData = [[NSMutableArray alloc] initWithCapacity:10];
tableData = [[NSMutableArray alloc] initWithObjects: nil];
[tableData addObject:favShot]; }
-(NSString *) saveFilePath {
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, TRUE);
return [[path objectAtIndex:0] stringByAppendingPathComponent:#"savefile.plist"]; }
- (void) viewWillDisappear:(BOOL)animated: (UIApplication *) application {
NSArray *values = [[NSArray alloc] initWithObjects:tableData, nil];
[values writeToFile:[self saveFilePath] atomically: TRUE]; }
- (void)viewDidLoad {
tableData = [[NSMutableArray alloc] initWithObjects: nil];
NSString *myPath = [self saveFilePath];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if (fileExists)
{
NSArray *values = [[NSArray alloc] initWithContentsOfFile:myPath];
tableData = [values mutable copy];
}
UIApplication *myApp = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:myApp];
[super viewDidLoad]; }
Thank you.

Every time you call postArray you're creating an instance of NSMutableArray, then throwing it away (leaking it if you aren't using ARC). Then you're creating another instance of NSMutableArray. Then you're adding an object (favShot) to that second instance.
Next time you call postArray it's going to throw away your old array and create 2 new ones.
What you want to do is create the tableData instance when you create the controller instance, or when the view loads. Then, don't set tableData = ... as that will discard the old instance. Just add and remove objects.
edit
- (void) postArray {
[tableData addObject:favShot];
}
- (NSString *)saveFilePath {
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, TRUE);
return [[path objectAtIndex:0] stringByAppendingPathComponent:#"savefile.plist"];
}
- (void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[tableData writeToFile:[self saveFilePath] atomically: TRUE];
}
- (void)viewDidLoad {
NSString *myPath = [self saveFilePath];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if (fileExists)
{
tableData = [[NSMutableArray alloc] initWithContentsOfFile:myPath];
} else {
tableData = [[NSMutableArray alloc] initWithObjects: nil];
}
UIApplication *myApp = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:myApp];
[super viewDidLoad];
}

Try
- (void) viewWillDisappear:(BOOL)animated: (UIApplication *) application
{
NSString *filePath = [self saveFilePath];
NSMutableArray *savedArray = [NSMutableArray arrayWithContentsOfFile:filePath];
if (!savedArray) savedArray = [NSMutableArray array];
[savedArray addObject:tableData];
[savedArray writeToFile:filePath atomically:YES];
}

Related

Multiple methods named 'objectAtIndex:' found with mismatched result, parameter type or attributes

I have a table on my ios app that shows last tweets, however after updating my app to 64bit architecture I'm getting four times the following error: multiple methods named 'objectAtIndex:' found with mismatched result, parameter type or attributes error.
This is the relevant part of the code:
-(void)tableView:(UITableView*)tableView didHoldRowAtIndexPath:(NSIndexPath*)indexPath {
if ([NXCatchall isiPad]) {
NSDictionary *tweet = [twitterHandler getTweetForArrayIndex:indexPath.row];
NSDictionary *urls = [[tweet objectForKey:#"entities"] objectForKey:#"urls"];
urlDict = [NSMutableDictionary dictionary];
for (int i=0; i<urls.count; i++) {
NSString *displayName = [[urls valueForKey:#"display_url"] objectAtIndex:i];
NSURL *url = [NSURL URLWithString:[[urls valueForKey:#"expanded_url"]objectAtIndex:i]];
[urlDict setValue:url forKey:displayName];
}
if (urlDict.count != 0) {
UIActionSheet *actionSheet = [[UIActionSheet alloc] init];
[actionSheet setTitle:#"Open in Safari"];
[actionSheet setDelegate:self];
for (int i=0; i<urlDict.count; i++) {
// NSString *key = [[dict allKeys] objectAtIndex:i];
//Yes it's ugly, it works at runtime. Deal with it.
[actionSheet addButtonWithTitle:[[urlDict valueForKey:(NSString*)
[[urlDict allKeys]objectAtIndex:i]]absoluteString]];
}
[[NSNotificationCenter defaultCenter] postNotificationName:#"heldTweetWithInfo" object:self userInfo:[NSDictionary dictionaryWithObject:actionSheet forKey:#"actionSheet"]];
}
}
}
#pragma mark - Table view delegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *tweet = [twitterHandler getTweetForArrayIndex:indexPath.row];
if ([NXCatchall isiPad]) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"selectedTweetNotification" object:self userInfo:tweet];
}
NSDictionary *urls = [[tweet objectForKey:#"entities"] objectForKey:#"urls"];
urlDict = [NSMutableDictionary dictionary];
for (int i=0; i<urls.count; i++) {
NSString *displayName = [[urls valueForKey:#"display_url"] objectAtIndex:i];
NSURL *url = [NSURL URLWithString:[[urls valueForKey:#"expanded_url"]objectAtIndex:i]];
[urlDict setValue:url forKey:displayName];
}
Affected lines start with NSString & NSURL
Try casting it into NSArray. My guess is that some other class declares objectAtIndex.
NSString *displayName = [(NSArray *)[urls valueForKey:#"display_url"] objectAtIndex:i];
NSURL *url = [NSURL URLWithString:[(NSArray *)[urls valueForKey:#"expanded_url"]objectAtIndex:i]];

Replace item in NSMutablearray

I have an NSMutablearray that I have data saved to. Data is input on my main ViewController. When you tap on a saved item, the SecondViewController has a textfield that the data entry is loaded into in order to be edited. I want to be able to edit that item and then save it back to the array.
I am fairly new to Objective-C / Cocoa Touch so please go easy on me.
- (ToDoItem *) updateToDoItem: (ToDoItem *) todoitem {
NSLog(#"ToDoItemSvc.updateToDoItem: %#", [todoitem description]);
NSMutableArray *toDoItems = [NSMutableArray array];
for (NSString *todoitem in toDoItems) {
if ([[ToDoItem] isEqualToString:toDoItems]) {
[toDoItems replaceObjectAtIndex:todoitem];
}
}
I know this isnt completely correct and may be completely off base but Im hoping I am moving in the right direction with this.
EDIT
Here is my ToDoItemSvcArchive.m file
#import "ToDoItemSvcArchive.h"
#implementation ToDoItemSvcArchive
NSString *filePath;
NSMutableArray *toDoItems;
- (id) init {
NSArray *dirPaths =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
filePath = [[NSString alloc] initWithString: [docsDir
stringByAppendingPathComponent: #"ToDoItems.archive"]];
[self readArchive];
return self;
}
- (void) readArchive {
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: filePath])
{
toDoItems = [NSKeyedUnarchiver unarchiveObjectWithFile: filePath];
}
else
{
toDoItems = [NSMutableArray array];
}
}
- (void) writeArchive {
[NSKeyedArchiver archiveRootObject:toDoItems toFile:filePath];
}
- (ToDoItem *) createToDoItem: (ToDoItem *) todoitem {
NSLog(#"ToDoItemSvc.createToDoItem: %#", [todoitem description]);
[toDoItems addObject:todoitem];
[self writeArchive];
return todoitem;
}
- (NSMutableArray *) retrieveAllToDoItems {
return toDoItems;
}
- (ToDoItem *) updateToDoItem: (ToDoItem *) todoitem {
NSLog(#"ToDoItemSvc.updateToDoItem: %#", [todoitem description]);
NSMutableArray *toDoItems = [NSMutableArray array];
for (NSString *todoitem in toDoItems) {
if ([[ToDoItem] isEqualToString:toDoItems]) {
[toDoItems replaceObjectAtIndex:todoitem];
}
}
// return todoitem;
}
- (ToDoItem *) deleteToDoItem: (ToDoItem *) todoitem {
NSLog(#"ToDoItemSvc.deleteToDoItem: %#", [todoitem description]);
[toDoItems removeObject:todoitem];
[self writeArchive];
return todoitem;
}
#end
EDIT
I guess what I dont really understand is how to get the actual index of the object that needs to be replaced.
Edit
I think this is better
- (ToDoItem *) updateToDoItem: (ToDoItem *) todoitem {
NSLog(#"ToDoItemSvc.updateToDoItem: %#", [todoitem description]);
NSUInteger index = [toDoItems indexOfObject:todoitem];
[toDoItems replaceObjectAtIndex:0 withObject:todoitem];
return todoitem;
}
Im still not sure how to specify the correct index.
Over complicating the entire thing
Changed todoitemtext to todoitemobject and made it a todoitem rather than an NSString. Now when I click update, it updates the object and when you go back to the list, the view calls reloaddata and the view is updated with the new object
- (IBAction)updateToDoItem:(id)sender {
NSLog(#"updatingToDoItem: entering");
toDoItemObject.todoitem = toDoItem.text;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
toDoItem.text = toDoItemObject.todoitem;
}
Works like a champ now.

Creating NSMutableArray of objects

I'm having trouble adding objects to an NSMutableArray. I clearly place 2 objects in the typeList array, but the count only shows up as 1. What am I doing wrong?
content.h
#interface TBContentModel : NSObject
+(NSMutableArray*)typeList;
+(void)setTypeList:(NSMutableArray*)str;
content.m
static NSMutableArray *typeList = nil;
#implementation TBContentModel
- (id) init {
self = [super init];
if (self) {
typeList = [NSMutableArray array];
}
return self;
}
contentviewcontroller.m
#implementation TBViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *jsonString = #"[{\"Content\":268,\"type\":\"text\"},{\"Content\":65,\"type\":\"number\"}]";
NSData *data = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSMutableArray *array = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
for (NSMutableDictionary *dictionary in array)
{
TBContentModel *test = [[TBContentModel alloc] init];
test.type = dictionary[#"type"];
[[TBContentModel typeList] addObject:test];
NSLog(#"%#", test.type);
}
}
- (IBAction)tapButton:(id)sender {
NSLog(#"%d", [TBContentModel.typeList count]); // always shows 1
}
You are re-creating your static typeList object everytime you allocate and initialize a new TBContentModel object.
Make the following changes:
static NSMutableArray *typeList = nil;
static dispatch_once_t once;
+ (NSMutableArray*)typeList {
dispatch_once(&once, ^{
typeList = [NSMutableArray array];
});
return typeList;
}
And remove the following line from your init method:
typeList = [NSMutableArray array];

Saved data getting replaced by new data on app relaunch

I am making a calorie counter which displays the total calorie count of various foods added. I have written code to save data following this tutorial. When I relaunch the app I can see the calorie count is the same as when I quit the app but when I add a new food item, the calorie count is reset and the count begins from the start. Why is the value not being saved??
EDIT:
This is a snippet of my code:
if(alertView.tag == ChickenAlertView) {
NSString *buttonTitle = [alertView buttonTitleAtIndex:buttonIndex];
if([buttonTitle isEqualToString:#"Ok"]){
float chicken= ([ChickenText.text floatValue]);
currentnumber = (chicken/100)*219;
result = result + currentnumber;
calories.text= [[NSString alloc] initWithFormat:#"%.f",result];
}
}
From NSLog, i can see the value of result is going to 0 every time I relaunch the app. How do I prevent it?
This is where I save the code:
- (NSString *) saveFilePath {
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[path objectAtIndex:0] stringByAppendingPathComponent:#"savefile.plist"];
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSArray *values = [[NSArray alloc] initWithObjects: NameLabel.text, AgeLabel.text, HeightLabel.text, WeightLabel.text, BMILabel.text, calories.text, [NSNumber numberWithFloat:result], nil];
[values writeToFile:[self saveFilePath] atomically:YES];
[values release];
}
- (void)viewDidLoad
{
foodData = [[foodData alloc]init];
NSString *myPath = [self saveFilePath];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if (fileExists)
{
NSArray *values = [[NSArray alloc] initWithContentsOfFile:myPath];
NameLabel.text = [values objectAtIndex:0];
AgeLabel.text = [values objectAtIndex:1]
HeightLabel.text = [values objectAtIndex:2];
WeightLabel.text = [values objectAtIndex:3];
BMILabel.text = [values objectAtIndex:4];
calories.text = [values objectAtIndex:5];
result = [[values objectAtIndex:6] floatValue];
[values release];
}
UIApplication *myApp = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:myApp];
[super viewDidLoad];
}

When NSDictionary is added to array, it replaces the previous dictionary

UPDATE: Figured out some stuff and changed code.
When I add my NSDictionary to my array it suddenly replaces the previous dictionary I added last time. I don't know why this is happening. I am using a plist as data storage.
I get a error message like this:
Thread 1:Program received signal: "EXC_BAD_ACCESS".
Init
-(id)init{
self=[super init];
if(self){
dbArray = [[NSMutableArray alloc] init];
}
return self;
}
Adding a new item.
-(void)addNewItem:(NSString *)aString
{
// Creates a mutable dictionary with a anonymous string under the NAME key.
NSDictionary *newString = [[NSDictionary alloc] initWithObjectsAndKeys:aString,#"name", nil];
// Adds the new string to empty dbArray.
[dbArray addObject:(newString)];
NSLog(#"[add]:Added anonymous string to dbArray, under name key.");
// Writes the current dbArray (with the dict) to plist and releases retain counts.
[self writeItem];
[newString release];
}
My method to view my data.
-(void)viewData
{
// View data from the created plist file in the Documents directory.
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *finalPath = [documentsDirectory stringByAppendingPathComponent:#"data.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:finalPath]) {
self.dbArray = [NSMutableArray arrayWithContentsOfFile:finalPath];
}
else {
self.dbArray = [NSMutableArray array];
}
}
instead this
self.dbArray = [[NSMutableArray alloc] init];
use this
if( nil == self.dbArray ) {
self.dbArray = [[NSMutableArray alloc] init];
}
UPDATE: (based on provided code)
you're using different instances of DataObject class for displaying & saving data. Your content is over-written, because you don't load data from file during initialization of each instance; to fix that fast, you need to implement init method of your DataObject class as below:
- (id)init{
self = [super init];
if(self){
[self viewData];
}
return self;
}
the following code from viewDidLoad of ViewController class will crash your application very often:
db = [[DataObject alloc] init];
[db viewData];
[db release];
array = [[NSMutableArray alloc] initWithArray:[db dbArray]];
replace it with
db = [[DataObject alloc] init];
[db viewData];
array = [[NSMutableArray alloc] initWithArray:[db dbArray]];
call [db release] only in dealloc implementation
another problem, that you'll probably arise - is updated data is not displayed when you're back to the main screen; to fix that add the following method implementation to your ViewController.m file:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[db viewData];
self.array = [NSMutableArray arrayWithArray: db.dbArray];
[self.tableView reloadData];
}
also in AddView.m replace the following code
// Dismiss view and reload tableview.
ViewController *vc = [[ViewController alloc] init];
[self dismissModalViewControllerAnimated:YES];
[vc release];
with
// Dismiss view and reload tableview.
[self dismissModalViewControllerAnimated:YES];
Just as advise: see more information about using delegates and passing object instances & copies between objects.
I think you are creating a new Array:
self.dbArray = [[NSMutableArray alloc] init];
You should create the dbArray on the viewDidLoad or on the init of your UIViewController (I am assuming you are using this on an UIViewController)
inside your DataObject do the following:
-(id)init{
self=[super init];
if(self){
self.dbArray = [[NSMutableArray alloc] init];
}
return self;
}

Resources