Firebase wrapping user information with an "Optional()" wrapper - swift

My code to get a current user's email address is:
let currUserId = FIRAuth.auth()?.currentUser?.email
currUser = "Logged in as User: \(currUserId)"
The result is shown like this:
Logged in as User: Optional("email")
Is there any way to eliminate this wrapper. I seem to be having this same issue when Firebase pushes my UID to my database, as it likes to wrap it in an Optional("uid") wrapper.

Unwrap the optional:
if let user = currUserID{
print(user)
}

Related

Notify User of New Signin with Firebase - Swift/Xcode

I have looked all over and can't figure out how to notify a user when a new device signs into their account. I'm using Firebase Authentication. I'd like to setup some kind of notification system or email that tells the user someone has signed into their account from a different device so they can know for security reasons.
Ideas?
Also, how could you monitor information about what device (s) are signed into a specific account? For example, maybe the user is curious how many devices he has signed into his account, what type of device they are, what the name of those devices are, and where they are located (example: San Antonio, Texas).
Ideas?
yes, you can use keychain for this situation. First you should create an uniqueId. After you can save to the db uniqueId. This value will change if the user uses another device. I'm using third party framework for Keychain services. You can use framework. It's perfect.
https://github.com/kishikawakatsumi/KeychainAccess
final class DeviceManager {
static let shared = DeviceManager()
private init() { }
var uniqueDeviceId: String {
get {
let keychain = Keychain(service: KeyManager.keychainServiceName).accessibility(.always)
if let deviceId = keychain[KeyManager.keychainDeviceIdKey] {
return deviceId
}
let vendorId = UIDevice.current.identifierForVendor!.uuidString
keychain[KeyManager.keychainDeviceIdKey] = vendorId
return vendorId
}
}
}
Super simple; have a field in your user document that stores a device name along with its status.
You app will be observing this users document and when something changes, all of the users devices will be notified of that change.
Let me set this up; here's a basic Firestore structure
users
uid_0
userName: "Jay"
devices:
device_0: offline
device_1: offline
When the app starts, it will add an observer to this users document (using the uid as the documentId)
func observeUser() {
let usersCollection = self.db.collection("users")
usersCollection.document("uid_0").addSnapshotListener { (documentSnapshot, err) in
guard let document = documentSnapshot else {
print("Error fetching document: \(err!)")
return
}
let device = document.get("devices") as! [String: String]
print(device)
}
}
Now in the Firestore closure shown above, if a users device changes status, offline to online for example, it outputs all of the devices to console. You would take whatever action is needed when the device changes status.
Keep in mind that if a NEW device is added, that event will also fire so you could present a message in the UI "A new device was added!"
So then some testing code that toggles the device_0 status from offline to online. I have a button click that does self.status = !self.status and then calls the toggleStatus function
var status = false
func toggleStatus() {
var isOnline = ""
if self.status == false {
isOnline = "online"
} else {
isOnline = "offline"
}
let userCollection = self.db.collection("users")
let thisDevice = "device_0"
let devicesDict = [
"devices":
[thisDevice: isOnline] //sets device_0 to offline or online
]
let document = usersCollection.document("uid_0").setData(devicesDict, merge: true)
}
In a nutshell, when a user authenticates with a device for the first time, it would perhaps ask for a device name, or craft one from the devices mac address or something under the hood. That device name is stored in the users document/devices with it's online status.
The device name would be stored locally as well, in user defaults for example so it's automatically sent up to Firestore upon login.
The end result here is that if any users devices change status; offline to online or vice versa, or any device is added or removed all of the devices are notified of that event.

Why does the display name of my users keep reverting in Firebase with swift?

I encountered the following error in my swift project: https://github.com/firebase/firebase-ios-sdk/issues/4393. To overcome this issue I change the display name to the characters before the # in a users email like so:
var displayName = user.email!
if let atRange = displayName.range(of: "#") {
displayName.removeSubrange(atRange.lowerBound.. < displayName.endIndex)
}
if user.displayName!.count < 2{
let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
changeRequest?.displayName = displayName
changeRequest?.commitChanges {(err) in
if let err = err{
print(err)
}
}
}
This works when I first log in but if I log out and then back in again the display name reverts back to Optional(""). Why would it be doing this?
Edit
I created a workaround where I run the above code not only when a user is first created, but every time a user logs in, this seems excessive though and there must be a reason why the display name keeps getting overwritten.
This is not an error, this is intended behaviour from Apple's side. By default, you should be updating the Firebase user with the appropriate display name rather than relying on 3rd party information since these are only available on first login.
Simply checking if the name exists on initial login and is longer than 0 characters, not null, etc. then you can update the name with the following:
let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
changeRequest?.displayName = displayName
changeRequest?.commitChanges { (error) in
// ...
}
This is also documented within the git-issue you posted https://github.com/firebase/firebase-ios-sdk/issues/4393#issuecomment-612193703
Ultimately, you are logging into your app as a Firebase user, not an apple user. Apple is only Authenticating the process, so you must update the Firebase user where possible as Apple is highly inconsistent across all platforms.

Store new user information to Firebase with multiple view controllers

I have three different view controllers: (in this order) first+last name, birthday, email+password.
I have already connected my app to firebase and I know how to send the user information to firebase, but only for one of the view controllers. I want firebase to store all of the information from all three view controllers (name, birthday, and email/password) after clicking the "sign up" button on the last view controller (email+password). Please let me know how I can combine all of the information to one new user, rather than making them all new users.
It seems like you are making multiple API calls rather than one single API to sign up a new user, meaning only one call is necessary. There are a couple of different ways you could do this, but the main idea is that you need to get all the data to the very end of the onboarding sign up and then call the Firebase API.
I suggest you make a data object called NewUser and store the data as you progress through the sign-up process. It would look something like this:
class NewUser {
// MARK: - Variables
var userID:String
var name:String?
var birthday:String?
var email:String?
var password:String?
// MARK: - Init Variables
init(userID:String, name:String, birthday:String, email:String, password:String) {
self.userID = userID
self.name = name
self.birthday = birthday
self.email = email
self.password = password
}
}
Example to set name data:
NewUser.name = name
Then call NewUser.name to access the stored data.

How to manage acces control at login

I been looking for control what a kind of user can see in my app, this is a scholar project. I'm using Swift and Firebase Authentication. I have two kinds of users: Model and Client. In my app I have some views for the Model and other for the Client. What I want to do is that once they log in, in to the app show just the views for their kind of user. I don't know how to verify if the user that is trying to sign in is a Model or a Client.
#IBAction func signInTapped(_ sender: UIButton) {
if validateFields(){
Auth.auth().signIn(withEmail: emailTxt.text!, password: passTxt.text!, completion:{
(user, error) in
if let u = user {
//User is found
}else{
//Error
}
})
}
}
I know that the code need to be where is the comment "User is found" but I don't know if I need to modify something into the the Firebase Console
Create a Firebase Database or Firestore to your project.
Now when you authenticate a user you should also create a userobject in your databse. In this object you can create a field to store whether your user is a model or a client.
Now once the user has signed in, you can download this userobject from the database, check whether the user is a model or client, and send the user to their corresponding views.
You can use custom claims.
You set them using the Admin SDK
// Javascript
admin.auth().setCustomUserClaims(uid, {model: true}).then(() => {
// The new custom claims will propagate to the user's ID token the
// next time a new one is issued.
});
Then in client SDK just read the claim.
user.getIDTokenResult(completion: { (result, error) in
guard let model = result?.claims?["model"] as? NSNumber else {
// Something
}
if model.boolValue {
// Show something else
} else {
// Something else again
}
})
Shamelessly copied from Firebase Docs

Swift & Parse.com find by specific column not ID

Reference: http://blog.parse.com/2014/06/06/building-apps-with-parse-and-swift/
I'm trying to find a columns value: userPassword, based in the userName column. Using the above reference from Parse it shows that to get data from parse you should use:
var query = PFQuery(className: "GameScore")
query.getObjectInBackgroundWithId(gameScore.objectId) {
(scoreAgain: PFObject!, error: NSError!) -> Void in
if !error {
NSLog("%#", scoreAgain.objectForKey("playerName") as NSString)
} else {
NSLog("%#", error)
}
}
However, as you can see it is looking for (gameScore.objectId) - The problem is I do not know this value as the user isnt entering a complex parse generated ID. They're entering their chosen username. In the rows I have userName and Password set. How do I search the rows for the userPassword so I can verify it based on their specified userName.
Thanks in advance
Why are you querying the database for a username and password. Adding a new user is very simple with Parse. Taken directly from their docs:
Query User table on Parse
You can query the user table first, using a PFQuery:
PFQuery *query = [PFUser query];
[query whereKey:#"username" equalTo:username];
Adding New User
The idea of user accounts that let people access their information and share it with others in a secure manner is at the core of any social app. Whether your app creates its own sharing environment or integrates with existing social networks, you will need to add functionality to let people manage their accounts in your app.
We provide a specialized user class called PFUser that automatically handles much of the functionality required for user account management.
First make sure to include our SDK libraries from your .h file:
#import <Parse/Parse.h>
Then add this code into your app, for example in the viewDidLoad method (or inside another method that gets called when you run your app):
func myMethod() {
var user = PFUser()
user.username = "myUsername"
user.password = "myPassword"
user.email = "email#example.com"
// other fields can be set just like with PFObject
user["phone"] = "415-392-0202"
user.signUpInBackgroundWithBlock {
(succeeded: Bool!, error: NSError!) -> Void in
if error == nil {
// Hooray! Let them use the app now.
} else {
let errorString = error.userInfo["error"] as NSString
// Show the errorString somewhere and let the user try again.
}
}
}
This call will asynchronously create a new user in your Parse app. Before it does this, it checks to make sure that both the username and email are unique. It also securely hashes the password in the cloud.
You can learn more about Users, including how to verify emails and handle read and write permissions to data, by visiting our docs.
Run your app. A new object of the class User will be sent to the Parse Cloud and saved. When you're ready, click the button below to test if a User was created.
Further
I created a tutorial about connecting to parse if you still wish to go down the route of querying the server manually:
http://ios-blog.co.uk/tutorials/swift-create-user-sign-up-based-app-with-parse-com-using-pfuser/