Modifying NSDictionary in run Time [duplicate] - ios

This question already has answers here:
Mutable Objects inside NSUserDefaults
(4 answers)
Closed 9 years ago.
Please Help me.
I have a plist clothingList.plist
I am accessing it like this in a method
NSString *path=[[NSBundle mainBundle] pathForResource:#"ClothingList" ofType:#"plist"];
//NSDictionary *ClothingAssets ; //Declared globally in .h file
ClothingAssets=[[NSMutableDictionary alloc]initWithContentsOfFile:path];
[[NSUserDefaults standardUserDefaults]setObject:ClothingAssets forKey:#"ClothingAssets"];
[[NSUserDefaults standardUserDefaults] synchronize];
Now I want to modfify a bool value in Clothing Assets Dictionary in another method.
ClothingAssets=[[NSUserDefaults standardUserDefaults] dictionaryForKey:#"ClothingAssets"];
[[[[[[ClothingAssets objectForKey:#"ClothingStore"] objectAtIndex:temp_Store]objectForKey:#"Assets" ]objectForKey:[NSString stringWithFormat:#"%#",temp_AssetType]] objectAtIndex:ii] setValue:#"YES" forKey:#"isLock"] ;
When I run the code For the first time it crashes and Show an error like this:
************ Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'***
*** First throw call stack:
(0x1fb5012 0x29c5e7e 0x1fb4deb 0x1f7b347 0x3f39bf 0x435d9 0x41f76 0xea5020 0x29d9705 0xab5920 0xab58b8 0xb76671 0xb76bcf 0xb75d38 0xae533f 0xae5552 0xac33aa 0xab4cf8 0x3397df9 0x3397ad0 0x1f2abf5 0x1f2a962 0x1f5bbb6 0x1f5af44 0x1f5ae1b 0x33967e3 0x3396668 0xab265c 0x22ed 0x2225 0x1)
libc++abi.dylib: terminate called throwing an exception******
But When I run the code for second time it is working properly.
please help me.

There are many similar questions. From the exception its evident that you are trying to include a value to an immutable dictionary.
Unwrap the values one at a time and since you are going to edit them always make a mutableCopy
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *clothingAssets=[[defaults dictionaryForKey:#"ClothingAssets"] mutableCopy];
NSMutableArray *clothingStores = [clothingAssets[#"ClothingStore"] mutableCopy];
NSMutableDictionary *clothingStore = [clothingStores[temp_Store] mutableCopy];
NSMutableDictionary *assets = [clothingStore[#"Assets"]mutableCopy];
NSString *assetTypesKey = [NSString stringWithFormat:#"%#",temp_AssetType];
NSMutableArray *assetTypes = [assets[assetTypesKey]mutableCopy];
NSMutableDictionary *assetType = [assetTypes[i] mutableCopy];
//Value is set
assetType[#"isLock"] = #"YES";
//Now you need to update the values back to the top most level
[assetTypes replaceObjectAtIndex:i withObject:assetType];
assets[assetTypesKey] = assetTypes ;
clothingStore[#"Assets"] = assets;
[clothingStores replaceObjectAtIndex:temp_Store withObject:clothingStore];
clothingAssets[#"ClothingStore"] = clothingStores;
[defaults setObject:clothingAssets forKey:#"ClothingAssets"];
[defaults synchronize];

Nested message sends aside, this is actually rather straightforward. When you see that error, it means you're trying to mutate an immutable object. The runtime throws a tantrum when you try to do that. Instead, do this:
ClothingAssets=[[NSUserDefaults standardUserDefaults] dictionaryForKey:#"ClothingAssets"];
NSDictionary *clothingStore = [ClothingAssets objectForKey:#"ClothingStore"];
NSArray *assets = [clothingStore objectForKey:#"Assets"];
NSDictionary *subAsset = [assets objectAtIndex: temp_Store];
NSArray *subAssetArray = [subAsset objectForKey: [NSString stringWithFormat:#"%#",temp_AssetType];
// At this point I'm afraid I run out of creative ways to describe your storage hierarchy:
NSDictionary *subAssetArraySubDictionary = [subAssetArray objectAtIndex: ii];
NSMutableDictionary *mutableCopy = [subAssetArraySubDictionary mutableCopy];
// And finally, set the value:
// Beware: This uses the string YES instead of a boolean value for YES - you need to remember this
// when accessing it later.
[mutableCopy setValue: #"YES" forKey: #"isLock"];

Related

Error while converting nsdata to nsstring

I'm trying to convert NSData to NSString
Here's my code
NSData *Data = [[NSUserDefaults standardUserDefaults] objectForKey:#"phoneNumber"];
NSString *getPhone = [NSString stringWithUTF8String:[Data bytes]];
NSLog(#"%#",getPhone);
And the error I'm getting is
-[__NSCFData rangeOfString:]: unrecognized selector sent to instance
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFData rangeOfString:]: unrecognized selector sent to instance
Make the following change:
NSString getPhone = [[NSUserDefaults standardUserDefaults] objectForKey:#"phoneNumber"];
NSLog(#"%#",getPhone);
hm, strange issue. you can print out getPhone directly after set user defaults to understand if some other part of code does not modify it. [[NSUserDefaults standardUserDefaults] setObject:myPhoneString forKey:#"phoneNumber"];
So try this:
NSLog(#"%#", myPhoneString); // check current value
[[NSUserDefaults standardUserDefaults] setObject:myPhoneString forKey:#"phoneNumber"];
NSLog(#"%#", [[NSUserDefaults standardUserDefaults] objectForKey:#"phoneNumber"]) // check value after set it.
values should be the same, but also you will need to print this value in another place to test if some other code have not modified it. (I mean in place where you print it NSLog(#"%#",getPhone);)
Alos be sure that you set NSString to the NSUserDefault instead of data because as I see in your example you set NSString and output NSString as well so you don't need NSData in this your case.
NSString * getPhone = [[NSUserDefaults standardUserDefaults]
stringForKey:#"phoneNumber"];

iOS UIStepper & NSUserDefaults - How to manage them together and use a double

I have the following code. However, the line
stepper.value = [NSNumber numberWithDouble:loadNumber];
errors with the error 'Sending NSNumber *__strong to a parameter of incompatible type 'double''. I think the stepper will only accept a double, but am unsure what I am doing wrong to set it.
What I need to achieve is the following:-
1) Have a default value set in NSUserDefaults for saveNumber, which will be used until a variation is made by the user.
2) on load or view appearing, use saveNumber to set the value of both stepper.value and myLabel.
3) ensure that when changed with the stepper, the value is updated in myLabel, and NSUserDefaults saveNumber is also updated.
Can anyone advise where I am going wrong, and help correct my code please?
Further,I just realised that in the final app, I will have this setting on a different viewController to the one that will use the NSUserDefaults savedNumber value. With this in mind, can you also let me know how to ensure that savedNumber is available not only to this viewController, but any other one I require the values on?
Many thanks.
- (void)viewWillAppear:(BOOL)animated
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSNumber *loadNumber = [defaults objectForKey:#"saveNumber"];
self.myLabel.text = [NSString stringWithFormat:#"%#",loadNumber];
UIStepper *stepper = [[UIStepper alloc] init];
stepper.value = [NSNumber numberWithDouble:loadNumber];
}
- (IBAction)stepChanged:(id)sender {
NSNumber *saveNumber = [NSNumber numberWithDouble:[(UIStepper *)sender value]];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:saveNumber forKey:#"saveNumber"];
[defaults synchronize];
self.myLabel.text = [NSString stringWithFormat:#"%#",saveNumber];
}
I have one outstanding issue which is that I cannot get the defaults to deliver if no value has been set!
I have the following code in AppDelegate.m:-
NSDictionary *defaults = #{ #"saveNumber": #8 };
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
When the App first launches (and every time after until the stepper has been used) the value being seen is 'null'.
Any ideas?
This:
stepper.value = [NSNumber numberWithDouble:loadNumber];
needs to be:
stepper.value = [loadNumber doubleValue];
And you don't need to call synchronize after setting the value in NSUserDefaults.
A value stored in NSUserDefaults can be accessed from any class, not just the class that sets it.
Update:
To set a default value you want the following:
NSDictionary *defaults = #{ #"saveNumber": #6 };
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
This will set a default value of 6 for #"saveNumber".

why iOS App terminating due to NSRangeException

I'm having a weird error popping up. It's listing that my app is
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'
What puzzles me is that the app runs perfectly fine in the simulator, but when I go to test it on my device I get that error. I've isolated the problem to the line of code below:
updatingTracker = [userDatas objectAtIndex:repeatCount];
Here is the rest of the relevant code:
[self getUserDatas];
[self timerDidFire];
-(void) getUserDatas
{
userDatas = [[NSMutableArray alloc] init];
NSUserDefaults *userdef = [NSUserDefaults standardUserDefaults];
int count = [[userdef objectForKey:#"user_count"] intValue];
for(int i=0;i<count;i++)
{
NewTracker *newtrack=[[NewTracker alloc] init];
newtrack.FromCurrency = [userdef objectForKey:[NSString stringWithFormat:#"from_string_%d",i]];
newtrack.ToCurrency = [userdef objectForKey:[NSString stringWithFormat:#"to_string_%d",i]];
newtrack.savedCurrency = [userdef objectForKey:[NSString stringWithFormat:#"save_currency_%d",i]];
newtrack.userTarget = [userdef objectForKey:[NSString stringWithFormat:#"user_target_%d",i]];
newtrack.trackerId = [userdef objectForKey:[NSString stringWithFormat:#"tracker_id_%d",i]];
newtrack.realtimeBase = [userdef objectForKey:[NSString stringWithFormat:#"realtime_base_%d",i]];
newtrack.realtimeTarget = [userdef objectForKey:[NSString stringWithFormat:#"realtime_target_%d",i]];
newtrack.realtimeUSD = [userdef objectForKey:[NSString stringWithFormat:#"realtime_USD_%d",i]];
[userDatas addObject:newtrack];
}
}
- (void) timerDidFire
{
NSLog(#"bug finder");
updatingTracker = [userDatas objectAtIndex:repeatCount];
[self chartconnectionStart:#"3m"];
}
Any ideas or help on why this is happening or how to fix it would be appreciated.
Your userDatas array is empty. You most likely don't have anything populated in your user defaults on your phone, so the code that adds to the array is never executed. You probably have some data already populated on your simulator and that's why it's not working on the phone
Simple put a conditional breakpoint on that line for the condition [userDatas count] == 0. Or put in an if to catch that and a breakpoint on it. They use the debugger to figure out how you got there and what the associated values are.
Basic debugging.
Code like this
(void) timerDidFire{
NSLog(#"bug finder");
if([userDatas count]>0){
updatingTracker = [userDatas objectAtIndex:repeatCount];
[self chartconnectionStart:#"3m"];
}
}
Happy coding..........

NSDictionary leak misunderstanding

Please someone tell me why retaining NSMutabledictionary after some operations is ok, but retain on creation gives a leak.
-(void) Func2
{
NSString *_errorDesc;
NSPropertyListFormat format;
NSString *_plistPath = [[NSBundle mainBundle] pathForResource:#"List" ofType:#"plist"];
NSData *_plistData = [[NSFileManager defaultManager] contentsAtPath:_plistPath];
NSDictionary *dataDict = (NSDictionary *) [NSPropertyListSerialization
propertyListFromData:_plistData
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format
errorDescription:&_errorDesc];
for (int i = 0; i<[dataDict count]; i++)
{
[_ups setObject:[NSArray arrayWithObjects:[NSNumber numberWithInt:0],[NSNumber numberWithInt:0], nil]forKey:[NSString stringWithFormat:#"%d",i]];
}
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:_ups forKey:#"Ups"];
[defaults synchronize];
}
-(void) Func1
{
_ups = [NSMutableDictionary dictionary];
//_ups = [[NSMutableDictionary dictionary]retain]; = leak
//_ups = [[NSMutableDictionary alloc]init]; = leak
if(![[NSUserDefaults standardUserDefaults] objectForKey:#"Ups"])
{
[self Func2];
}else{
_ups = [[NSUserDefaults standardUserDefaults] objectForKey:#"Ups"];
}
[_ups retain]; // - ok
}
Instruments->Leaks shows that leak detected only when I trying to retain on creation, but if I retain after filling a dictionary all fine.
Thanks.
Looking at just one of the if paths (the second) reduces to this:
_ups = [[NSMutableDictionary alloc]init];
// Returns a retained instance assigned to _ups.
_ups = [[NSUserDefaults standardUserDefaults] objectForKey:#"Ups"];
// Now a new instance is assigned to "_ups` without releasing the first instance.
[_ups retain];
//The above retain is incorrect since the method name does not start with "new" or have "copy" in it. This implies that the returned value should be autoreleased. Review Objective-C naming conventions. See Apple's Objective-C Conventions.
All in all, best practice is to use ARC. With ARC the first instance would have been automatically released.

Failed to save double Value in NSUSerDefault

Hi everybody :) i want to save a double Value into NSUserDefault like this:
NSUserDefaults *startValues = [NSUserDefaults standardUserDefaults];
[startValues setDouble:[[self.dataList objectAtIndex:indexPath.row]doubleValue] forKey:#"lat"];
[startValues synchronize];
I get no Errors or Warnings from Xcode, but when i run the Code, it crashes with the Message:
-[__NSCFDictionary doubleValue]: unrecognized selector sent to instance
I also tried to split up the Code like:
double lat = [[self.dataList objectAtIndex:indexPath.row]doubleValue];
[startValues setDouble:lat forKey:#"lat"];
but of course, i get the same error. Whats the Problem?
Thanks for your time and help :)
The problem is that [self.dataList objectAtIndex:indexPath.row] contains a dictionary. NSDictionary does not understand the doubleValue message.
You have to somehow extract the double value from the dictionary:
NSDictionary *dictionary = self.dataList[indexPath.row];
double value = [dictionary[#"latitude"] doubleValue];
[startValues setDouble:value forKey:#"lat"];

Resources