微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为Swift / Firestore应用创建唯一的用户名

如何解决为Swift / Firestore应用创建唯一的用户名

我已经通过Firebase文档,Swift / Xcode文档以及此处的Stack Overflow做了大量研究。不幸的是,我无法找到解决该问题的连贯且可行的解决方案-并且似乎已经以许多不同的方式尝试过多次该问题。

我不是专业编码员。不用说,我正在弄清楚这一点,并在将其作为编码项目来帮助他们学习的同时教我的孩子。

最终目标:允许swift / xcode应用程序创建用户,但前提是该用户的userName尚未被使用。

相关堆栈溢出讨论:

我将附加当前的Swift代码,并在此问题上取得进展时对其进行更新。当前代码是用于Xcode视图控制器的注册过程。这段代码在Firestore数据库中创建了两个不同的集合:“用户”(包含用户的个人资料信息)和“用户名”(实质上是所有用户名的索引)。

此索引(集合:用户名)有一个以每个用户名命名的文档。这个想法是创建一个Firebase规则,不允许用户创建一个用户名=(集合中文名称用户名)的帐户。

更新

似乎有两种方法可以实现最终目标。一个快速代码中的有机组成部分,但这可能会带来安全问题,因为该代码可能会被邪恶的参与者绕开。另一种方法是通过firebase数据库中的规则,但是我做起来并不那么流利(我将做更多的研究并向您报告)。也许最好的做法就是同时做这两个。

这是当前代码

版本1-最初存在问题的代码上传

//  SignUpViewController.swift
//  copyright © 2020 by Mix. All rights reserved.
//  Version 1.

import UIKit
import Firebase
import FirebaseAuth
import FirebaseFirestore

class SignUpViewController: UIViewController,UITextFieldDelegate,UIPickerViewDelegate,UIPickerViewDataSource {
    
    @IBOutlet weak var signUpButton: UIButton!
    @IBOutlet weak var errorLabel: UILabel!
    @IBOutlet weak var userNameTextField: UITextField!
    @IBOutlet weak var emailTextField: UITextField!
    @IBOutlet weak var passwordTextField: UITextField!
    @IBOutlet weak var verifyPasstextField: UITextField!
    @IBOutlet weak var firstNameTextField: UITextField!
    @IBOutlet weak var lastNameTextField: UITextField!
    @IBOutlet weak var stateTextField: UITextField!
    @IBOutlet weak var birthdayTextField: UITextField!
    
    let datePicker = UIDatePicker()
    let id = Auth.auth().currentUser!.uid
    let email = Auth.auth().currentUser!.email
    
 let state_arr = ["","AL","AK","AZ","AR","CA","CO","CT","DE","FL","GA","HI","ID","IL","IN","IA","KS","KY","LA","ME","MD","MA","MI","MN","MS","MO","MT","NE","NV","NH","NJ","NM","NY","NC","ND","OH","OK","OR","PA","RI","SC","SD","TN","TX","UT","VT","VA","WA","WV","WI","WY"]
    
    //picker view
    let statePickerView = UIPickerView()
    //Hold Current arr
    var currentArr : [String] = []
    //that hold current text field
    var activeTextField : UITextField?
    
    func createDatePicker() {
        let toolbar = UIToolbar()
        toolbar.sizetoFit()
        
       let done2Button = UIBarButtonItem(title: "Select",style: .plain,target: self,action: #selector(doneTapped2))
           let space2Button = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,target: nil,action: nil)
           
           toolbar.setItems([space2Button,done2Button],animated: false)

        birthdayTextField.inputAccessoryView = toolbar
        birthdayTextField.inputView = datePicker
        
        datePicker.datePickerMode = .date
        
    }
    
    
    
    
   
    
    
    
override func touchesEnded(_ touches: Set<UITouch>,with event: UIEvent?) {
        self.view.endEditing(true)
    }
    
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
    activeTextField = textField
    switch textField {
    case stateTextField:
    currentArr = state_arr
    default:
        
        print("Default")
    }
        return true
    }
    

    
  
    
    
    
    
func numberOfComponents(in pickerView: UIPickerView) -> Int {
            return 1
        }
        
func pickerView(_ pickerView: UIPickerView,numberOfRowsInComponent component: Int) -> Int {
        return currentArr.count
        }
func pickerView(_ pickerView: UIPickerView,titleForRow row: Int,forComponent component: Int) -> String? {
            return currentArr[row]
        }
func pickerView(_ pickerView: UIPickerView,didSelectRow row: Int,inComponent component: Int) {
            print("Selected item is",currentArr[row])
            activeTextField!.text = currentArr[row]
        }
func createtoolbar() {
       let toolbar = UIToolbar()
    toolbar.barStyle = .default
    toolbar.sizetoFit()
    
    let doneButton = UIBarButtonItem(title: "Select",action: #selector(doneTapped))
    let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,action: nil)
    let cancelButton = UIBarButtonItem(title: "Cancel",action: #selector(cancelTapped))
    toolbar.setItems([cancelButton,spaceButton,doneButton],animated: false)
    
    stateTextField.inputAccessoryView = toolbar
    }
    
@objc func doneTapped() {
        activeTextField?.resignFirstResponder()
        }
   
@objc func cancelTapped() {
        
        activeTextField?.resignFirstResponder()
        }
   
override func viewDidLoad() {
        super.viewDidLoad()
        self.errorLabel.alpha = 0
        self.navigationController?.setNavigationBarHidden(false,animated: false)
        setUpElements()
        createtoolbar()
        createDatePicker()
    stateTextField.delegate = self
    statePickerView.delegate = self
    statePickerView.dataSource = self
    stateTextField.inputView = statePickerView
                     }
                     
func setUpElements() {
        Utilities.styleFilledButton(signUpButton)
                     }
func validateFields() -> String? {
        if firstNameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
            lastNameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
            emailTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
            passwordTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
            verifyPasstextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
            userNameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
            stateTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
            birthdayTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == ""
    {
    return "Please fill in all fields."
     }
                let cleanedPassword = passwordTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
                
                if Utilities.isPasswordValid(cleanedPassword) == false {
                    // Password isn't secure enough
                    return "Please make sure your password is at least 8 characters,contains a special character and a number."
    }
    
                    if passwordTextField.text != verifyPasstextField.text {
                      // Password isn't secure enough
                      return "Passwords do not match."
                  }
    
    
    return nil
    
    }

    
    
    
    
@IBAction func termsButtonTapped(_ sender: Any) {
        self.view.endEditing(true)
    }
    
@IBAction func policyButtonTapped(_ sender: Any) {
        self.view.endEditing(true)
    }
    
@IBAction func signUpTapped(_ sender: Any) {
    self.view.endEditing(true)
    verifyState()
    self.errorLabel.alpha = 0
 
   
    let error = validateFields()
            if error != nil {
                   showError(error!)
               }
            else {
                   
                   // Create cleaned versions of the data
                    let firstName = firstNameTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
                    let lastName = lastNameTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
                    let email = emailTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
                    let password = passwordTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
                    let username = userNameTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
                    let state = stateTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
                    let birthday = birthdayTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
                   
                   // Create the user
                Auth.auth().createuser(withEmail: email,password: password) { (result,err) in
                    
                       // Check for errors
                       if err != nil {
                           
                           // There was an error creating the user
                           self.showError("Error creating user")
                       }
                       else {
                           
                           // User was created successfully,Now store the first name and last name
                           let db = Firestore.firestore()
                           
                        db.collection("users").document(Auth.auth().currentUser!.uid).setData( ["firstname":firstName,"lastname":lastName,"username":username,"email": email,"birthday":birthday,"state":state,"uid": Auth.auth().currentUser!.uid]) { (error) in
                               
                               if error != nil {
                                   // Show error message
                                   self.showError("Error saving user data")
                               }
                           }
                        
                        
                        db.collection("usernames").document(username).setData( ["username":username,"uid"
                            : Auth.auth().currentUser!.uid]) { (error) in
                                                     
                                                     if error != nil {
                                                         // Show error message
                                                         self.showError("Error saving user data")
                                                     }
                            
                    
                            
                                                 }
                        
                           
                           // Transition to the home screen
                           self.transitionToHome()
                       }
                       
                   }
      
        self.view.endEditing(true)
    }
    }
    
@IBAction func termsSwitch(_ sender: UISwitch) {
           
        self.view.endEditing(true)
              if (sender.isOn == true)
              {signUpButton.isEnabled = true}
        else if (sender.isOn == false)
              {signUpButton.isEnabled = false}
              }
    
    
func showError(_ message:String) {
              
              errorLabel.text = message
              errorLabel.alpha = 1
          }
          
func transitionToHome() {
              let HoMetabViewController = storyboard?.instantiateViewController(identifier: Constants.Storyboard.hoMetabViewController) as? HoMetabViewController
              
              view.window?.rootViewController = HoMetabViewController
              view.window?.makeKeyAndVisible()
              
          }
func verifyState() {
if stateTextField.text == "" {
    self.showError("Select a State.")
    return
    
}
}
   
    
func verifyPassword() {
    if passwordTextField.text == verifyPasstextField.text {
        
    }
    else {
        self.showError("Passwords do not match.")
        return
    }
    }
    
    @objc func doneTapped2() {
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        formatter.timeStyle = .none
        birthdayTextField.text = formatter.string(from: datePicker.date)
        self.view.endEditing(true)
        
    }
}

解决方法

没有开箱即用的解决方案。您已经在使用Auth.auth().createUser,它将确保电子邮件是唯一的,但用户名别无其他选择。

我认为数据库中的规则将不起作用,因为身份验证和数据库是两个单独的产品,它们不会互相读取数据。数据库规则可以防止访问数据库中的数据,但不能访问身份验证服务,在身份验证服务中,您需要检查用户名。

如果您只是在学习对客户端进行检查的话,我认为这是最简单的选择。虽然就安全性而言,它可能不是100%最佳选择,但它可能是最佳选择。可以通过在每次用户更改用户名时从服务器获取用户ID并仅在用户名不存在时让用户继续使用Auth.auth().createUser代码来实现。

可以解决您的安全问题的一个更复杂的选择是使用Firebase Cloud Functions。这些是小的代码片段(当前没有swift选项),位于Firebase后端,可在事件或调用发生时运行。 More info on cloud functions.

如果您要走那条路线,则有很多在线指南。每当用户尝试创建新帐户时,我都会通过创建一个function that can be called from the app directly来解决这个问题。我也将传递用户尝试注册的信息,然后在后端功能上检查现有用户名和实际帐户创建(Auth.auth().createUser),并仅传递回您的应用要求(成功,失败,用户名ID是什么...)。这样可以解决安全性问题,但是要使其工作相当多的努力。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。