Я пытаюсь написать специальную функцию, которая регистрирует нового пользователя в Firebase. Я импортировал Firebase в класс с именем DatabaseManager. Там я управляю всем взаимодействием с базой данных. В этом классе я хотел бы иметь настраиваемую функцию для добавления пользователей, которая выдает все ошибки Firebase. Это значит, что я могу использовать функцию в классе ViewController, где я могу обнаруживать ошибки и показывать предупреждения. Однако я не могу заставить эту функцию работать должным образом, и я не уверен, что делаю неправильно.

Вот моя функция:

enum createAccountError : Error{
        case emailInUse, weakPassword, networkError, unknownError
    }

    //Mark: create a user profile
    ///create account with email, password, username, phoneNumber, birthDate, name
    func createAccount(_ userModel: UserModel, _ password: String?, completion: @escaping (_ inner: ()throws -> Bool)->())  {
        Auth.auth().createUser(withEmail: userModel.email!, password: password!, completion: {(user, error) in
            if let error = error {
                if let errCode = AuthErrorCode(rawValue: error._code) {
                    switch errCode {
                    case .emailAlreadyInUse:
                        completion({throw createAccountError.emailInUse})
                    case .weakPassword:
                        completion({throw createAccountError.weakPassword})
                    case .networkError:
                        completion({throw createAccountError.networkError})
                    default:
                        completion({throw createAccountError.unknownError})
                    }
                }
                return
            } else {
                completion({return true})
            }
        })
    }

И вот как я пробовал его использовать:

DatabaseManager.system.createAccount(user, password) { (( inner: ()throws -> Bool)->()) in
            do{
                let result = try inner()
            } catch .emailInUse{
                //show alert
            }
            }
1
dre_84w934 31 Дек 2017 в 01:11

2 ответа

Лучший ответ

Я создал тестовую функцию для демонстрации, и все работает правильно

// error type
enum TestError: Error { case notFound, empty }
// define typealias for eiser usage of complex function type
typealias ThrowableCallback = () throws -> Bool

func createAccount(_ shouldThrow: Bool, completion: @escaping (_ inner: ThrowableCallback) -> Void) {
  // call completion callback asynchronously
  DispatchQueue.main.async {
    if shouldThrow {
      // throw error
      completion({ throw TestError.notFound })
    }
    // return value
    completion({ return true })
  }
}

Применение:

createAccount(true) { (inner: ThrowableCallback) -> Void in
        do {
          let success = try inner()
          print(success)
        } catch {
          print(error)
        }
      }

UPD: я не рекомендую использовать эту технику для обработки ошибок в асинхронных функциях. Используйте отдельные обратные вызовы для успеха и неудачи или обещания для корректной обработки асинхронного кода (подробнее см. this)

UPD 2: Актуальное решение

typealias ThrowableCallback = () throws -> User 
func createAccount(_ userModel: UserModel,
                   _ password: String,
                   completion: @escaping (_ inner: ThrowableCallback) -> Void) {
  Auth.auth().createUser(withEmail: userModel.email!, password: password!, completion: {(user, error) in 
    if let error = error { completion({ throw error }) } 
    else { completions({ return user! }) } 
  }) 
}

// usage
createAccount(userModel, somePassword) { (inner: ThrowableCallback) -> Void in
        do {
          let createdUser = try inner()
        } catch {
          ler errCode = (error as NSError)._code
          switch errCode {
             case .emailAlreadyInUse:
               showAlert("Email is already in use")
             case .weakPassword:
               showAlert("please enter stronger password ")
             case .networkError:
               showAlert("it seams that there is no internet connection")
             default:
               showAlert("Error creating user. Please try again later")
             }
        }
      }
1
Andy C 1 Янв 2018 в 14:08

Я бы предложил провести рефакторинг вашей функции следующим образом:

func createAccount(_ userModel: UserModel, _ password: String?, completion: @escaping(Error?) -> ())  {
    Auth.auth().createUser(withEmail: userModel.email!, password: password!, completion: {(user, error) in
        if let error = error {
            if let errCode = AuthErrorCode(rawValue: error._code) {
                switch errCode {
                case .emailAlreadyInUse:
                    completion(createAccountError.emailInUse)
                case .weakPassword:
                    completion(createAccountError.weakPassword)
                case .networkError:
                    completion(createAccountError.networkError)
                default:
                    completion(createAccountError.unknownError)
                }
            }

        } else {
            completion(nil)
        }
    })
}

Затем, когда вы вызываете функцию, вы можете проверить, не было ли такой ошибки:

DatabaseManager.system.createAccount(user, password) { (error) in
   guard error == nil else {
      //Handle error
      return
   }

   //There was no error
1
Zach Fuller 30 Дек 2017 в 22:49