NSMutableDictionary not working in voids and IBActions - iphone

I created an NSMutableDictionary called *temp in my .h file of the main viewController, and added this code to bring in the information from my .plist file.
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"];
temp=[NSMutableDictionary dictionaryWithContentsOfFile:path];
}
In the same view controller, I added a button action and added the following code:
-(IBAction)mathButton:(UIButton *)_sender
{
label1.text = [temp objectForKey:#"m1name"];
}
Where "label1 is a text field in the .xib, and m1name is one of the keys in the .plist
But when I run it, it doesn't work, and highlights label1.text = [temp objectForKey:#"m1name"]; and calls it bad access.
I've been stuck on this for a couple of days, and tried lots of things. An answer would be really helpful.
Thanks

temp=[NSMutableDictionary dictionaryWithContentsOfFile:path];
You're not retaining the dictionary created via dictionaryWithContentsOfFile:path. You should either change that line to:
temp = [[NSMutableDictionary dictionaryWithContentsOfFile:path] retain];
(and make sure it's released in dealloc), or, if temp is a property, set it via
self.temp = [NSMutableDictionary dictionaryWithContentsOfFile:path];

In .h:
#interface ...
{
NSMutableDictionary* temp;
}
In .m:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString* path = [[NSBundle mainBundle] pathForResource: #"Data"
ofType: #"plist"];
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath: path];
if (exists)
{
temp = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
NSLog(#"%#", [temp description]);
}
}
- (IBAction) mathButton: (UIButton *)_sender
{
label1.text = [temp objectForKey: #"m1name"];
}
If MRC:
- (void) dealloc
{
[temp release];
[super dealloc];
}

Related

Implementing a Search History feature in iOS

I've got a search page at the moment which will load a list of results for a web-service, but when I return to the search page I would like to 'save' whatever was entered (e.g. 'resto italian') and then display that entry and previous entries into a table view below, like in my following image:
My plan was to use property list serialization - if there isn't already a list, create a property list called history.plist, and populate it with each search term that is made, and display the nearest ten in the table view like above.
What I've tried:
// should create history.plist
- (NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingString:#"history.plist"];
}
/* This is the action for when 'search' is clicked - calls the method above to create
a new plist if it's not already created.
I then try to display the contents of the of the file in the textfield itself
(for testing purposes) but it's not saving/displaying properly at the moment. */
- (IBAction)saveHistory:(id)sender {
NSString *filePath = [self dataFilePath];
if([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
for (int i = 0; i < (sizeof(array)); i++) {
UITextField *theField = self.searchHistory;
theField.text = [NSString stringWithFormat:#"%#", array];
}
}
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:app];
}
Any links to tutorials attempting to do this, suggestions towards what I should do, or improvements to what I have would be greatly appreciated.
This should fix the problem:
// This is inside viewDidLoad
UIApplication *myApp = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:myApp];
// This is inside my table view - where I'm loading the file data to display in table cells
NSString *myPath = [self dataFilePath];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if (fileExists) {
NSArray *values = [[NSArray alloc] initWithContentsOfFile:myPath];
for (int i = 0; i < values.count; i++) {
cell.historyDisplay.text = [NSString stringWithFormat:#"%#", [values objectAtIndex:i]];
}
}
// This is the file path for history.plist
- (NSString *)dataFilePath {
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[path objectAtIndex:0] stringByAppendingString:#"history.plist"];
}
// This is my search button - I want to save whatever was typed in the text field, into history.plist, to display in my tableview whenever a user goes to it.
- (IBAction)saveHistory:(id)sender {
NSMutableArray *values = [[NSMutableArray alloc]initWithContentsOfFile:[self dataFilePath]];
if(searchInputTextField.text.length > 0)
[values addObject:searchInputTextField.text];
[values writeToFile:[self dataFilePath] atomically:YES];
[leTableView reloadData];
}
I would use my suggest in comments, but here's some edits to your code that might help in the meantime.
NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
for (int i = 0; i <array.count; i++) {
//I don't know what this line means
UITextField *theField = self.searchHistory;
//Change this line to this
theField.text = [NSString stringWithFormat:#"%#", [array objectAtIndex:i]];
}
I would use Core Data, creating a class, i.e. HistoryRecord with attributes termSearched and timestamp of type NSString and NSDate respectively.
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface HistoryRecordManagedObject : NSManagedObject
#property (nonatomic, retain) NSString *termSearched;
#property (nonatomic, retain) NSDate *timestamp;
+ (NSArray *)findEntity:(NSString *)entity withPredicate:(NSPredicate *)predicate
#end
Implementation
#import "HistoryRecordManagedObject.h"
#implementation HistoryRecordManagedObject
#dynamic termSearched;
#dynamic timstamp;
+ (NSArray *)findEntity:(NSString *)entity withPredicate:(NSPredicate *)predicate
{
NSError *error;
NSArray *fetchedObjects;
/* After set all properties, executes fetch request */
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:entity
inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entityDesc];
[fetchRequest setPredicate:predicate];
fetchedObjects = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
return fetchedObjects;
}
#end
Of course that's not just this! There are some extra stuff that must be done to use Core Data such as create the model. Read a little about it! It's worth!
Good luck!
In the action for searching, just save the search result to NSUserDefaults.
NSMutableArray *searches = [[NSUserDefaults standardUserDefaults] arrayForKey:#"searches"];
[searches insertObject:textField.text atIndex:0];
[[NSUserDefaults standardUserDefaults] setObject:searches forKey:#"searches"];
[[NSUserDefaults standardUserDefaults] synchronize];
Then load the same array for the tables data source and reload the table in viewwillappear and when keyboard is dismissed.
Replace your saveHistory function by below way:
- (IBAction)saveHistory:(id)sender
{
NSMutableArray *values = [[NSMutableArray alloc]initWithContentsOfFile:[self dataFilePath]];
if(searchInputTextField.text.length > 0)
[values addObject:searchInputTextField.text];
[values writeToFile:[self dataFilePath] atomically:YES];
[leTableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return values.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [values objectAtIndex:indexPath.row];
}

Why is my NSMutableDictionary null?

Here's my delegate method for UIAlertView. I have no idea why myWordsDictionary is null when running the app. (Note: _dailyWords is an NSDictionary object and bookmarked is an NSString object.)
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex != [alertView cancelButtonIndex]) {
NSLog(#"Clicked button index !=0");
// Add the action here
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *myWordsPath = [documentPath stringByAppendingPathComponent:#"MyWords.plist"];
NSMutableDictionary *myWordsDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:myWordsPath];
[myWordsDictionary setValue:[_dailyWords objectForKey:bookmarked] forKey:bookmarked];
[myWordsDictionary writeToFile:myWordsPath atomically:YES];
NSLog(#"%#", [myWordsDictionary description]);
[myWordsDictionary release];
} else {
NSLog(#"Clicked button index = 0");
// Add another action here
}
}
Thanks in advance.
myWordsPath may not contain any valid file, and you are initializing your NSMutableDictionary from content of the the file present at that path. That's why you are getting a null dictionary.
First, try check myWordsPath, if it returns null, you should look and find correct path.
Second, how you generate your plist? MyWords.plist must contain valid format. If you unsure, you should create from within XCode, there is plist object.
NSBundle *bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:
#"MyWords" ofType:#"plist"];
NSDictionary *dictionary = [[NSDictionary alloc]
initWithContentsOfFile:plistPath];
please try this code..
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
documentPath = [documentPath stringByAppendingPathComponent:#"MyWords.plist"];
NSMutableDictionary *myWordsDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:documentPath];

Instance Variable not allocating in memory

I'm having an NSObject class where i have an init method defined something like below,
- (id)initWithPlistName:(NSString *)plistFileName{
if (self = [super init]) {
plistName = plistFileName;
plistContent = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle]
pathForResource:plistName ofType:#"plist"]]; // this plistContent array is not allocating in memory
}
return self;
}
I'm calling this method in my applications AppDelegate Class didFinishLaunchingWithOptions method, plistContent is my iVar of type NSArray but whenever control comes to plistContent alloc init line and while returning self, there is no memory allocated for my array.
What may be the problem happening here, Any help is appreciated in advance.
I suppose you have not changed the Datatype of your plist root key in your plist flie from dictionary to array
Check file exists:
NSString *path = [[NSBundle mainBundle] pathForResource:plistName ofType:#"plist"];
if(path)
{
plistContent = [[NSArray alloc] initWithContentsOfFile:path];
}
else
{
NSLog(#"File Not exists");
}
Try this
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"plistName" ofType:#"plist"];
NSDictionary *myDict = [[NSDictionary alloc] initWithContentsOfFile:filePath];
NSArray *array = [NSArray arrayWithArray:[myDict objectForKey:#"Root"]];

old values in pList file get overwritten

Hello there I'm trying to create a dictionary with with phone numbers and I can populate the dictionary and add it to a plist but when I try to add to the plist again I overwrite the file.
#import "FirstViewController.h"
#interface FirstViewController ()
{
NSMutableDictionary *NameNumberDict;
NSDictionary *plistDict;
NSMutableArray *numberArray;
NSString *filePath;
}
#end
#implementation FirstViewController
#synthesize NameTxtField;
#synthesize FirstNumField;
#synthesize SecNumField;
#synthesize personName;
#synthesize phoneNumbers;
- (void)viewDidLoad
{
[super viewDidLoad];
//get some memory
plistDict = [[NSDictionary alloc]init];
NameNumberDict = [[NSMutableDictionary alloc]init];
//make a file path string
NSArray *pathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [pathArray objectAtIndex:0];
filePath = [path stringByAppendingPathComponent:#"data.plist"];
NSFileManager *manager = [NSFileManager defaultManager];
// here i set the Dictionary to the file
//give the file to my dictonary
NameNumberDict = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
NSLog(#"The count: %i", [NameNumberDict count]);
//make sure the file is there
NSString *err = nil;
NSData *plist;
plist = [NSPropertyListSerialization dataFromPropertyList:NameNumberDict format:NSPropertyListBinaryFormat_v1_0 errorDescription:&err];
if([manager fileExistsAtPath:#"data.plist"] == NO)
{
[manager createFileAtPath:[NSString stringWithFormat:#"data.plist"] contents:plist attributes:nil];
}
}
- (void)viewDidUnload
{
[self setNameTxtField:nil];
[self setFirstNumField:nil];
[self setSecNumField:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
// Populate the dictionary when they dismiss the keyboard if all the data is filled out
-(IBAction)TextFieldReturn:(id)TextField
{
if(!NameTxtField.text && !FirstNumField.text && !SecNumField.text);
{
NSString *name = NameTxtField.text;
numberArray = [[NSMutableArray alloc]init];
[numberArray addObject:FirstNumField.text];
[numberArray addObject:SecNumField.text];
[NameNumberDict setObject:name forKey:#"Name"];
[NameNumberDict setObject:numberArray forKey:#"Number"];
NSLog(#"dicSave: %#",NameNumberDict);
}
[TextField resignFirstResponder];
}
// and down here is where im lost Im not sure how to append the data to the Dictionary and write it to file
- (IBAction)AddBtn:(UIButton *)sender
{
plistDict = [[NSMutableDictionary alloc]initWithDictionary:NameNumberDict];
[plistDict writeToFile:filePath atomically:YES];
NSLog (#"File %# exists on iPhone",filePath);
}
#end
In your "TextFieldReturn" method, I see you're always resetting the "Name" and "Number" objects of your "NameNumberDict" mutable dictionary each time it's being called.
I think what you really want to do is add a new dictionary (for each new name & number) to a mutable array and then write that mutable array out to a file (which would end up being your property list file).
Also, just as a style note: Method names and variable names should always start with a lowercase letter (e.g. "textFieldReturn", "addBtn", "nameNumberDict", etc.) while class names (e.g. "FirstViewController") are what start capitalized.

Saving data to a plist file

I am having a little trouble saving to a plist file, when i am reading the data i am using:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return amounts.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//Create Cell
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"cell"];
//Fill cell
NSDictionary *budgetItem = [amounts objectAtIndex:indexPath.row];
cell.textLabel.text = [budgetItem objectForKey:#"value"];
cell.detailTextLabel.text = [budgetItem objectForKey:#"description"];
//Return it
return cell;
}
- (void) loadData{
// Load items
NSString *error;
NSPropertyListFormat format;
NSString *localizedPath = [[NSBundle mainBundle] pathForResource:#"savebudget" ofType:#"plist"];
NSData *plistData = [NSData dataWithContentsOfFile:localizedPath];
NSArray *amountData = [NSPropertyListSerialization propertyListFromData:plistData
mutabilityOption:NSPropertyListImmutable
format:&format
errorDescription:&error];
if (amountData) {
self.amounts = [[NSMutableArray alloc] initWithCapacity:[amountData count]];
for (NSDictionary *amountsDictionary in amountData) {
[self.amounts addObject:amountsDictionary];
}
}
Which works fine from a static plist file with-in my resources folder, but when i try and create my own, nothing seems to happen:
-(void) addData {
NSString *path = [[NSBundle mainBundle] pathForResource:#"saveBudget" ofType:#"plist"];
NSMutableDictionary* plist = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
[plist setValue:amountTxt.text forKey:#"value"];
[plist writeToFile:path atomically:YES];
[plist release];
}
- (IBAction)add:(id)sender {
[self addData];
[self.delegate budgetEnterMinusViewControllerDidFinish:self];
}
Any help more than welcome...
Ugh. Terrible and confusing code. First: use this to load instead:
NSString* path = [[NSBundle mainBundle] pathForResource:#"savebudget" ofType:#"plist"];
NSDictionary* amountData = [NSDictionary dictionaryWithContentsOfFile: path error: NULL];
if (amountData) {
self.amounts = [NSMutableArray arrayWithArray: amountData];
}
Note, no retain or alloc/init here because you are assigning to a retaining property.
So the real problem:
You are reading a plist that that you say contains an array of dictionaries. But then when you add data, you try to write back one single dictionary to that same plist.
Also, in your addData method you do not actually add any data.
And ... If you load your initial data from your app's bundle, then you should write it back to your ~/Documents directory after changing it. And of course read it back from there the next time your app starts.