NSUserDefaults - How to tell if a key exists - ios

I'm working on a small iPhone app, and I am using NSUserDefaults as my data persistence. It only has to keep track of a few things, such as some names and some numbers so I figure I might as well keep it simple.
I found this page for some reference, but I don't think it can answer my question. Basically, I want to be able to check if a value (or a key) already exists in the NSUserDefaults and then do something accordingly.
Some examples: The app starts up, if this is the first time it starts up it outputs an alert saying welcome. To tell if this is first time it has opened it reads the UserDefaults and checks.
Example 2: It says, "Hello [Name]", where Name is something you have entered. If you have opened the app and there is no name, it should say "Hello World." I need to check if you have entered a name already and act accordingly. The name would be stored in NSUserDefaults.
Some help here? I'd really appreciate it!

objectForKey: will return nil if it doesn't exist.

As mentioned above it wont work for primitive types where 0/NO could be a valid value. I am using this code.
NSUserDefaults *defaults= [NSUserDefaults standardUserDefaults];
if([[[defaults dictionaryRepresentation] allKeys] containsObject:#"mykey"]){
NSLog(#"mykey found");
}

The objectForKey: method will return nil if the value does not exist. Here's a simple IF / THEN test that will tell you if the value is nil:
if([[NSUserDefaults standardUserDefaults] objectForKey:#"YOUR_KEY"] != nil) {
...
}

Swift 3 / 4:
Here is a simple extension for Int/Double/Float/Bool key-value types that mimic the Optional-return behavior of the other types accessed through UserDefaults.
(Edit Aug 30 2018: Updated with more efficient syntax from Leo's suggestion.)
extension UserDefaults {
/// Convenience method to wrap the built-in .integer(forKey:) method in an optional returning nil if the key doesn't exist.
func integerOptional(forKey: String) -> Int? {
return self.object(forKey: forKey) as? Int
}
/// Convenience method to wrap the built-in .double(forKey:) method in an optional returning nil if the key doesn't exist.
func doubleOptional(forKey: String) -> Double? {
return self.object(forKey: forKey) as? Double
}
/// Convenience method to wrap the built-in .float(forKey:) method in an optional returning nil if the key doesn't exist.
func floatOptional(forKey: String) -> Float? {
return self.object(forKey: forKey) as? Float
}
/// Convenience method to wrap the built-in .bool(forKey:) method in an optional returning nil if the key doesn't exist.
func boolOptional(forKey: String) -> Bool? {
return self.object(forKey: forKey) as? Bool
}
}
They are now more consistent alongside the other built-in get methods (string, data, etc.). Just use the get methods in place of the old ones.
let AppDefaults = UserDefaults.standard
// assuming the key "Test" does not exist...
// old:
print(AppDefaults.integer(forKey: "Test")) // == 0
// new:
print(AppDefaults.integerOptional(forKey: "Test")) // == nil

Extend UserDefaults once to don't copy-paste this solution:
extension UserDefaults {
func hasValue(forKey key: String) -> Bool {
return nil != object(forKey: key)
}
}
// Example
UserDefaults.standard.hasValue(forKey: "username")

"objectForKey will return nil if it doesn't exist." It will also return nil if it does exist and it is either an integer or a boolean with a value of zero (i.e. FALSE or NO for the boolean).
I've tested this in the simulator for both 5.1 and 6.1. This means that you cannot really test for either integers or booleans having been set by asking for "the object". You can get away with this for integers if you don't mind treating "not set" as if it were "set to zero".
The people who already tested this appear to have been fooled by the false negative aspect, i.e. testing this by seeing if objectForKey returns nil when you know the key hasn't been set but failing to notice that it also returns nil if the key has been set but has been set to NO.
For my own problem, that sent me here, I just ended up changing the semantics of my boolean so that my desired default was in congruence with the value being set to NO. If that's not an option, you'll need to store as something other than a boolean and make sure that you can tell the difference between YES, NO, and "not set."

I just went through this, and all of your answers helped me toward a good solution, for me. I resisted going the route suggested by, just because I found it hard to read and comprehend.
Here's what I did. I had a BOOL being carried around in a variable called "_talkative".
When I set my default (NSUserDefaults) object, I set it as an object, as I could then test to see if it was nil:
//converting BOOL to an object so we can check on nil
[defaults setObject:#(_talkative) forKey:#"talkative"];
Then when I went to see if it existed, I used:
if ([defaults objectForKey:#"talkative"]!=nil )
{
Then I used the object as a BOOL:
if ([defaults boolForKey:#"talkative"]) {
...
This seems to work in my case. It just made more visual sense to me.

Try this little crumpet:
-(void)saveUserSettings{
NSNumber* value;
value = [NSNumber numberWithFloat:self.sensativity];
[[NSUserDefaults standardUserDefaults] setObject:value forKey:#"sensativity"];
}
-(void)loadUserSettings{
NSNumber* value;
value = [[NSUserDefaults standardUserDefaults] objectForKey:#"sensativity"];
if(value == nil){
self.sensativity = 4.0;
}else{
self.sensativity = [value floatValue];
}
}
Treat everything as an object. Seems to work for me.

Swift version to get Bool?
NSUserDefaults.standardUserDefaults().objectForKey(DefaultsIsGiver) as? Bool

In Swift3, I have used in this way
var hasAddedGeofencesAtleastOnce: Bool {
get {
return UserDefaults.standard.object(forKey: "hasAddedGeofencesAtleastOnce") != nil
}
}
The answer is great if you are to use that multiple times.
I hope it helps :)

Swift 3.0
if NSUserDefaults.standardUserDefaults().dictionaryRepresentation().contains({ $0.0 == "Your_Comparison_Key" }){
result = NSUserDefaults.standardUserDefaults().objectForKey(self.ticketDetail.ticket_id) as! String
}

Related

Optional<String> to Int64 is nil, hardcoded it works

I have a function:
func setId(id: Int64?) {
self._id = id
}
This function is called here:
let defaults = UserDefaults.standard
let id = defaults.object(forKey: "userId") as? String
if (id != nil) {
setId(id: Int64(id!))
}
In the function setId() the value of id always seems to be nil. When debugging and stepping one call up in the stack, to the caller (2nd piece of code) the value of id is
po id:
Optional<String>
- some : "Optional(350350002)"
When I check it as Int64:
po Int64(id!):
nil
When I check it with a hardcoded value:
po Int64(Optional("350350002")!):
Optional<Int64>
- some : 350350002
Also po Int64((id as NSString)): nil
What is going on here? How does the hardcoded value differ from the actual variable?
Thanks.
The problem was that the following was saved to UserDefaults: String(describing: self._userId), of course saving the actual word 'Optional(...' with it, as String.
I thought the debugger was showing me an Optional with value 350350002 instead of an actual string: "Optional(350350002)" + the saving to UserDefaults code was not written by me, why I didn't notice this.

Checking for null value (not nil or NSnull) in swift always return nil? [duplicate]

This question already has answers here:
How is optional binding used in swift?
(9 answers)
Closed 6 years ago.
I am working on a project which uses both swift an objective c. The team member before me have written this code in objective C ,which I am not familiar with. There is problem that most of the part involving storing and retrieving value from Sqlite is in obj C. This has been done in a common class to avoid Code redemption. However if i use swift to retrieve value through that obj C file a problem occur. If there is no value in that specified row it return "null".
Update: Checked for optional binding as said by Antony Raphel
Even if i check for nil directly before converting to 'as? String' the same error persist. I came to know that there is no equivalent of "null" in swift. Is there any hack to the value is empty (null) in swift?
Just replace your
var prevNotifCount = self.cmmn.getPreviousNotificationCount() as? String
and use
guard let prevNotifCount = self.cmmn.getPreviousNotificationCount() else{
print("No previous notification value")
return
}
no need to check for nil, if it will fail , else block will be executed
if let prevNotifCount = self.cmmn.getPreviousNotificationCount() as? String
{
self.cmmn.saveInDatabase("19", phoneNumber: "0", otp: "0")
print(self.cmmn.getPreviousNotificationCount())
}
else
{
print("No previous notification value")
}
This is standard Swift approach called optional binding. You safely unwrap an optional and if it is not nil assign it to a local variable
Try by adding if let to check nil condition like this:-
if let NotifCount = self.cmmn,getPreviousNotificationCount() as? String
{
prevNotifCount = NotifCount
}
Please try this, Hope it helps!
Use if let statement.
if let preNotifCount = self.cmmn.getPreviousNotofication {
//business logic
}
Now business logic would only be executed if preNotifCount is not nil.

how to don't get a nil value from NSUserDefaults in ViewDidLoad

I have a case where when my viewControler starts in viewDidLoad I have to load some data using NSUserDefaults.standardUserDefaults() which doesn't exist in this monent. This data are saved when I tap send Button in the same viewController and I need this data when I open this viewController again. Now it looks like that:
var orderHistory = [String:String]()
vievDidLoad(){
let userDefault = NSUserDefaults.standardUserDefaults()
let orderHistory = userDefault.objectForKey("orderHistory")
if orderHistory == nil {
self.orderHistory = orderHistory["name":"", "surname":""] as! [String:String]
} else {
self.orderHistory = orderHistory as! [String:String]
{
}// end viewDidLoad
In this moment I recieve an imformation, I have a problem with memory. How should I avoid this situation?
As Leo Dabus said you should try using the ?? nil coalescing operator.
ObjectForKey does not provide a default value because it doesnt know what kind of object it is until you set it the first time. This results in a nil crash if you try to access it value without having it set once.
Compare this to say "boolForKey" where you dont have to do this, because it knows you are dealing with boolean values and therefore defaults to false automatically.
You also dont have to create 2 orderHistory dictionaries, it just makes your code more confusing.
Try this instead
var orderHistory = [String:String]()
vievDidLoad(){
let userDefault = NSUserDefaults.standardUserDefaults()
orderHistory = userDefault.objectForKey("orderHistory") as? [String: String] ?? orderHistory
//than just use the 1 dictionary without the if statements or creating another one.
}// end viewDidLoad
You check if saved data exists (as? [String: String]) and update the dictionary accordingly. If no saved data exists it will use the default values in orderHistory (?? orderHistory), which in your case is an empty dictionary.
This way you dont have to do a nil check, its all done in that one line.
Also try putting your keys into structs or global files so that you avoid typos. I see people not doing this all the time and its really bad practice.
So for example, above your class create a struct
struct Key {
static let orderHistory = "OrderHistory"
}
and use it like so
...objectForKey(Key.orderHistory)
This code makes no sense:
if orderHistory == nil
{
self.orderHistory = orderHistory["name":"", "surname":""] as! [String:String]
}
The if statement guarantees that orderHistory is nil, thereby guaranteeing that the attempt to fetch keys from orderHistory will crash. Actually, that doesn't look like valid Swift. I would expect that line to throw a compiler error.
Are you trying to create a new dictionary?
If so, your code should read like this:
if orderHistory == nil
{
self.orderHistory = ["name":"", "surname":""]
}

Insert a potentially null value into the sqlite database in iOS

I have a class called Content, whose URL property is nullable (URL: String?).
I'd like to store this URL property in my sqlite database using FMDB, but Xcode complains I need to unwrap the optional with !
but the problem is when I do content.URL! it crashes because it's nil.
success = db.executeUpdate("INSERT INTO CONTENT(ID, Icon, Title, Description, URL, IsActive) VALUES(?,?,?,?,?,?,?,?,?)", withArgumentsInArray: [content.ID, content.icon, content.title, content.description, content.URL!, content.isActive])
How can I successfully insert URL both when it has and does not have a value?
Thanks!
One approach that I use for cases like this is to create a class extension.
For example:
class func databaseSafeObject(object: AnyObject?) -> AnyObject {
if let safeObject: AnyObject = object{
return safeObject;
}
return NSNull();
}
Then you can just use:
NSObject.databaseSafeObject(content.URL);
to get something that can be directly inserted in the db.
So this ended up working for me, although it seems kinda irking that this is how it has to be:
(content.URL == nil ? NSNull() : content.URL!)
There exists Swift wrappers for SQLite that may be a better fit that fmdb which can run in Swift but does not use Swift features such as optionals (that you miss here), type safety, and error handling. See for example my GRDB.swift http://github.com/groue/GRDB.swift which was heavily influenced by ccgus/fmdb.
The AnyObject type didn't work for me when working with variables of type Int and Double, so I created a similar function to handle optional Swift variables.
private func getOptionalOrNull(_ possibleValue:Any?)->Any {
if let theValue = possibleValue {
return theValue
} else {
return NSNull()
}
}

Swift: Testing optionals for nil

I'm using Xcode 6 Beta 4. I have this weird situation where I cannot figure out how to appropriately test for optionals.
If I have an optional xyz, is the correct way to test:
if (xyz) // Do something
or
if (xyz != nil) // Do something
The documents say to do it the first way, but I've found that sometimes, the second way is required, and doesn't generate a compiler error, but other times, the second way generates a compiler error.
My specific example is using the GData XML parser bridged to swift:
let xml = GDataXMLDocument(
XMLString: responseBody,
options: 0,
error: &xmlError);
if (xmlError != nil)
Here, if I just did:
if xmlError
it would always return true. However, if I do:
if (xmlError != nil)
then it works (as how it works in Objective-C).
Is there something with the GData XML and the way it treats optionals that I am missing?
In Xcode Beta 5, they no longer let you do:
var xyz : NSString?
if xyz {
// Do something using `xyz`.
}
This produces an error:
does not conform to protocol 'BooleanType.Protocol'
You have to use one of these forms:
if xyz != nil {
// Do something using `xyz`.
}
if let xy = xyz {
// Do something using `xy`.
}
To add to the other answers, instead of assigning to a differently named variable inside of an if condition:
var a: Int? = 5
if let b = a {
// do something
}
you can reuse the same variable name like this:
var a: Int? = 5
if let a = a {
// do something
}
This might help you avoid running out of creative variable names...
This takes advantage of variable shadowing that is supported in Swift.
Swift 3.0, 4.0
There are mainly two ways of checking optional for nil. Here are examples with comparison between them
1. if let
if let is the most basic way to check optional for nil. Other conditions can be appended to this nil check, separated by comma. The variable must not be nil to move for the next condition. If only nil check is required, remove extra conditions in the following code.
Other than that, if x is not nil, the if closure will be executed and x_val will be available inside. Otherwise the else closure is triggered.
if let x_val = x, x_val > 5 {
//x_val available on this scope
} else {
}
2. guard let
guard let can do similar things. It's main purpose is to make it logically more reasonable. It's like saying Make sure the variable is not nil, otherwise stop the function. guard let can also do extra condition checking as if let.
The differences are that the unwrapped value will be available on same scope as guard let, as shown in the comment below. This also leads to the point that in else closure, the program has to exit the current scope, by return, break, etc.
guard let x_val = x, x_val > 5 else {
return
}
//x_val available on this scope
One of the most direct ways to use optionals is the following:
Assuming xyz is of optional type, like Int? for example.
if let possXYZ = xyz {
// do something with possXYZ (the unwrapped value of xyz)
} else {
// do something now that we know xyz is .None
}
This way you can both test if xyz contains a value and if so, immediately work with that value.
With regards to your compiler error, the type UInt8 is not optional (note no '?') and therefore cannot be converted to nil. Make sure the variable you're working with is an optional before you treat it like one.
From swift programming guide
If Statements and Forced Unwrapping
You can use an if statement to find out whether an optional contains a
value. If an optional does have a value, it evaluates to true; if it
has no value at all, it evaluates to false.
So the best way to do this is
// swift > 3
if xyz != nil {}
and if you are using the xyz in if statement.Than you can unwrap xyz in if statement in constant variable .So you do not need to unwrap every place in if statement where xyz is used.
if let yourConstant = xyz {
//use youtConstant you do not need to unwrap `xyz`
}
This convention is suggested by apple and it will be followed by devlopers.
Although you must still either explicitly compare an optional with nil or use optional binding to additionally extract its value (i.e. optionals are not implicitly converted into Boolean values), it's worth noting that Swift 2 has added the guard statement to help avoid the pyramid of doom when working with multiple optional values.
In other words, your options now include explicitly checking for nil:
if xyz != nil {
// Do something with xyz
}
Optional binding:
if let xyz = xyz {
// Do something with xyz
// (Note that we can reuse the same variable name)
}
And guard statements:
guard let xyz = xyz else {
// Handle failure and then exit this code block
// e.g. by calling return, break, continue, or throw
return
}
// Do something with xyz, which is now guaranteed to be non-nil
Note how ordinary optional binding can lead to greater indentation when there is more than one optional value:
if let abc = abc {
if let xyz = xyz {
// Do something with abc and xyz
}
}
You can avoid this nesting with guard statements:
guard let abc = abc else {
// Handle failure and then exit this code block
return
}
guard let xyz = xyz else {
// Handle failure and then exit this code block
return
}
// Do something with abc and xyz
Swift 5 Protocol Extension
Here is an approach using protocol extension so that you can easily inline an optional nil check:
import Foundation
public extension Optional {
var isNil: Bool {
guard case Optional.none = self else {
return false
}
return true
}
var isSome: Bool {
return !self.isNil
}
}
Usage
var myValue: String?
if myValue.isNil {
// do something
}
if myValue.isSome {
// do something
}
One option that hasn't specifically been covered is using Swift's ignored value syntax:
if let _ = xyz {
// something that should only happen if xyz is not nil
}
I like this since checking for nil feels out of place in a modern language like Swift. I think the reason it feels out of place is that nil is basically a sentinel value. We've done away with sentinels pretty much everywhere else in modern programming so nil feels like it should go too.
Instead of if, ternary operator might come handy when you want to get a value based on whether something is nil:
func f(x: String?) -> String {
return x == nil ? "empty" : "non-empty"
}
Another approach besides using if or guard statements to do the optional binding is to extend Optional with:
extension Optional {
func ifValue(_ valueHandler: (Wrapped) -> Void) {
switch self {
case .some(let wrapped): valueHandler(wrapped)
default: break
}
}
}
ifValue receives a closure and calls it with the value as an argument when the optional is not nil. It is used this way:
var helloString: String? = "Hello, World!"
helloString.ifValue {
print($0) // prints "Hello, World!"
}
helloString = nil
helloString.ifValue {
print($0) // This code never runs
}
You should probably use an if or guard however as those are the most conventional (thus familiar) approaches used by Swift programmers.
Optional
Also you can use Nil-Coalescing Operator
The nil-coalescing operator (a ?? b) unwraps an optional a if it contains a value, or returns a default value b if a is nil. The expression a is always of an optional type. The expression b must match the type that is stored inside a.
let value = optionalValue ?? defaultValue
If optionalValue is nil, it automatically assigns value to defaultValue
Now you can do in swift the following thing which allows you to regain a little bit of the objective-c if nil else
if textfieldDate.text?.isEmpty ?? true {
}
var xyz : NSDictionary?
// case 1:
xyz = ["1":"one"]
// case 2: (empty dictionary)
xyz = NSDictionary()
// case 3: do nothing
if xyz { NSLog("xyz is not nil.") }
else { NSLog("xyz is nil.") }
This test worked as expected in all cases.
BTW, you do not need the brackets ().
If you have conditional and would like to unwrap and compare, how about taking advantage of the short-circuit evaluation of compound boolean expression as in
if xyz != nil && xyz! == "some non-nil value" {
}
Granted, this is not as readable as some of the other suggested posts, but gets the job done and somewhat succinct than the other suggested solutions.
If someone is also try to find to work with dictionaries and try to work with Optional(nil).
let example : [Int:Double?] = [2: 0.5]
let test = example[0]
You will end up with the type Double??.
To continue on your code, just use coalescing to get around it.
let example : [Int:Double?] = [2: 0.5]
let test = example[0] ?? nil
Now you just have Double?
This is totally logical, but I searched the wrong thing, maybe it helps someone else.
Since Swift 5.7:
if let xyz {
// Do something using `xyz` (`xyz` is not optional here)
} else {
// `xyz` was nil
}

Resources