Check if UserDefault exists - Swift - ios

I'm trying to check if the a user default exists, seen below:
func userAlreadyExist() -> Bool {
var userDefaults : NSUserDefaults = NSUserDefaults.standardUserDefaults()
if userDefaults.objectForKey(kUSERID) {
return true
}
return false
}
However, no mater what it will always return true even when the object doesn't exist yet? Is this the right way for checking existence ?

Astun has a great answer. See below for the Swift 3 version.
func isKeyPresentInUserDefaults(key: String) -> Bool {
return UserDefaults.standard.object(forKey: key) != nil
}

I copy/pasted your code but Xcode 6.1.1 was throwing some errors my way, it ended up looking like this and it works like a charm. Thanks!
func userAlreadyExist(kUsernameKey: String) -> Bool {
return NSUserDefaults.standardUserDefaults().objectForKey(kUsernameKey) != nil
}
Swift 5:
if UserDefaults.standard.object(forKey: "keyName") != nil {
//Key exists
}

Yes this is right way to check the optional have nil or any value objectForKey method returns AnyObject? which is Implicit optional.
So if userDefaults.objectForKey(kUSERID) have any value than it evaluates to true. if userDefaults.objectForKey(kUSERID) has nil value than it evaluates to false.
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.
Now there is a bug in simulators than after setting key in userDefaults they always remain set no matter you delete your app.You need to reset simulator.
Reset your Simulator check this method before setting key in userDefaults or remove key userDefaults.removeObjectForKey(kUSERID) from userDefaults
and it will return NO.On devices it is resolved in iOS8 beta4.

This is essentially the same as suggested in other answers but in a more convenient way (Swift 3+):
extension UserDefaults {
static func contains(_ key: String) -> Bool {
return UserDefaults.standard.object(forKey: key) != nil
}
}
usage: if UserDefaults.contains(kUSERID) { ... }

Simple Code to check whether value stored in UserDefault.
let userdefaults = UserDefaults.standard
if let savedValue = userdefaults.string(forKey: "key"){
print("Here you will get saved value")
} else {
print("No value in Userdefault,Either you can save value here or perform other operation")
userdefaults.set("Here you can save value", forKey: "key")
}

Many of the solutions here are valid. Still, I think they solve the wrong problem.
Usually, code like this is used to check if a value is set so another default value can be used:
if isKeyPresentInUserDefaults(key: "username") {
return UserDefaults.standard.string(forKey: "username")
} else {
return "No username was set"
}
You shouldn't care if a key is set or not. There is a far more elegant approach for having default values in UserDefaults:
UserDefault.standard.register(defaults: ["username": "No username was set"])
If you run this code at app launch, subsequent calls to UserDefaults.standard.string(forKey: "username") will return the default value of "No username was set" if no value was set for the key yet.

for swift 3.2
func userAlreadyExist(kUsernameKey: String) -> Bool {
return UserDefaults.standard.object(forKey: kUsernameKey) != nil
}

public class PreferencesUtils {
private init() {
}
public static func setBoolData(boolValue: Bool, dataName: String) {
UserDefaults.standard.set(boolValue, forKey: dataName)
}
public static func getBoolData(dataName: String)-> Bool{
let defaults = UserDefaults.standard
if(defaults.value(forKey: dataName) != nil) {
return defaults.value(forKey: dataName)! as! Bool
} else {
return false
}
}
public static func saveStringData(data: String, dataName: String){
let preferences = UserDefaults.standard
preferences.set(data, forKey: dataName)
let didSave = preferences.synchronize()
if !didSave {
debugPrint("Not saved yet")
}
}
public static func getSavedStringData(dataName: String)-> String{
let defaults = UserDefaults.standard
if(defaults.value(forKey: dataName) != nil){
return defaults.value(forKey: dataName) as! String
} else {
return ""
}
}
public static func saveIntData(data : Int, dataName: String){
let preferences = UserDefaults.standard
preferences.set(data, forKey: dataName)
let didSave = preferences.synchronize()
if !didSave {
debugPrint("Not saved yet")
}
}
public static func getSavedIntData(dataName: String) -> Int {
let defaults = UserDefaults.standard
if(defaults.value(forKey: dataName) != nil){
return defaults.value(forKey: dataName) as! Int
}else{
return 0
}
}
}
Or you can try this library: Link

func keyExists(key: String) -> Bool {
guard let _ = UserDefaults.standard.object(forKey: key) {
return false;
}
return true;
}

override func viewDidLoad()
{
super.viewDidLoad()
if UserDefaults.standard.string(forKey: "CHARRY") == "CHARRY"{
lb.text = "CHARRY"
im.image = UIImage(named: "CHARRY")
}
}
#IBAction func PressedCar(_ sender: UIButton){
lb.text = "CHARRY"
im.image = UIImage(named: "CHARRY")
UserDefaults.standard.set("CAR", forKey: "CHARRY")
}

Related

How to save data from two UITextViews to UserDefaults

For each UITextView using UserDefaults, I've made a function to save and a function to display.
Whatever text is added needs to be displayed at the time of adding, saved and then displayed again when opening the app again.
If I install the app with ONLY the function to save, quit the app and add the function to display then reinstall without deleting the installed app everything works perfectly.
If I install the app with both functions added it doesn't work.
There has to be a simple solution for this, I'm obviously doing something wrong.
The data from one textView is used to calculate results and then to display them on the other textView.
All data is added with other functions, none by the user.
numberHistoryView.isEditable = false
numberHistoryView.isSelectable = false
func saveHistoryTextView()
{
let defaults = UserDefaults.standard
let numberHistory = numberHistoryView.text
defaults.set(numberHistory, forKey: "combos")
}
func displaySavedHistory()
{
let defaults = UserDefaults.standard
let savedCombos = defaults.object(forKey: "combos") as? String ?? ""
numberHistoryView.text = savedCombos
}
func saveFrequencyTextView()
{
let defaults = UserDefaults.standard
let numberFrequency = numberFrequencyCount.text
defaults.set(numberFrequency, forKey: "frequency")
}
func displaySavedFrequency()
{
let defaults = UserDefaults.standard
let savedFrequency = defaults.object(forKey: "frequency") as? String ?? ""
numberFrequencyCount.text = savedFrequency
}
override func viewWillDisappear(_ animated: Bool)
{
saveHistoryTextView()
saveFrequencyTextView()
}
override func viewWillAppear(_ animated: Bool)
{
displaySavedHistory()
displaySavedFrequency()
}
This depends on the order and timing in which you are calling save and display methods.
When you're installing a fresh app, there will be no data in saved in UserDefaults. So when you call displaySavedHistory() and displaySavedFrequency() methods in viewWillAppear(_:), nothing will be fetched because nothing is saved yet.
Now, when you save the data using saveHistoryTextView() and saveFrequencyTextView() methods in viewWillDisappear(_:) and then you kill and run the app again, the saved data will be fetched and displayed.
Also, since you're saving the data in UserDefaults, and UserDefaults are saved within the sandbox, so the data won't persist when you delete the app. You've to save the data in iCloud or keychain etc. if you want to persist the data even after app deletion.
Once I put my brain into a theta state with the right frequency I managed to figure it out.
Thanks to #Naresh and all other contributors for trying to help as you may have assisted me a little.
The solution basically just required a simple if statement.
Everything now works perfectly.
func saveHistoryTextView()
{
if numberHistoryView.text?.count != nil
{
let defaults = UserDefaults.standard
defaults.set(numberHistoryView.text!, forKey: "combos")
}
}
func displaySavedHistory()
{
let defaults = UserDefaults.standard
if let savedCombos = defaults.string(forKey: "combos")
{
numberHistoryView.text = savedCombos
}
}
func saveFrequencyTextView()
{
if numberFrequencyCount.text?.count != nil
{
let defaults = UserDefaults.standard
defaults.set(numberFrequencyCount.text!, forKey: "frequency")
}
}
func displaySavedFrequency()
{
let defaults = UserDefaults.standard
if let savedFrequency = defaults.string(forKey: "frequency")
{
numberFrequencyCount.text = savedFrequency
}
}
}
override func viewWillDisappear(_ animated: Bool)
{
saveHistoryTextView()
saveFrequencyTextView()
}
override func viewWillAppear(_ animated: Bool)
{
displaySavedHistory()
displaySavedFrequency()
}
You can do it with property observer as:
private let DATA_KEY = "Saved Data"
//After initialising the outlet we can set the data
#IBOutlet weak var textView: UITextView! {
didSet {
textView.text = self.data
}
}
private var data: String {
set {
//Save data in user defaults
UserDefaults.standard.set("The value you will assign", forKey: DATA_KEY)
}
get {
//get the data from user defaults.
return UserDefaults.standard.value(forKey: DATA_KEY) as? String ?? ""
}
}
//UITextViewDelegate: set the text data on end of UITextView editing
func textViewDidEndEditing(_ textView: UITextView) {
self.data = textView.text
}

Could not cast value of type '__NSCFData' (0x39490110) to 'NSString' (0x394990ac)

There is crash while the casting the jsonstring to object using objectMapper framework in the following part of the code but the way I have implemented is correct I guess.
Could not cast value of type '__NSCFData' (0x39490110) to 'NSString' (0x394990ac)
Here is the code:
static func saveUserRefererInfo(userRefInfo : UserRefererInfo?)
{
if userRefInfo != nil {
let jsonText = Mapper().toJSONString(userRefInfo!, prettyPrint: true)!
userDefaults.set(jsonText, forKey: SharedPreferenceHelper.USER_REFERER_INFO)
} else {
userDefaults.set(nil, forKey: SharedPreferenceHelper.USER_REFERER_INFO)
}
}
static func getUserRefererInfo() -> UserRefererInfo?
{
let userRefInfo = userDefaults.value(forKey: SharedPreferenceHelper.USER_REFERER_INFO)
if userRefInfo != nil {
return Mapper<UserRefererInfo>().map(JSONString: userRefInfo! as! String)
}
return nil
}
The crash is happening in this line of the code:
return Mapper<UserRefererInfo>().map(JSONString: userRefInfo! as! String)
The error is pretty clear: The type of userRefInfo is (NS)Data.
I don't know how userRefInfo is processed further but this is the usual way to get Data from UserDefaults.
if let userRefInfo = userDefaults.data(forKey: SharedPreferenceHelper.USER_REFERER_INFO)
return Mapper<UserRefererInfo>().map(JSONString: userRefInfo)
}
Never value(forKey: and never if foo != nil { ... foo! }
Consider that ObjectMapper became obsolete in favor of Codable in Swift 4+

Login credentials missing in swift UserDefaults

I have an application which uses a rest api for authentication. The problem I am facing now is that I save user's token in my UserDefaults and username too because those are the two main parameters needed to get user details. so if the application is closed by the user he should still be able to view the view his profile when he opens the application back but instead the profile returns empty details. this is the UserDefaults codes that I have
let defaults = UserDefaults.standard
var isLoggedIn : Bool {
get {
return defaults.bool(forKey: LOGGED_IN_KEY)
}
set {
defaults.set(newValue, forKey: LOGGED_IN_KEY)
}
}
//Auth Token
var authToken: String {
get {
return defaults.value(forKey: TOKEN_KEY) as? String ?? ""
}
set {
defaults.set(newValue, forKey: TOKEN_KEY)
}
}
var userUsername: String {
get {
return defaults.value(forKey: USERNAME_KEY) as? String ?? ""
}
set {
defaults.set(newValue, forKey: USERNAME_KEY)
}
}
I have no idea why it isnt retrieving the user data.
My second question is when I logout the user, all the users details are cleared as expected but the moment I try loging in with a different user, the new user's authToken and details gets printed in the console but the user profile returns the profile of the previous person. which is not supposed to be. my code is shown below
func logoutUser() -> Void {
pk = 0
username = ""
email = ""
firstName = ""
lastName = ""
AuthService.instance.isLoggedIn = false
AuthService.instance.authToken = ""
AuthService.instance.userUsername = ""
}
#IBAction func logoutPressed(_ sender: Any) {
UserDataService.instance.logoutUser()
dismiss(animated: true, completion: nil)
}
I would also like to add that when i run the api using postman i get a response that "detail": "Signature has expired." so i had to input the new token in the header so it displays the user details again
enum SettingKeys: String {
case authToken
//...
}
struct Settings {
static var authToken: String? {
get { return UserDefaults.standard.string(forKey: SettingKeys.authToken.rawValue) }
set(value) { UserDefaults.standard.set(value, forKey: SettingKeys.authToken.rawValue) }
}
static func deleteAll(exclude: [SettingKeys] = []) {
let saveKeys = exclude.map({ $0.rawValue })
for key in UserDefaults.standard.dictionaryRepresentation().keys {
if !saveKeys.contains(key) {
UserDefaults.standard.removeObject(forKey: key)
}
}
}
}
I recommend storing keys as Enum, because then u can use it like that:
//Read
if let token = Settings.authToken {
//do something
}
//Write
Settings.authToken = "123456"
//Delete settings
Settings.deleteAll()
//or choose what to leave
Settings.deleteAll(exclude: [.authToken])
And it's worth to mention that defaults.synchronize() is deprecated.

Is there any way of sharing getter and setter through properties in Swift?

I'm building a helper to enable typed access to NSUserDefaults properties. Something like this:
struct UserDefaults {
private static var standardUserDefaults: NSUserDefaults = {
return NSUserDefaults.standardUserDefaults()
}()
private static let propKey = "PROP"
static var prop: Bool {
get {
return standardUserDefaults.boolForKey(propKey)
}
set {
standardUserDefaults.setBool(newValue, forKey: propKey)
standardUserDefaults.synchronize()
}
}
}
This way I can have a nice syntax for reading and writing to NSUserDefaults:
UserDefaults.prop // read
UserDefaults.prop = false // write
The problem is that there's a lot of boilerplate code for this, I need 10 lines for each aditional property.
Is there any way of reducing the amount of lines needed for each new property? Reusing getter and setter? Any kind of run time generator?
You can try wrapping the actual value in a class that handles all the dirty work for you:
class WrappedUserDefault<T> {
let key : String
let defaultValue : T
var value : T {
get {
if let value = UserDefaults.standardUserDefaults.objectForKey(key) as? T {
return value
} else {
return defaultValue
}
}
set {
if let value = newValue as? AnyObject {
UserDefaults.standardUserDefaults.setValue(value, forKey: key)
} else {
UserDefaults.standardUserDefaults.removeObjectForKey(key)
}
UserDefaults.standardUserDefaults.synchronize()
}
}
init(key:String, defaultValue:T) {
self.key = key
self.defaultValue = defaultValue
}
}
struct UserDefaults {
static let standardUserDefaults = NSUserDefaults.standardUserDefaults()
static let ready = WrappedUserDefault<Bool>(key:"ready", defaultValue: true)
static let count = WrappedUserDefault<Int>(key: "count", defaultValue: 0)
}
Then with just a little bit more code you wind up with:
UserDefaults.count.value++
UserDefaults.ready.value = true
UserDefaults.ready.value
If the verbosity of ready.value bothers you, you can somewhat hide that, although then you're back to you're back to having a fair amount of copy/paste code:
struct UserDefaults {
static let standardUserDefaults = NSUserDefaults.standardUserDefaults()
private static let readyWrapper = WrappedUserDefault<Bool>(key:"ready", defaultValue: true)
static var ready : Bool {
get { return readyWrapper.value }
set { readyWrapper.value = newValue }
}
}
At least in this case though, the copy/paste code is fairly trivial, so unlikely to need to be altered in the future.
I like David's answer much better, but here's another option. Drops your 10 lines per variable down to 5 (mainly because of new line removal...)
struct UserDefaults {
private static var standardUserDefaults: NSUserDefaults = {
return NSUserDefaults.standardUserDefaults()
}()
//Repeate these 5 lines for all new variables,
//changing the as? to the proper variable type
//Adding in a default value for the return in
//case the as? cast fails for any reason
private static let propKey = "PROP"
static var prop: Bool {
get { return (getVar(propKey) as? Bool) ?? false }
set { setVar(newValue, key:propKey) }
}
//The generic set/get
private static func getVar(key : String) -> AnyObject?
{
return standardUserDefaults.objectForKey(key)
}
private static func setVar(newValue : AnyObject, key : String)
{
if(newValue is Bool)
{
standardUserDefaults.setBool((newValue as? Bool)!, forKey: key)
}
//... More cases here
else if(newValue == nil)
{
standardUserDefaults.removeObjectForKey(key)
}
else
{
standardUserDefaults.setObject(newValue, forKey: key)
}
standardUserDefaults.synchronize()
}
}

iOS Equivalent For Android Shared Preferences

I am porting an Android app to iOS, one thing I used was the Shared Preferences in Android to save each time a level was complete.
That way when the user gets back into the app, they can see they are up to level 3 or whatever.
Is there a similar mechanism in iOS? or do I have to manually write out to an application specific file?
If so, how do I write out to files only visible to my application?
Thanks.
Use NSUserDefaults: - note that this is for small bits of data, such as the current level like you mentioned. Don't abuse this and use it as a large database, because it is loaded into memory every time you open your app, whether you need something from it or not (other parts of your app will also use this).
Objective-C:
Reading:
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
NSString *currentLevelKey = #"currentlevel";
if ([preferences objectForKey:currentLevelKey] == nil)
{
// Doesn't exist.
}
else
{
// Get current level
const NSInteger currentLevel = [preferences integerForKey:currentLevelKey];
}
Writing:
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
NSString *currentLevelKey = #"currentlevel";
const NSInteger currentLevel = ...;
[preferences setInteger:currentLevel forKey:currentLevelKey];
// Save to disk
const BOOL didSave = [preferences synchronize];
if (!didSave)
{
// Couldn't save (I've never seen this happen in real world testing)
}
.
Swift:
Reading:
let preferences = NSUserDefaults.standardUserDefaults()
let currentLevelKey = "currentLevel"
if preferences.objectForKey(currentLevelKey) == nil {
// Doesn't exist
} else {
let currentLevel = preferences.integerForKey(currentLevelKey)
}
Writing:
let preferences = NSUserDefaults.standardUserDefaults()
let currentLevelKey = "currentLevel"
let currentLevel = ...
preferences.setInteger(currentLevel, forKey: currentLevelKey)
// Save to disk
let didSave = preferences.synchronize()
if !didSave {
// Couldn't save (I've never seen this happen in real world testing)
}
Here is an update for Swift 3
Reading
let preferences = UserDefaults.standard
let currentLevelKey = "currentLevel"
if preferences.object(forKey: currentLevelKey) == nil {
// Doesn't exist
} else {
let currentLevel = preferences.integer(forKey: currentLevelKey)
}
Writing
let preferences = UserDefaults.standard
let currentLevel = ...
let currentLevelKey = "currentLevel"
preferences.set(currentLevel, forKey: currentLevelKey)
Update
From UserDefaults documentation
synchronize() waits for any pending asynchronous updates to the defaults database and returns; this method is now unnecessary and shouldn't be used.
class Configuration {
static func value<T>(defaultValue: T, forKey key: String) -> T{
let preferences = UserDefaults.standard
return preferences.object(forKey: key) == nil ? defaultValue : preferences.object(forKey: key) as! T
}
static func value(value: Any, forKey key: String){
UserDefaults.standard.set(value, forKey: key)
}
}
Example
//set
Configuration.value(value: "my_value", forKey: "key_1")
//get
let myValue = Configuration.value(defaultValue: "default_value", forKey: "key_1")
As per the previous answer, you already know that UserDefaults is the equivalent to shared preferences in ios. You can create a common write function and for read create function based on data type. And call your required method from anywhere.
ViewController.swift
// write data
writeAnyData(key: "MY_KEY", value: "MyData")
// read string data
readStringData(key: "MY_KEY"), animated: true)
Utils.swift
// read and write user default
let userDefault = UserDefaults.standard
// write
func writeAnyData(key: String, value: Any){
userDefault.set(value, forKey: key)
userDefault.synchronize()
}
// read int values
func readIntData(key: String) -> Int{
if userDefault.object(forKey: key) == nil {
return 0
} else {
return userDefault.integer(forKey: key)
}
}
// read string values
func readStringData(key: String) -> String{
if userDefault.object(forKey: key) == nil {
return ""
} else {
return userDefault.string(forKey: key)!
}
}
// read bool value
func readBoolData(key: String) -> Bool{
if userDefault.object(forKey: key) == nil {
return false
} else {
return userDefault.bool(forKey: key)
}
}

Resources