How to access dictionaries within other methods? - ios

Below is the code I am using to try and access the initialised dictionary with another method:
#import "UserFoodData.h"
#implementation UserFoodData
-(void)loadUserFoodData{
NSString *appDataPath = [[NSBundle mainBundle] pathForResource:#"UserFoodData" ofType:#"plist"];
NSMutableDictionary *userFoodDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:appDataPath];
for (userFoodDictionary in userFoodDictionary){
NSLog(#"%#", userFoodDictionary);
}
}
-(void)setName:(NSString *)foodName andCarbs:(NSNumber *)carbAmount{
[userFoodDictionary setObject:foodName forKey:foodName];
}
#end
The error messages I seem to be getting is:
Use of undeclared identifier 'userFoodDictionary'
Now what I believe is wrong is that the compiler believes that there is a possibility that the setName:andCarbs method could but executed before the loadUserFoodData Which is called when the apps main screen has been initialised. Could someone please help me with a work around?

The error messages I seem to be getting is:
Use of undeclared identifier 'userFoodDictionary'
Compiler says:
1. I didn't see any variable like userFoodDictionary locally or instance variable in -(void)setName:(NSString *)foodName andCarbs:(NSNumber *)carbAmount.
2. But you are trying to access it.
and also your for loop has mistakes. please verify my answer
Compiler is trying to look for userFoodDictionary in "UserFoodData" class. Because you are accessing userFoodDictionary outside of loadUserFoodData method. So in order to access outside of local method you must keep that reference in header file then you can access it any where in class.
#interface UserFoodData : NSObject
#property(nonatomic,retain)NSMutableDictionary *userFoodDictionary;
#end
#import "UserFoodData.h"
#implementation UserFoodData
//#synthesize userFoodDictionary; synthesise is automatically entered by compiler
-(void)loadUserFoodData{
NSString *appDataPath = [[NSBundle mainBundle] pathForResource:#"UserFoodData" ofType:#"plist"];
self.userFoodDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:appDataPath];
//Please not down this
for (id aFood in userFoodDictionary){
NSLog(#"%#", aFood);
}
}
-(void)setName:(NSString *)foodName andCarbs:(NSNumber *)carbAmount{
[self.userFoodDictionary setObject:foodName forKey:foodName];
}
#end

I'm assuming you get the error in the second method that you posted.
- (void)setName:(NSString *)foodName andCarbs:(NSNumber *)carbAmount{
[userFoodDictionary setObject:foodName forKey:foodName];
}
userFoodDicitionary is not defined in that method. If you want the dictionary to persist between method calls, store it in a #property.
You should really read some introductory material to Objective-C, you could start with this Apple document

There are multiple issues here...
For one, your for loop has a syntax error...not only that, but you're trying to iterate through a dictionary named userFoodDictionary with an iterator variable also named userFoodDictionary. Change that to something like:
for (id dictKey in userFoodDictionary) {
NSLog(#"%#", dictKey);
}
You're also trying to access a variable out of scope. userFoodDictionary is declared within the scope of loadUserFoodData. After that method completes, the variable is deallocated, so of course you can't access it in setName:andCarbs:. To do that, you need to define it in a wider scope -- make a property, a static variable, an iVar, etc.
There are many lacking OOP concepts here...if you are new to programming, Objective-C is a difficult place to start, and it may throw you for many loops (pun intended).

Try this...
#import "UserFoodData.h"
#implementation UserFoodData
-(void)loadUserFoodData{
NSString *appDataPath = [[NSBundle mainBundle] pathForResource:#"UserFoodData" ofType:#"plist"];
NSMutableDictionary *userFoodDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:appDataPath];
for (userFoodDictionary in userFoodDictionary){
NSLog(#"%#", userFoodDictionary);
}
}
-(void)setName:(NSString *)foodName andCarbs:(NSNumber *)carbAmount{
NSString *appDataPath = [[NSBundle mainBundle] pathForResource:#"UserFoodData" ofType:#"plist"];
NSMutableDictionary *userFoodDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:appDataPath];
[userFoodDictionary setObject:foodName forKey:foodName];
}
#end
Let me know if that helps... :)

I think the issue is your NSMutableDictionary should be ivar that is declare NSMutableDictionary in .h as follow
NSMutableDictionary *userFoodDictionary
in .m
-(void)loadUserFoodData{
NSString *appDataPath = [[NSBundle mainBundle] pathForResource:#"UserFoodData" ofType:#"plist"];
userFoodDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:appDataPath];
for (userFoodDictionary in userFoodDictionary){
NSLog(#"%#", userFoodDictionary);
}
}
-(void)setName:(NSString *)foodName andCarbs:(NSNumber *)carbAmount{
[userFoodDictionary setObject:foodName forKey:foodName];
}

So the issue is in the below line
for (userFoodDictionary in userFoodDictionary){
NSLog(#"%#", userFoodDictionary);
}
replace it with
for (NSMutableDictionary *userFoodDic in userFoodDictionary){
NSLog(#"%#", userFoodDic);
}
May this will solve your issue
ok Got the other issue too
Please declare the userFoodDictionary as one of the property too
as described by #Sebastian in his answer :)

Related

Global NSString without alloc access only in 2 place or in two method and in 3rd place it crash the application

Here NSString *documentsPath declare in globally.
#import "DetailViewController.h"
#import "Information.h"
#interface DetailViewController ()
{
NSString *documentsPath;
NSArray *paths;
}
assigning and accessing in viewDidLoad method.
- (void)viewDidLoad
{
NSLog(#"Enter viewDidLoad DetailViewController");
paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
**documentsPath** = [paths objectAtIndex:0];
NSString *plistPath = [**documentsPath** stringByAppendingPathComponent:#"InfoDetail.plist"];
}
also accessing in settingCellImage method
-(void)settingCellImage:(UITableViewCell *)cell noOfRow:(int)row
{
NSLog(#"Enter in settingCellImage method");
NSLog(#"Doc Path Cell ===>%#",documentsPath);
NSString *imagePath = [**documentsPath** stringByAppendingFormat:[NSString stringWithFormat:#"/%d.png",row]];
}
But when i use it in didSelectRowAtIndexPath method it crash the application.
(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Information *information = [[[Information alloc]initWithNibName:#"Information" bundle:nil]autorelease];
information.infoDict = [test objectAtIndex:indexPath.row];
NSLog(#"Selected row No is ==> %d",indexPath.row);
NSLog(#"Using #=== %d",indexPath.row);
NSLog(#"documents path == %#",**documentsPath**);// here it gives EXE_BAD_ACCESS.
NSString *selectedImagePath = [documentsPath stringByAppendingFormat:[NSString stringWithFormat:#"/%d.png",indexPath.row+1]];
}
I'm new in iPhone development and it is my demo project. so please help me as soon as possible.
and please give me answer in easy way so i can understand very fast. And yes i know already that i'm not alloc init documentsPath direct assign in viewDidLoad method, and i want to know that why it will access in 2 places and in 3rd place it will crash the application. I hope someone can help me. Thank you!
Since you're not using ARC, you have to make sure you are retain/releasing/autoreleasing all your objects. What's happening is that your documentsPath got released and now you are accessing a released object. You could assign the string like so:
documentsPath = [[NSString stringWithString:[paths objectAtIndex:0]] retain];
And then you have to release it in your dealloc call.
I think the problem is you use stringByAppendingFormat two times for documentsPath and then at didSelectRowAtIndexPath when you access to it. App will crash.
Use ARC.
Even if you do use ARC, you should understand the basics of object ownership. Do a search on "Basic Memory Management Rules" in Xcode and read that entire section. Also read the section below, "Practical Memory Management"
To summarize, if you need an object to stick around, you need to retain it/maintain a strong reference to it. Every retain needs to be balanced with a release. This means that when you are done with an object that you have retained, you must release it.
Your code is not following those rules, so you're crashing.
documentsPath has static assign and then i manipulate it with stringByAppendingFormat it creates problem because documentsPath is a immutable and then also you are manipulate it so the application is crash. so the manipulations of immutable object generate problem.Because NSString object by default immutable object you can not change or manipulate assigned string.
Try This instead of when you want to use stringByAppendingFormat .
NSString *imagePath = [[NSString alloc] initWithFormat:#"%#/%d.png", documentsPath,indexPath.row+1];
Use NSString initWithFormat and don't manipulate String.

Error Message NSData

I need to display an RTF file in a UITextView. My code looks like below:
My .h file:
#property (strong, nonatomic) IBOutlet UITextView *creditsTextView;
My .m file
#synthesize creditsTextView;
- (void)viewDidLoad {
[super viewDidLoad];
NSData *creditsData = [[NSBundle mainBundle] pathForResource:#"Credits" ofType:#"rtf"];
NSAttributedString *attrString;
NSDictionary *docAttributes;
attrString = [[NSAttributedString alloc]
initWithRTF: creditsData documentAttributes: &docAttributes];
}
This code gives me the following Error messages:
at
*creditsData : Incompatible pointer types 'NSData *' with expression of type 'NSString *'
and at
initWithRTF : No visible #interface for 'NSAttributedString' declares the selector 'initWithRTF:documentAttributes:'
How can I fix these two errors?
This method:
NSString *path = [[NSBundle mainBundle] pathForResource:#"Credits" ofType:#"rtf"];
Returns the path were your file is.
If, after you have the path, you want to read the file, use this:
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:path]];
Find the documentation here:
NSBundle Class Reference
NSData Class Reference
Also, there's no method called InitWithRTF in NSMutableAttributedString.
[[NSBundle mainBundle] pathForResource: returns type NSString, see NSBundle Class Reference, so you cannot directly assign NSData with NSString, so you can pass the returned URL to NSData with a different initialiser.
And NSAttributedString does not have an init method called initWithRTF. These are why you are seeing the errors.

iphone - String does not keep its value

The string selectedSong does not keep its value when I, in my storyboard switch to another ViewController. Even though all my viewcontroller are connected to the ViewController classes.
I've done like this
First View:
- (IBAction)selectTheSong {
selectedSong = #"test";
NSLog(#"%#", selectedSong);
}
and there it returns the value test in the log.
Second View:
NSLog(#"%#", selectedSong);
if (audioPlayer == nil) {
NSString* soundPath =[[NSBundle mainBundle] pathForResource:selectedSong ofType:#"mp3"];
NSURL *soundURL = [NSURL fileURLWithPath:soundPath];
NSError *error = nil;
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundURL error:&error];
[self.audioPlayer prepareToPlay];
}
[self.audioPlayer play];
and this time it returns (null) in the log.
Is the string not set up properly? I tried making it a property etc. but that didn't work.
Please help! Thanks!
You will need to set the string up as a property with the strong retain type in your class interface.
eg.
#property (nonatomic, strong) NSString *myString;
Also, always access that string instance using the self keyword to ensure the synthesized accessor methods are being called.
i.e.
self.myString = #"Hello!";
Hope that helps!
Using a property is the way to go. How did you pass the value of the string from one ViewController to the other? You should do that in the prepareForSegue: method.

In Objective-C, How Can I Make a Global Configuration That Is Accessible to All?

I am new to Objective-C and iOS development and I'm not sure if I'm doing it right. Anyway, basically I have a file Configs.plist which, for now has two sets of Keys:Value (Customer:Generic and Short_Code:Default). I want these data to be easily accessible to all classes so I created these:
Configs.h
extern NSString * const CUSTOMER;
extern NSString * const SHORT_CODE;
#interface Configs
+ (void)initialize;
+ (NSDictionary *)getConfigs;
#end
Configs.m
#import "Configs.h"
NSString * const CUSTOMER = #"Customer";
NSString * const SHORT_CODE = #"Short_Code";
static NSDictionary *myConfigs;
#implementation Configs
+ (void)initialize{
if(myConfigs == nil){
NSString *path = [[NSBundle mainBundle] pathForResource:#"Configs" ofType:#"plist"];
settings = [[NSDictionary alloc] initWithContentsOfFile:path];
}
}
+ (NSDictionary *)getConfigs{
return settings;
}
#end
And on a the test file Test.m:
NSLog(#"Customer: %#", [[Configs getConfigs] objectForKey:CUSTOMER]);
NSLog(#"Short Code: %#", [[Configs getConfigs] objectForKey:SHORT_CODE]);
The thing is, this approach works but I want to know if there are better ways to do this.
I think this is good as long as your configuration does not change during execution. If it does, you're better off with the singleton exposing your config as properties, so you would be able to do something like this:
[[Config shared] customer];
[[Config shared] setShortCode:#"CODE"];
You could still init the config from the plist, or implement coding protocol to store it in the NSUserDefaults.

iOS5 using ARC: Implicit conversion of int to NSDictionary

i´m using ARC to update my old project.
I have a "Filehelper" class in which i use c-functions e.g. for methods i need in almost every projects. (eg.load plist, etc..)
ViewController
NSDictionary* dictionary = getDictionaryFromPlistWithName(#"TestFile", #"plist");
Filehelper.m
#pragma Returns content of plist as NSDictionary
NSDictionary* getDictionaryFromPlistWithName(NSString* fileName, NSString* pathExtension) {
NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:pathExtension];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:path];
if(fileExists){
NSDictionary* dictionary = [NSDictionary dictionaryWithContentsOfFile:path];
return dictionary;
}
else{
[NSException raise:#"File not found" format:#"path %#", path];
return nil;
}
}
I get this error:
*error: Automatic Reference Counting Issue: Implicit conversion of 'int' to 'NSDictionary ' is disallowed with ARC
Any ideas how to fix it to use it with iOS 5?
I´ve read something that i need to use (__bridge NSDictionary*), but that didn´t help.
PS.
What´s your workflow for classes which you already need? Use C-functions, too?=
What´s the best way?
The most likely problem here is that you forgot to #include your header file. I bet there are further warnings that you're ignoring, particularly one that says something like "unknown signature for getDictionaryFromPlistWithName, assuming it returns int". Read through your warnings and never ignore them (I strongly recommend -Werror for all ObjC projects).

Resources