iphone - String does not keep its value - ios

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.

Related

Objective C Properties memory issue

i have to show the user details from NSUserDefaults in more than 5 view controllers. So i have created a NSObject subclass, which will load the user details from server when the first view controllers viewDidLoad is called.
Here is my First view controller viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// Getting the Current User Details
CurrentUserDetails *userDetails = [[CurrentUserDetails alloc]init];
[userDetails initializeTheCurrentUserData];
//CurrentUserDetails is my NSObject class
}
And
#import <Foundation/Foundation.h>
#interface CurrentUserDetails : NSObject
#property(strong,nonatomic) NSString *memberName;
#property(strong,nonatomic) NSString *designation;
#property(strong,nonatomic) NSString *memberType;
#property(strong,nonatomic) NSString *entreprenuer;
#property(strong,nonatomic) NSDate *expiryDate;
#property(strong,nonatomic) NSData *imageData;
- (void) initializeTheCurrentUserData;
#end
and implementation
#implementation CurrentUserDetails
- (void) initializeTheCurrentUserData{
NSData *data = [[NSUserDefaults standardUserDefaults] valueForKey:#"userDictionary"];
NSDictionary *retrievedDictionary = [NSKeyedUnarchiver unarchiveObjectWithData:data];
self.memberName = [retrievedDictionary valueForKey:#"Name"];
self.designation = [retrievedDictionary valueForKey:#"Designation"];
self.memberType = [[retrievedDictionary valueForKey:#"Member_type"] stringValue];
self.expiryDate = [retrievedDictionary valueForKey:#"Expiry"];
self.kanaraEntreprenuer = [retrievedDictionary valueForKey:#"CityName"];
NSString *imageUrl = [retrievedDictionary valueForKey:#"Member_image"];
self.imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#%#",[GlobalVariables getBaseURLForMemberImage],imageUrl]]];
}
And when iam trying to take the details from other class like this..
CurrentUserDetails *userDetails = [[CurrentUserDetails alloc]init];
memberName = userDetails.memberName;
designation = userDetails.designation;
memberType = userDetails.memberType;
dateFromServer = userDetails.expiryDate;
entreprenuer = userDetails.entreprenuer;
imageDataFromServer = userDetails.imageData;
I am getting nil values.
But if call initializeTheCurrentUserData method each time, i am getting the exact values. I though once a property is assigned with a value , we can use the property for entire program. I'm getting confusion.. Can anyone please tell me about this????. Do i need to call initializeTheCurrentUserData everytime when i want to use the values?
Once you set a property of an instance, that property remains for that instance. You, however, are creating new instances with [[CurrentUserDetails alloc] init]. Each new instance will be initialized with default values (nil for NSString).
Call -initializeTheCurrentUserData in -init so each instance will be initialized with the values from user defaults.
#implementation CurrentUserDetails
- (instancetype)init {
self = [super init];
if (self != nil) {
[self initializeTheCurrentUserData];
}
return self;
}
- (void)initializeTheCurrentUserData {
…
}

OCMock: how to stub -[NSString stringWithContentsOfURL:encoding:error:]

I've tried many things. This still does not work:
id stringMock = OCMClassMock([NSString class]);
[[[[stringMock stub] ignoringNonObjectArgs] stringWithContentsOfURL:[OCMArg anyPointer] encoding:NSUTF8StringEncoding error:(NSError * __autoreleasing *)[OCMArg anyPointer]] andReturn:stringFromFile];
How can I make -[NSString stringWithContentsOfURL:encoding:error:] return stringFromFile anywhere in my code?
Unfortunately, it's not possible.
From OCMock documentation about the limitations (section 10):
10.5 Class methods on NSString cannot be stubbed or verified
id stringMock
= OCMClassMock([NSString class]); // the following will not work OCMStub([stringMock stringWithContentsOfFile:[OCMArg any]
encoding:NSUTF8StringEncoding error:[OCMArg setTo:nil]]);
It is not
possible to stub or verify class methods on NSString. Trying to do so
has no effect.
You can, however, wrap NSString and mock the wrapper. It means that you'll need to replace all the calls in your code with this wrapper, but the tests would work..
Here's an example:
The wrapper:
#interface NSStringWrapper : NSObject
+ (nullable id)stringWithContentsOfURL:(NSURL *)url;
#end
#implementation NSStringWrapper
+ (nullable id)stringWithContentsOfURL:(NSURL *)url
{
return [NSString stringWithContentsOfURL:url];
}
#end
The test:
static NSString *stringFromFile = #"myStringFromFile";
-(void)testStringWrapper
{
id nsstringWrapperMock = OCMClassMock([NSStringWrapper class]);
OCMStub(ClassMethod([nsstringWrapperMock stringWithContentsOfURL:OCMOCK_ANY])).andDo(^(NSInvocation *invocation)
{
[invocation setReturnValue:&stringFromFile];
});
NSURL *fakeURL = [NSURL URLWithString:#"htttp://google.com"];
NSString *test = [NSStringWrapper stringWithContentsOfURL:fakeURL];
XCTAssertEqual(test, stringFromFile, #"Should always return stringFromFile");
}
Another option is to try OCMockito, which mocks class methods differently than OCMock.
__strong Class mockStringClass = mockClass([NSString class]);
[[given([mockStringClass stringWithContentsOfFile:anything()
encoding:NSUTF8StringEncoding
error:NULL])
withMatcher:anything() forArgument:2]
willReturn:stringFromFile];
Unlike OCMock which swizzles class methods, this requires you to inject the class. That is, instead of production code making a direct call like
str = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
you need to do something like
str = [self.stringClass stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
You should be able to add OCMockito without disrupting your existing use of OCMock.

How to access dictionaries within other methods?

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 :)

Trying to Track Down Where JSON Dictionary Value Needs to Be Implemented iOS

I have the following method which is creating a URL to an image:
-(NSURL*)urlForImageWithId:(NSNumber*)IdPhoto isThumb:(BOOL)isThumb {
NSString* urlString = [NSString stringWithFormat:#"%#/%#upload/%#%#.jpg",
kAPIHost, kAPIPath, IdPhoto, (isThumb)?#"-thumb":#""
];
return [NSURL URLWithString:urlString];
}
I need to update the path to include the IdUser value that is associated with said image. Here is the way that I have attempted this:
-(NSURL*)urlForImageWithId:(NSNumber*)IdPhoto isThumb:(BOOL)isThumb {
NSString* urlString = [NSString stringWithFormat:#"%#/%#upload/%#/%#%#.jpg",
kAPIHost, kAPIPath, IdUser, IdPhoto, (isThumb)?#"-thumb":#""
];
return [NSURL URLWithString:urlString];
}
When I tried to do this, xcode says “Declare IdUser,” so I did. I Added this to the .h
#property (assign, nonatomic) NSNumber* IdUser;
and I added this below implementation:
#synthesize IdUser;
but when I run the program I get a null value for IdUser.
When I log the dictionary like this:
NSLog(#"Result from Stream: %#",json);
I get the following output:
result = (
{
IdPhoto = 5;
IdUser = 7;
title = this is the title;
username = presto;
});
}
as one example. Here you can clearly see that the IdUser value is being passed through the dictionary. My guess is that the IdPhoto from -(NSURL*)urlForImageWithId:(NSNumber*)IdPhoto isThumb:(BOOL)isThumb {
must be defined somewhere else. Any idea on what I should look for to track this down and be able to pull that IdUser value over as well?
Just adding a property does not set the value of that property. Where is self.IdUser being initiated? It sounds like you are confusing a key of a dictionary with a ViewController property.
Try this to see if that is the case (do this after you get that NSArray named json).
NSDictionary *dict = [json objectAtIndex:0];
self.IdUser = [dict objectForKey:#"IdUser"];
NSLog(#"self.IdUser = %#", self.IdUser];
if idUser is an instance variable, you need to write it as _IdUser or self.IdUser.
Also try: [_idUser stringValue] inside the NSString stringWithFormat method to get the correct type as you specified it as a string ("#").

TableViewController crashes when calling a retained property?

I have a Table View Controller and during its initialisation I set an NSArray property which is then used in the cellForRowAtIndexPath method to display the data on the table.
But, when I touch a row, once I call this retained NSArray property it says EXC_BAD_ACCESS!
FYI the property is defined as shown below, and uses a custom getter function:
#property (nonatomic,retain) NSArray *dataList;
and in the .m file:
#synthesize dataList;
- (NSArray *)dataList
{
if (!dataList)
{
NSString *p = [kind lowercaseString];
NSString *s = [[NSBundle mainBundle] pathForResource:p ofType:#"txt"];
NSLog(#"%#",s);
NSData *dataRep = [NSData dataWithContentsOfFile:s];
NSPropertyListFormat format;
dataList = [NSPropertyListSerialization propertyListFromData: dataRep
mutabilityOption: NSPropertyListImmutable
format: &format
errorDescription: nil];
if (dataList.count == 0)
NSLog(#"Fetch failed!");
}
return dataList;
}
Any suggestions?
This is the problem:
dataList = [NSPropertyListSerialization propertyListFromData ...
This function does not begin with alloc, copy, or retain, therefore it returns an autoreleased object. However, you need it to be retained so that it stays around.
You have two options:
self.dataList = [NSPropertyListSerialization propertyListFromData ...
or,
dataList = [[NSPropertyListSerialization propertyListFromData ...] retain];

Resources