NSUSerDefault not updating my global variable for swift - ios

I have a signin/out function for my app. However, I relised that when I logout, something is weird with my global user variable
I have a global variable under my constants.swift
var CURRENT_USER_UID = NSUserDefaults.standardUserDefaults().valueForKey("currentUserUid") as? String
However, when I logout from the current user and log a new one in, at the end of the sign in function, I have the following print statement
print("Sign in successfully. user.uid = \(user?.uid)")
print("CURRENT_USER_UID = \(CURRENT_USER_UID))")
print("NSUserDefaults.standardUserDefaults().valueForKey(KEY_USER_UID) \(NSUserDefaults.standardUserDefaults().valueForKey(KEY_USER_UID))")
These prints out
Sign in successfully. user.uid = Optional("3P9uavuLtDQZV8ColwTseBIrY4w1")
CURRENT_USER_UID = Optional("hakV4smGyveurPhFN7g9rad4xsP2"))
NSUserDefaults.standardUserDefaults().valueForKey("currentUserUid") Optional(3P9uavuLtDQZV8ColwTseBIrY4w1)
"hakV4smGyveurPhFN7g9rad4xsP2" was the previous user's userUID.
"3P9uavuLtDQZV8ColwTseBIrY4w1" is the current user's UID which I only just logged in. If working correctly, I would expect all three to be "3P9uavuLtDQZV8ColwTseBIrY4w1"
Does anyone know why my CURRENT_USER_UID does not return the same thing as NSUserDefaults.standardUserDefaults().valueForKey("currentUserUid") ??

The CURRENT_USER_ID won't reflect the changes happened to the NSUserDefaults. The initially assigned value won't change, the value won't be calculated again.
For fixing the issue, Either you should assign the changed user id to CURRENT_USER_ID (When that changes), or you can write a global function which returns the user id (I prefer this approach over changing the global variable in your code):
func getCurrentUserId() -> String?
{
return NSUserDefaults.standardUserDefaults().valueForKey("currentUserUid") as? String
}

Your global variable is initialised when the app is launched, so it is given the value that NSUserDefaults.standardUserDefaults().valueForKey("currentUserUid") returns at that point. When user defaults is updated the global isn't magically re-initialised. You need to update it yourself.
The simple (but wrong) solution is to assign the uid value to CURRENT_USER_ID when you complete the login and save it to NSUserDefaults.
The right solution is not to use global variables. You should encapsulate your user state in an appropriate class and use this to login/logout/obtain the current user.

Related

Realm doesn't always return an object for its primary key from the cloud, although it does exist there

I am quite new to Realm and now I'm facing the issue, mentioned in the title. Basically, one of my Realms is a User class (this is different from Users that is created when new users log in) in Realm Cloud. The class has a userId property, which is set as its primary key. Now, I try to fetch users for a particular primary key with the following line:
let user = realm?.object(ofType: User.self, forPrimaryKey: "f677ca48b17bc7b90a886dd5d50148c1")
I do see that there is a user in the cloud with this primary key, but he is not always returned. I mean, say, if I run the same code 10 times, I will get the user only 4 times out of 10 (roughly). The above code is run from a method, which is always called when the controller is on screen. Also, there is no condition checking in the method for that part of code, so it is always called when the method is called.
I've also checked the realm property and it is not nil, so I don't quite understand why this happens.
The same happens when I try to get the current user by the line:
currentUser = realm?.object(ofType: User.self, forPrimaryKey: SyncUser.current?.identity)
The identity value is not nil (I print it before calling the code).
UPDATED
Here is how the User looks like:
class User: Object {
#objc dynamic var userId: String = ""
#objc dynamic var name: String = ""
override static func primaryKey() -> String? {
return "userId"
}
}
Maybe, I am missing something or that is a problem/bug on the cloud side, I don't know. If you've encountered such an issue and/or know what could cause it, I would greatly appreciate your help. Thanks in advance.
This is caused because of the race condition that could pops up when you try to access a reference that is not on the main thread & being worked on by other thread .
therefore it will return nil each most of the time when you try to call it from the main thread because Realm had no chance to write or read from yet as you are accessing it directly .
What to do:
Make sure that your properties in the User object are marked with dynamic .
properties of your Object subclasses need to be declared with the dynamic modifier to allow Realm to override the getter and setter of the property.
Without this the Swift compiler will access the object's instance
variable directly, which doesn't provide any opportunity for Realm to
read or write data from the Realm file.
Also please read this answer .

How to make app (with username/password login) know that a user is logged in?

I'm trying to make a login screen and made a database with Firebase by Google. The way I tried to make accounts in database is just by making a new child which is "Users" and then the next child would be a user (which is uniquely defined with a unique username). Then, user has other attributes and an attribute called loggedIn which is set to String 'false' but when on loginScreen login goes successfully set to String 'true'. How can I know after login and when that LoginViewController goes away which account has logged in exactly on that phone (simulator at the time) because there can be more users at one time with the attribute value 'loggedIn' set to 'true' and because of that can't go back to database to check that. I'm really new at this and don't know if this whole approach is okay by making in real-time database something like that and actually checking that by the attribute. Maybe I have to use some kind of a local database or something similar?
Swift 4, iOS development, Xcode 9
If you just want to persist locally the login status you may use UserDefaults. Just have a key called currentUser and set the value as his unique Id.
Once a user logs out just clear that key.
You can also use that to determine whether to launch the login screen when the app opens.
But it may be better to use something like AWS Cognito or similar services to handle user management.
Are you building an iOS app only or a cross-platform project?
I'm not sure this is the perfect answer for you. However, some of my apps use CloudKit and the login is authenticated and only then opens up areas of the app on the user's device. I'm sure if you wanted to check the status of successful logins, the authentication on the cloud could update as confirmation and you could subsequently access the cloud database using the CloudKit Dashboard.
It's quite intuitive and works seamlessly with Xcode.
I have a class called LoginManager, defined as an NSObject. Included in there is an optional String variable called accessToken with setter and getter functions for saving and retrieving my access token, including an option for clearing the token which would be used upon logout. There is also a function to check the status of the key.
var accessToken: String? {
get {
return //key from storage
}
set {
if newValue == nil {
//remove key from storage
} else {
//save key to storage
}
}
}
func isUserLogedIn() -> Bool {
return self.accessToken != nil
}
In my own case, I have an application manager object that changes the root view controller based on this info.
LoginManager.singleton.isUserLogedIn() ? loadMainView() : loadLoginView()
So the resulting view controller is never loaded unless the user is logged in. Is that useful to you?

Checking if a value has been in the user class has been created Parse Server Swift

In a project I am working on the user goes through a sing up process. In the process a user is created using their entered username and password, if that succeeds then a fullName, and accountVerified is added to the current user with its corresponding value.
The problem is if the user quits the application before the save is complete then the image, fullName, and accountVerified is not added to the current user.
This causes a problem later when the application is relaunched. When the app is relaunched it checks for a user, if there is a user then it checks of if their accountVerified value is true or false. Since the user quit before the save completed there is no accountVerified to check as acountVerified and its value were not added to the user.
To account for this I will save the users fullName, and accountVerified to NSUserDefaults and when the app is launched check if those are created and given a value. If not then use the NSUserDefault values will be used to set the values and save to the current user.
The Question:
I use Parse Server with Heroku and mLab mongoDB. I am wondering what the best way to check the current users object to see if accountVerified, and fullName have been created. Any help is much appreciated!
Query user with 'where' accuntVerified = True; set1 Query user with 'where' accountVerified = False; set2 If the user does not exist in set1 or set2, accountverified is not set.

Getting data only when logged in

In my app i have some functions: login, getNews, getWeather. The last two functions can be executed only if the user is logged in. I don't want to login everytime also if the user is already logged in, so if the two lasts functions returns a not logged in error i want to login and then executed the queue of functions that required the login.
That's only an example, the real code is much longer and the functions that depends on login are more than two. I want to know the best solution to manage that situation.
For example, you can store in UserDefaults state of logged in user or not. And base on this call any functions you want.
When the user has logged in you can store his credential in the keychain.
Look here for a brief tutorial:
Tutorial One
Tutorial Two.
Then you can check every time if you have his credentials stored and log in automatically, and then perform the functions getNews and getWeather.
To store data between your app launches, you can try any of the following two methods :
1) The very simple solution would be to store the user data in your UserDefaults in the form of user dictionary.
for ex. NSUserDefaults.standardUserDefaults().setObject(userDict, forKey: #"User Dictionary"). This can be painful if your userDict is large or difficult to make.
2) The other solution can be, use a archiver to store the user data. You can make your custom archiver from this link.
So when user success login use NSUserDefaults logged value = 1
Example;
Add prefs with that codes.
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
Success login after set logged object inside 1.
let logged = 1
prefs.setObject(logged, forKey: "logged")
And check user if logged or not logged show login
You can add under below check logged codes everywhere inside project
if prefs.valueForKey("logged") as! Int == 1 {
}else{
// go login
}
For logout you can use ;
let logged = 0
prefs.setObject(logged, forKey: "logged")
Thanks

Removing a key from NSUserDefaults not working

I have a logout function in my app. Seems to be a weird problem where it doesn't save the NSUserDefaults. Here I simply want to remove the key. However after logging out if I then open the app again it will find that this key is still in the NSUserDefaults.
func didLogout() {
// Clear user data
let settings = NSUserDefaults.standardUserDefaults()
settings.removeObjectForKey("userData")
settings.synchronize()
unregisterForRemoteNotifications()
openLoginScreen()
}
Any ideas what I could be doing wrong here?
try this
DispatchQueue.main.async {
UserDefaults.standard.removeObject(forKey: YOUR_KEY_HERE)
}
helped for me
removeObjectForKey(_:)
Removing a default has no effect on the value returned by the
objectForKey: method if the same key exists in a domain that precedes
the standard application domain in the search list.
Just use another key instead of userData. It might exists in another domain.
The code above is correct. The key will be still there, but it will return only nil value. So, when user logout you can set
NSUserDefaults.standardUserDefaults().removeObjectForKey("userData")
and when new user login you can set new value by checking
if NSUserDefaults.standardUserDefaults().objectForKey("userData") == nil
One of our (XCTest) unit tests was failing every other run. It turned out that -removeObjectForKey: was — inexplicably — only working every other run. I verified this with defaults.dictionaryRepresentation before and after -removeObjectForKey:. I thought perhaps the keys were being added to two domains and the first remove wasn't getting them both (the docs say this can happen) so I cleverly added a second remove, but that also had no effect. My solution was to set the key to the uninitialized value instead.
Any ideas?
There is no issue in your above code you might have set data in app delegate or when you login your app, or you have mistyped key value.
If you want to clear all data. This will Work
let appDomain = NSBundle.mainBundle().bundleIdentifier!
NSUserDefaults.standardUserDefaults().removePersistentDomainForName(appDomain)
I did the following to delete the userdefault of the app on user loggout
private static let userDefaults = NSUserDefaults.standardUserDefaults()
private static let userTokenKey = "userTokenKey"
userDefaults.removeObjectForKey(userTokenKey)
userDefaults.synchronize()

Resources