NSUserDefaults set the default value of integer variable to empty - objective-c

I used NSUserDefaults to store an integer value. When I run the project there's a default value of 0 in it. How do I remove the 0 value? I just want it to be empty until the user put something in it.
Here is my code:
- (IBAction)save:(id)sender{
int port = [[portField text] integerValue];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:port forKey:#"port"];
[defaults synchronize];
}
- (void)viewDidLoad {
int port = [defaults integerForKey:#"port"];
NSString *portString = [NSString stringWithFormat:#"%lu",(unsigned long)port];
portField.text = portString;
[super viewDidLoad];
}

Well, this line:
int port = [defaults integerForKey:#"port"];
will always assign some value to port.
Also, this:
NSString *portString = [NSString stringWithFormat:#"%lu",(unsigned long)port];
will always produce a non-empty string. There's no value you could pass for a %lu specifier that will produce an empty string.
So, what you want to do is ask for the object that's stored in the preferences, without converting it to a integer value:
id portObject = [defaults objectForKey:#"port"];
if (portObject)
{
NSInteger port;
if ([portObject respondsToSelector:#selector(integerValue)])
port = [portObject integerValue];
else
{
// portObject is some unexpected class of object, such as an array or dictionary. Take some appropriate action. Or:
port = 0;
}
portField.text = [NSString stringWithFormat:#"%ld", (long)port];
}
else
{
// There was no value either stored or registered for the key "port"
portField.text = #"";
}
Note that for this to work, you must not have registered a default value for the "port" key using -[NSUserDefaults registerDefaults:].

As far as I know, NSUserDefaults always return default values for primitive types when they are not exists.
If the default value is a valid value you use, I suggest to put a blank object or just NSString object to determine if the associated value is valid. e.g.
NSObject *objValid = [userDefaults objectForKey:#"integerIsValid"];
if(objValid != nil) {
// User stored integer value exists.
NSInteger i = [userDefaults integerForKey:#"SomeInteger"];
} else {
// Have not saved integer value.
}
When user save the integer value, also save the associated object:
[userDefaults setObject:[[NSObject alloc]init] forKey:#"integerIsValid"];
[userDefaults setInteger:i forKey:#"SomeInteger"];
UPDATE
If your value range can transform to another value range by some rules, then you can do it another way.
All values are in range [0, 100) for example, you can store in range [1, 101) and retrieve the real value by subtracting 1.

Related

CallKit call block set values from array not working

I am working on one app in which I want a feature to block call. If i am passing static values to addBlockingEntryWithNextSequentialPhoneNumber it's blocking numbers but when i'm passing array in addBlockingEntryWithNextSequentialPhoneNumber it's not blocking numbers. I have logged my array in Appdelegate and it looks fine.
Appdegate Code:
NSArray *sortedArr = [lockedArr sortedArrayUsingSelector:#selector(compare:)];
[DEFAULTS setObject:sortedArr forKey:#"SortedBlockedUsers"];
NSArray<NSNumber *> *blockedPhoneNumbers = [DEFAULTS objectForKey:#"SortedBlockedUsers"];
for (NSNumber *phoneNumber in blockedPhoneNumbers) {
NSLog(#"phoneNumber == %lld", (CXCallDirectoryPhoneNumber)[phoneNumber unsignedLongLongValue]);
}
output is: phoneNumber == 918849494978 phoneNumber == 919142142124
HandlerClass code:
NSArray<NSNumber *> *blockedPhoneNumbers = [[NSUserDefaults standardUserDefaults] objectForKey:#"SortedBlockedUsers"];
//NSArray<NSNumber *> *blockedPhoneNumbers = #[ #918823514521, #919586112211 ];
for (NSNumber *phoneNumber in blockedPhoneNumbers)
{
[context addBlockingEntryWithNextSequentialPhoneNumber:(CXCallDirectoryPhoneNumber)[phoneNumber unsignedLongLongValue]];
}
Finally after debugging my extension class I found that problem is assigning value to array from NSUserDefaults. We must have to save and retrieve value using NSUserDefaults SuiteName to interact with Extension class.

Syntax to check if NSInteger is nil or not?

The following is returning an error because evidently I cannot compare an NSInteger or int to nil. Can anyone suggest syntax to do this
NSInteger *lastID = [[NSUserDefaults standardUserDefaults] integerForKey:#"lastID"]==nil ? #1 : [[NSUserDefaults standardUserDefaults] integerForKey:#"lastID"];
NSInteger lastID = [[NSUserDefaults standardUserDefaults] integerForKey:#"lastID"] == 0 ? 1 : [[NSUserDefaults standardUserDefaults] integerForKey:#"lastID"];
NSInteger is a primitive type. nil is for objects. 0 check, checks is your userDefaults' integer value assigned a value. If it didn't assigned, it returns 0.
NSInteger is a primitive type, not an object. I'm quite sure that an NSInteger* is not what you want, but an NSInteger. integerForKey returns an NSInteger, not an NSInteger*.
NSInteger is a primitive, not an NSObject, so any hypothetical comparison to nil would effectively be comparing to numeric zero.
Since you are using the ternary operator to assign lastID to the boxed NSNumber #1 if the result would be nil, then it seems what you want to do is store an NSNumber in the defaults, use objectForKey: to retrieve it, and use integerValue on the retrieved NSNumber to get the primitive value.
If you do indeed want to store primitives, that's fine, but you'll need to change your logic to not expect nil, which is only pertinent to sublcasses of NSObject, basically.
UPDATE: an example app.
#import <Foundation/Foundation.h>
enum {
MyIdTypeUnrecognized = 0,
MyIdTypeInvalid = 1
} myIdType;
int main(int argc, char *argv[]) {
#autoreleasepool {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *key = #"lastID";
NSInteger lastID = -100;
[defaults removeObjectForKey:key];
NSLog(#"Case 1: should find 0, and set lastID to 1.");
lastID = [defaults integerForKey:key];
lastID = (lastID == MyIdTypeUnrecognized) ? MyIdTypeInvalid : lastID;
NSLog(#"--- lastID:%ld", lastID);
NSLog(#"Case 2: Set lastID to 2, then try to fetch.");
lastID = -100;
[defaults setInteger:2 forKey:key];
[defaults synchronize];
lastID = [defaults integerForKey:key];
lastID = (lastID == MyIdTypeUnrecognized) ? MyIdTypeInvalid : lastID;
NSLog(#"--- lastID:%ld", lastID);
}
}
//2015-09-04 10:04:38.075 nsnotfound[66983:5231434] Case 1: should find 0, and set lastID to 1.
//2015-09-04 10:04:38.076 nsnotfound[66983:5231434] --- lastID:1
//2015-09-04 10:04:38.076 nsnotfound[66983:5231434] Case 2: Set lastID to 2, then try to fetch.
//2015-09-04 10:04:38.076 nsnotfound[66983:5231434] --- lastID:2
I suggested using NSNumber earlier, as your question used a boxed NSNumber -- #1. #1 is simply shorthand for [NSNumber numberWithInteger:1]. If you don't want to use objects, you can definitely use primitives.
The example above uses both zero and one as sentinel values for your "unfound" and "invalid" IDs. All other IDs should be usable by your app logic. I hope this helps.
You can retrieve this number to NSNumber which is an object and that way you can check if it is nil or not.
NSInteger is basically a typecast of Int in 32 bit or long in 64 bit, as you compare int in similar way you can compare NSInteger.It will never be nil.
NSInteger isn't an object, its a primitive type stored as a NSNumber inside NSUserDefaults. Checking for existence of primitive types is a bit tricky, as its non-existence is equivalent to nil or 0. The cleanest way to get its value and get a default value if it doesn't exist is probably this way:
NSNumber *lastID = [[NSUserDefaults standardUserDefaults] objectForKey:#"lastID"] ?: #1;
This would return you lastID if it exists, otherwise you get a NSNumber with a value of 1.

How to get object value from NSMutableDictionary?

I have this bit of code where I put objects into a NSMutableDictionary:
SingletonDictionary *sd = [SingletonDictionary sharedDictionary];
UITextField *tf = (UITextField *)sender;
int tagValue = tf.tag; // get the tag value
[sd.dictionaryOfUserIndexes setObject:tf.text forKey:[NSString stringWithFormat:#"%d", tagValue]]; // value found in textField id'd by tag
UPDATED: This is the code where I'm trying to retrieve the contents of the dictionary:
SingletonDictionary *sd = [SingletonDictionary sharedDictionary]; // initialize
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; // here too...
sd.dictionaryOfUserIndexes = [defaults objectForKey:#"importFileIDs"]; // move from standardUserDefaults to dictionary singleton
NSLog(#"\n\nsd.dictionaryOfUserIndexes: %#", sd.dictionaryOfUserIndexes);
[sd.dictionaryOfUserIndexes[intTagValue]]]; // move contents of textField from dictionary
textField.text = [sd.dictionaryOfUserIndexes objectForKey: tagValue];
This is the contents of sd.dictionaryOfUserIndexes:
sd.dictionaryOfUserIndexes: {
0 = 2;
1 = 5;
2 = 8;
4 = 7;
}
The first column is the tag of the UITextField; the 2nd column is the data in that textField
The contents of the dictionary are valid; I got rid of the build errors, but when I run this, there is nothing in the textField.
I have looked and looked at this, and just hope I didn't do something stupid. Can someone please tell me what's wrong?
I figured it out...
textField.text = [sd.dictionaryOfUserIndexes objectForKey: [tagValue stringValue]];
Thanks everybody for your time; I appreciate it.

Value comparison with NSString - Xcode

I'm trying to compare a value to one stored in a NSString, but I get the following error:
Implicit conversion of int to 'NSArray *' is disallowed with ARC
Here is my code:
NSString *tmplabel = [tmpC description];
temp.text = [NSString stringWithFormat: #"%.f", [tmplabel doubleValue]];
if (tmpC >= 10) {
// perform action
}
else {
// perform action
}
How can I do this comparison? I just want to see if the value stored is bigger than or equal to 10.
Thanks for your time :)

Require Integer Value not Memory Address whilst avoiding Invalid receiver type compiler warning

I have the following code;
int days = [[SettingsUtils daysToRetainHistory] intValue];
[retainHistory setText:[NSString stringWithFormat:#"Days to retain History: %d", days]];
[daysToRetainHistory setValue:days animated:NO];
where [SettingsUtils daysToRetainHistory] is as follows;
+ (int) daysToRetainHistory {
return (int)[[NSUserDefaults standardUserDefaults] objectForKey:#"CaseBaseDaysToRetainHistory"];
}
I get the compiler warning Invalid receiver type 'int' because I call intValue on an int but unless I do this I can't seem to get the integer value out and always end up with the memory address i.e. 98765432 instead of 9 which ruins the UILabel display [retainHistory] and the UISlider [daysToRetainHistory] value.
How do I avoid the compiler warning and still get my integer value in the label and the necessary float value for setting the UISlider value?
It's because you are casting a memory address to an int that int value isn't working. Instead just use the method you want from NSUserDefaults:
+ (NSInteger)daysToRetainHistory
{
return [[NSUserDefaults standardUserDefaults] integerForKey:#"CaseBaseDaysToRetainHistory"];
}
then just use it directly:
NSInteger days = [SettingsUtils daysToRetainHistory];
You should also store your days with the same method:
[[NSUserDefaults standardUserDefaults] setInteger:days forKey:#"CaseBaseDaysToRetainHistory"];
Although if you set the NSNumber object and it actually does contain an integer, that will work fine too.