How to Send Different Users to Separate View Controllers Using Firebase and Xcode

How can i send different users to separate view controllers using firebase and Xcode

If you want to store a type for a user you have to use the database. Like this
Sample Image

When the user logs in, get the value from the database for the path "users/<userId>/type". Then use a switch statement to redirect to the correct view controller.

Here's the full code

 // Sign in to Firebase
FIRAuth.auth()?.signIn(withEmail: "ntoonio@gmail.com", password: "Password123", completion: {
(user, error) in
// If there's no errors
if error == nil {
// Get the type from the database. It's path is users/<userId>/type.
// Notice "observeSingleEvent", so we don't register for getting an update every time it changes.
FIRDatabase.database().reference().child("users/\(user!.uid)/type").observeSingleEvent(of: .value, with: {
(snapshot) in

switch snapshot.value as! String {
// If our user is admin...
case "admin":
// ...redirect to the admin page
let vc = self.storyboard?.instantiateViewController(withIdentifier: "adminVC")
self.present(vc!, animated: true, completion: nil)
// If out user is a regular user...
case "user":
// ...redirect to the user page
let vc = self.storyboard?.instantiateViewController(withIdentifier: "userVC")
self.present(vc!, animated: true, completion: nil)
// If the type wasn't found...
default:
// ...print an error
print("Error: Couldn't find type for user \(user!.uid)")
}
})
}
})

Instead of the whole switch statement you can do

let vc = self.storyboard?.instantiateViewController(withIdentifier: "\(snapshot.value)_View")
self.present(vc!, animated: true, completion: nil)

Warning! This will crash if the type isn't found. But that's fixable :)

I want to pass an instance of type user object between view controllers in swift

As mentioned in the comments, with asynchronous functions, you can't expect a return value right away. One common way to handle this is by using a callback function or completion handler.

I'm including a very basic example of this. Note that I'm not doing any error handling right now -- you'd want to build it out to be more robust, but this at least gets the concept:

extension HomeViewController {
public func assignValueToUserObject(completion: @escaping () -> Void) { //completion handler gets passed as an parameter
guard let uid = Auth.auth().currentUser?.uid else {
print("Could not get user id")
return
}

Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: { [self] snapshot in
if let dictionary = snapshot.value as? [String: AnyObject] {

user.first_name = dictionary["first_name"] as? String
user.last_name = dictionary["last_name"] as? String
user.email = dictionary["email"] as? String
user.profile_picture = dictionary["profile_picture"] as? String
completion() //call once the action is done
}
}, withCancel: nil)
} // End AssignValueToUserObject Method
} // End extension
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
title = "Home"
checkIfUserIsLoggedIn()
//don't call copy here anymore
}
func checkIfUserIsLoggedIn() {
// Check if user is logged in
if Auth.auth().currentUser == nil {
// User is not logged in, send user to login screen
let loginVC = storyboard?.instantiateViewController(identifier: "login")
loginVC?.modalPresentationStyle = .fullScreen
present(loginVC!, animated: false)
}

// User is logged in, fetch their info
assignValueToUserObject(completion: {
self.copyData() //only copy once the completion handler is run
})

} // End checkIfUserIsLoggedIn method

Update, showing a way to use a singleton to monitor a user value in different view controllers:

import Combine

struct User {
var id : UUID //whatever properties your user model has
}

class UserManager {
@Published var user : User?
static public var shared = UserManager()
private init() {

}

func login() {
//do your firebase call here and set `user` when done in the completion handler
self.user = User(id: UUID())
}
}

class HomeViewController : UIViewController {
private var userManager = UserManager.shared
private var cancellable : AnyCancellable?

init() {
super.init(nibName: nil, bundle: nil)
setupUserLink() //make sure this gets called in whatever initializer is used
}

required init?(coder: NSCoder) {
super.init(coder: coder)
setupUserLink() //make sure this gets called in whatever initializer is used
}

func setupUserLink() {
cancellable = userManager.$user.compactMap { $0 }.sink { user in
print(user.id.uuidString) //do something with the user value -- assign it to a variable, a control, etc
}
}
}

class ProfileViewController : UIViewController {
//do the same pattern as in HomeViewController, setting up the user link to be monitored
}

Error when trying to set up Firebase Authentication for multiple view controllers

I don't know if this will solve your problem, but
let createEmail = AppDelegate.shared.userInformation["createEmail."] as you see it here your key is different from the first one, where you save info as ["createEmail"] without the dot in the end.

Is it possible to transfer data retrieved from Firebase in the tab bar controller class from the tab bar controller to a view controller? Xcode, Swift

What is the best way to do this?

  • I believe once you perform login call (Inside login screen) you will have userID and other data related to that user, so inside Login controller you have added code for making Tabbar controller as initial controller.

  • So based on that best thing is to store userData into tabbar controller.

  • So once you sore data into tabbar controller (from either login or Home Screen) you can fetch user data in any root controller (home, search, profile) which is inside tab-bar by below code.

struct User { var userID: String? }

class TabbarController: UITabBarController {
var userData: User
}

class HomeController: UIViewController {
var userData: User?
override func viewWillAppear(_ animated: Bool) {
if let tabBar = self.tabBarController as? TabbarController {
self.userData = tabBar.userData
}
}
}

Sample Image

Firestore data loads every time when moving to different view controllers

This type of thing happens when you are using getData function of Firestone.
Using a snapshot listener will remove this issue.

How to manage users' different authentication in firebase

How to sign in a user from different device after he signed in using he's email and password on the first device without anonymous users.

After creating the user you need to save the email and password to NSUbiquitousKeyValueStore this way the other device will have access to email and password because you will need to sign in the user first before linking their account to Facebook as you will see below.

In FB console
1. Account Email address settings:Prevent creation of multiple accounts with the same email address This way the account doesn't get created again if the user already signed in with email address.

2.If the user choose email address as first login:

 var iCloudKeyStore: NSUbiquitousKeyValueStore = NSUbiquitousKeyValueStore()

FIRAuth.auth()?.createUser(withEmail: email, password: password) { (user, error) in

// User created
if error == nil{

// signed in successfully

//Save the password and the email. Or choose your way to save them
iCloudKeyStore.set(password, forKey: email)

}else{

//Erorr
}
}

  1. Now the user has an account in Firebase and will look like that:

Sample Image
4. Now on the other device the user decided to login with Facebook (different provider)

After you have signed in the user with Facebook and have fb's token: Usually inside the function's listener of FBSDKAccessTokenDidChange Notification, you sign in the user with Firebase with Facebook's token.

let credential = FIRFacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString)

FIRAuth.auth()?.signIn(with: credential) { (user, error) in
// ...
if error != nil {
// SETP 5. HERE

let nsError = (error as! NSError)
// Error: 17007 The email address is already in use by another account. ERROR_EMAIL_ALREADY_IN_USE

if nsError.code == 17007{

print(error,user?.uid,nsError.code,"facebook user error")

let email = nsError.userInfo["FIRAuthErrorUserInfoEmailKey"] as? String
self.signInUserWithEmail(email: email!)
print("email",email)
}

}else{

print(user!.uid,"facebook user login")
}

  1. Since the user already have an account with this email address which he's now trying to sign in with Facebook, so an error will occurs: as you see in step 4, now you need to sign in the user before you link is account to Facebook:

    func signInUserWithEmail(email:String){

    let password = iCloudKeyStore.string(forKey: email)

    FIRAuth.auth()?.signIn(withEmail: email, password: password, completion: { (user:FIRUser?, error:Error?) in

    if user != nil{

    self.linkAccountWihtFacebook()
    print(user?.uid)

    }else{

    print(error?.localizedDescription)
    }
    })

    }

    func linkAccountWihtFacebook(){

        let credential = FIRFacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString)

    FIRAuth.auth()?.currentUser?.link(with: credential, completion: { (user:FIRUser?, error:Error?) in

    if let LinkedUser = user{

    print("NEW USER:",LinkedUser.uid)

    }

    if let error = error as? NSError{

    //Indicates an attempt to link a provider of a type already linked to this account.
    if error.code == FIRAuthErrorCode.errorCodeProviderAlreadyLinked.rawValue{
    print("FIRAuthErrorCode.errorCodeProviderAlreadyLinked")
    }

    //This credential is already associated with a different user account.
    if error.code == 17025{

    }

    print("MyError",error)
    }

    })
    }
  2. This way you will have the following result in Firebase console:

Sample Image

Firebase documentation:
https://firebase.google.com/docs/auth/ios/account-linking

Link auth provider credentials to a user account

To link auth provider credentials to an existing user account:

1. Sign in the user using any authentication provider or method.


  1. Complete the sign-in flow for the new authentication provider up to, but not including, calling one of the FIRAuth.signInWith methods.
    For example, get the user's Google ID token, Facebook access token, or
    email and password.
  2. Get a FIRAuthCredential for the new authentication provider


Related Topics



Leave a reply



Submit