2016-05-10 32 views
0

我正在構建一個使用Firebase的電子郵件和密碼登錄功能的應用程序。我讓用戶使用用戶名,電子郵件和密碼進行註冊。如果用戶名不是唯一的,我正在努力阻止用戶被創建。我一直在閱讀其他問題(特別是Firebase-android-make-username-uniquehow-prevent-username-from-duplicate-signup-infirebase),但我還沒有得到它充分的工作。Firebase iOS Swift:防止重複的用戶名

我跟着上面的第一個鏈接的指示,成立了我的數據結構爲:

app : { 
    users: { 
     "some-user-uid": { 
      email: "[email protected]" 
      username: "myname" 
     } 
    }, 
    usernames: { 
     "myname": "some-user-uid" 
    } 
} 

和我的安全規則:

"users": { 
    "$uid": { 
    ".write": "auth !== null && auth.uid === $uid", 
    ".read": "auth !== null && auth.provider === 'password'", 
    "username": { 
     ".validate": " 
     !root.child('usernames').child(newData.val()).exists() || 
     root.child('usernames').child(newData.val()).val() == $uid" 
    } 
    } 
} 

採用這種設置,如果我嘗試創建一個用戶名已經存在的新用戶,它會阻止用戶被添加到我的數據結構中。當下面的代碼被調用時,如果用戶名是重複的,它會輸出「用戶數據無法保存」。

func createNewAccount(uid: String, user: Dictionary<String, String>) { 

    USER_REF.childByAppendingPath(uid).setValue(user, withCompletionBlock: { 
     (error:NSError?, ref:Firebase!) in 
     if (error != nil) { 
     print("User Data could not be saved.") 
     } else { 
     print("User Data saved successfully!") 
     } 
    }) 
    } 

    func addUsernameToUsernamePath (userData: Dictionary<String, String>) { 

    USERNAME_REF.updateChildValues(userData) 
    } 

這裏是我卡住的地方。我的create account方法在調用createUser和authUser(我需要獲得uid)之前不會調用上述兩種方法。我的問題是,用戶仍然被創建爲註冊用戶,我的安全規則只是保持用戶信息不被添加到我的數據結構中。如果有重複的用戶名,我需要弄清楚如何阻止用戶被創建。

@IBAction func createAccount() { 
    let username = usernameField.text 
    let email = emailField.text 
    let password = passwordField.text 

    if username != "" && email != "" && password != "" { 

     // Set Email and Password for the New User. 

     DataService.dataService.BASE_REF.createUser(email, password: password, withValueCompletionBlock: { error, result in 

     if error != nil { 
      print("Error: \(error)") 
      if let errorCode = FAuthenticationError(rawValue: error.code) { 
      switch (errorCode) { 
      case .EmailTaken: 
       self.signupErrorAlert("Email In Use", message: "An account has already been created for this email address.") 
      default: 
       self.signupErrorAlert("Oops!", message: "Having some trouble creating your account. Please try again or check your internet connection.") 
      } 
      } 

     } else { 

      DataService.dataService.BASE_REF.authUser(email, password: password, withCompletionBlock: { 
      err, authData in 


      let user = ["provider": authData.provider!, "email": email!, "username": username!] 
      let userData = [username!: authData.uid!] 

      DataService.dataService.createNewAccount(authData.uid, user: user) 
      DataService.dataService.addUsernameToUsernamePath(userData)   

      }) 

編輯

這裏是解決我的問題我更新的createAccount方法。

@IBAction func createAccount() { 
    let username = usernameField.text 
    let email = emailField.text 
    let password = passwordField.text 


if username != "" && email != "" && password != "" { 

    DataService.dataService.USERNAME_REF.observeEventType(.Value, withBlock: { snapshot in 
    var usernamesMatched = false 
    if snapshot.value is NSNull { 
     usernamesMatched = false 
    } else { 
     let usernameDictionary = snapshot.value 
     let usernameArray = Array(usernameDictionary.allKeys as! [String]) 
     for storedUserName in usernameArray { 
     if storedUserName == self.usernameField.text! { 
      usernamesMatched = true 
      self.signupErrorAlert("Username Already Taken", message: "Please try a different username") 
     } 
     } 
    } 

    if !usernamesMatched { 
     // Set Email and Password for the New User. 

     DataService.dataService.BASE_REF.createUser(email, password: password, withValueCompletionBlock: { error, result in 

     if error != nil { 
      print("Error: \(error)") 
      if let errorCode = FAuthenticationError(rawValue: error.code) { 
      switch (errorCode) { 
      case .EmailTaken: 
       self.signupErrorAlert("Email In Use", message: "An account has already been created for this email address.") 
      default: 
       self.signupErrorAlert("Oops!", message: "Having some trouble creating your account. Please try again or check your internet connection.") 
      } 
      } 

     } else { 

      // Create and Login the New User with authUser 
      DataService.dataService.BASE_REF.authUser(email, password: password, withCompletionBlock: { 
      err, authData in 


      let user = ["provider": authData.provider!, "email": email!, "username": username!] 
      let userData = [username!: authData.uid!] 

      // Seal the deal in DataService.swift. 
      DataService.dataService.createNewAccount(authData.uid, user: user) 
      DataService.dataService.addUsernameToUsernamePath(userData) 


      }) 
+0

在調用createUser()之前驗證用戶名不存在? – Kato

+0

感謝您的建議。我只是更新了我的代碼,它似乎解決了這個問題。 – chickenparm

+0

爲什麼你不是首先通過createUser-> FAuthenticationErrorEmailTaken檢查重複的用戶用戶名,如果它可用,然後在/ users節點中創建用戶?如果您這樣做,您可以將該代碼縮減爲幾行,因爲該用戶名已保證不會存在於/ users節點中。 – Jay

回答

1

您可以在沒有有效用戶名的情況下注冊,並且在部分註冊時顯示一個單獨的「設置用戶名」屏幕。

定義您的安全規則以在允許寫入數據庫的其他部分之前檢查非空用戶名。

+0

感謝您的建議。我只是通過在調用createUser()之前檢查用戶名是否不存在來找到一種解決方法。不過,我很欣賞這個建議,如果我對我的解決方案不滿意,可以實施它。 – chickenparm

+0

你可能會想要這樣做,否則如果兩個人同時註冊同一個用戶名,你將會遇到競爭狀態。 –

+0

好點。我只是嘗試在兩個iPhone上創建兩個具有相同用戶名的帳戶。當我在同一時間觸摸創建帳戶時遇到問題。 – chickenparm

0

我可以通過將createAccount()更新爲下面的代碼來獲得它。

@IBAction func createAccount() { 
    let username = usernameField.text 
    let email = emailField.text 
    let password = passwordField.text 


    if username != "" && email != "" && password != "" { 

     // Checks for internet connection before saving the meetup. Returns if there is no internet connection. 
     let reachability = try! Reachability.reachabilityForInternetConnection() 

     if reachability.currentReachabilityStatus == .NotReachable { 
     let internetAlert = UIAlertController(title: "No Internet Connection", message: "Please make sure your device is connected to the internet.", preferredStyle: .Alert) 
     let internetAlertAction = UIAlertAction(title: "OK", style: .Default, handler: nil) 
     internetAlert.addAction(internetAlertAction) 
     presentViewController(internetAlert, animated: true, completion: nil) 
     return 
     } 
     DataService.dataService.USERNAME_REF.observeEventType(.Value, withBlock: { snapshot in 
     var usernamesMatched = false 
     if snapshot.value is NSNull { 
      usernamesMatched = false 
     } else { 
      let usernameDictionary = snapshot.value 
      let usernameArray = Array(usernameDictionary.allKeys as! [String]) 
      for storedUserName in usernameArray { 
      if storedUserName == self.usernameField.text! { 
       usernamesMatched = true 
       self.signupErrorAlert("Username Already Taken", message: "Please try a different username") 
      } 
      } 
     } 

     if !usernamesMatched { 
      // Set Email and Password for the New User. 

      DataService.dataService.BASE_REF.createUser(email, password: password, withValueCompletionBlock: { error, result in 

      if error != nil { 
       print("Error: \(error)") 
       if let errorCode = FAuthenticationError(rawValue: error.code) { 
       switch (errorCode) { 
       case .EmailTaken: 
        self.signupErrorAlert("Email In Use", message: "An account has already been created for this email address.") 
       default: 
        self.signupErrorAlert("Oops!", message: "Having some trouble creating your account. Please try again or check your internet connection.") 
       } 
       } 

      } else { 

       // Create and Login the New User with authUser 
       DataService.dataService.BASE_REF.authUser(email, password: password, withCompletionBlock: { 
       err, authData in 


       let user = ["provider": authData.provider!, "email": email!, "username": username!] 
       let userData = [username!: authData.uid!] 

       // Seal the deal in DataService.swift. 
       DataService.dataService.createNewAccount(authData.uid, user: user) 
       DataService.dataService.addUsernameToUsernamePath(userData) 


       })